Связи MongoDB: вставлять или ссылаться? - PullRequest
470 голосов
/ 21 марта 2011

Я новичок в MongoDB - из реляционной базы данных. Я хочу разработать структуру вопроса с некоторыми комментариями, но я не знаю, какое отношение использовать для комментариев: embed или reference?

Вопрос с некоторыми комментариями, например stackoverflow , будет иметь такую ​​структуру:

Question
    title = 'aaa'
    content = bbb'
    comments = ???

Сначала я хочу использовать встроенные комментарии (я думаю, embed рекомендуется в MongoDB), например:

Question
    title = 'aaa'
    content = 'bbb'
    comments = [ { content = 'xxx', createdAt = 'yyy'}, 
                 { content = 'xxx', createdAt = 'yyy'}, 
                 { content = 'xxx', createdAt = 'yyy'} ]

Понятно, но меня беспокоит этот случай: Если я хочу отредактировать указанный комментарий, как мне получить его содержание и вопрос? Нет _id, чтобы позволить мне найти один, ни question_ref, чтобы позволить мне найти свой вопрос. (Я настолько новичок, что не знаю, есть ли способ сделать это без _id и question_ref.)

Должен ли я использовать ref, а не embed? Тогда я должен создать новую коллекцию для комментариев?

Ответы [ 10 ]

715 голосов
/ 21 марта 2011

Это больше искусство, чем наука. Документация Mongo по схемам является хорошим справочным материалом, но вот несколько вещей, на которые следует обратить внимание:

  • Положите как можно больше

    Радостьбазы данных документов заключается в том, что она устраняет множество соединений.Ваш первый инстинкт должен состоять в том, чтобы разместить как можно больше в одном документе.Поскольку документы MongoDB имеют структуру и поскольку вы можете эффективно выполнять запросы внутри этой структуры (это означает, что вы можете взять ту часть документа, которая вам нужна, поэтому размер документа не должен вас сильно беспокоить), нет необходимости в немедленной нормализации данных, таких каквы бы в SQL.В частности, любые данные, которые бесполезны, кроме его родительского документа, должны быть частью одного и того же документа.

  • Разделение данных, на которые можно ссылаться из нескольких мест, в его собственную коллекцию.

    Это не столько проблема «пространства хранения», сколько проблема «согласованности данных».Если многие записи ссылаются на одни и те же данные, более эффективно и менее подвержено ошибкам обновлять одну запись и хранить ссылки на нее в других местах.

  • Соображения относительно размера документа

    MongoDB устанавливает ограничение размера 4 МБ (16 МБ с 1,8) для одного документа.В мире ГБ данных это звучит мало, но это также 30 тысяч твитов или 250 типичных ответов переполнения стека или 20 мерцающих фотографий.С другой стороны, это гораздо больше информации, чем можно было бы представить за один раз на типичной веб-странице.Сначала подумайте, что облегчит ваши запросы.Во многих случаях беспокойство о размерах документов будет преждевременной оптимизацией.

  • Сложные структуры данных:

    MongoDB может хранить произвольные глубоко вложенные структуры данных, но не может эффективно их искать.Если ваши данные образуют дерево, лес или график, вам необходимо хранить каждый узел и его ребра в отдельном документе.(Обратите внимание, что существуют хранилища данных, специально предназначенные для этого типа данных, которые также следует учитывать)

    Также было указано , что невозможно вернуть подмножество элементов вдокумент.Если вам нужно выбрать несколько битов каждого документа, их будет легче выделить.

  • Согласованность данных

    MongoDB делает компромиссмежду эффективностью и согласованностью.Правило состоит в том, что изменения в одном документе всегда атомарные, в то время как обновления нескольких документов никогда не следует считать атомарными.Также нет способа «заблокировать» запись на сервере (вы можете встроить это в логику клиента, используя, например, поле «блокировка»).При разработке схемы подумайте, как вы будете поддерживать согласованность данных.Как правило, чем больше вы храните в документе, тем лучше.

Для того, что вы описываете, я бы вставил комментарии и дал каждому комментарию поле id с ObjectID.В ObjectID встроена метка времени, поэтому вы можете использовать ее вместо созданной в, если хотите.

32 голосов
/ 21 марта 2011

Если я хочу отредактировать указанный комментарий, как получить его содержание и вопрос?

Вы можете запросить по поддокументу: db.question.find({'comments.content' : 'xxx'}).

Это вернет весь документ с вопросом. Чтобы отредактировать указанный комментарий, вам нужно найти комментарий на клиенте, выполнить редактирование и сохранить его обратно в БД.

В общем, если ваш документ содержит массив объектов, вы обнаружите, что эти подобъекты необходимо будет изменить на стороне клиента.

30 голосов
/ 13 января 2015

В общем, встраивание хорошо, если у вас есть отношения один-к-одному или один-ко-многим между сущностями, и ссылка хороша, если у вас есть отношения многие-ко-многим.

20 голосов
/ 28 июля 2014

Ну, я немного опоздал, но все же хотел бы поделиться своим способом создания схемы.

У меня есть схемы для всего, что можно описать словом, как вы делали бы это в классическойООП.

EG

  • Комментарий
  • Аккаунт
  • Пользователь
  • Блог-пост
  • ...

Каждая схема может быть сохранена как документ или вложенный документ, поэтому я объявляю это для каждой схемы.

Документ:

  • Может использоваться в качестве ссылки,(Например, пользователь оставил комментарий -> комментарий имеет ссылку «сделано пользователем»)
  • Является ли «Root» в вашем приложении.(Например, пост блога -> есть страница о посте блога)

Субдокумент:

  • Может использоваться только один раз / никогда не является ссылкой.(Например, комментарий сохраняется в посте)
  • Никогда не является «рутом» в вашем приложении.(Комментарий только появляется на странице поста, но страница по-прежнему о посте)
17 голосов
/ 02 июня 2016

Я наткнулся на эту небольшую презентацию, исследуя этот вопрос самостоятельно. Я был удивлен тем, насколько хорошо это было продумано, как информация, так и презентация.

http://openmymind.net/Multiple-Collections-Versus-Embedded-Documents

Итого:

Как правило, если у вас много [дочерних документов] или они большие, лучше всего подойдет отдельная коллекция.

Меньшие и / или меньшее количество документов обычно подходят для встраивания.

17 голосов
/ 26 сентября 2013

Я знаю, что это довольно старо, но если вы ищете ответ на вопрос ОП о том, как вернуть только указанный комментарий, вы можете использовать оператор $ (query) , например:

db.question.update({'comments.content': 'xxx'}, {'comments.$': true})
10 голосов
/ 18 сентября 2014

Да, мы можем использовать ссылку в документе. Чтобы заполнить другой документ точно так же, как sql i joins. В mongo db у них нет соединений для сопоставления одного документа со многими. Вместо этого мы можем использовать populate чтобы выполнить наш сценарий ..

var mongoose = require('mongoose')
  , Schema = mongoose.Schema

var personSchema = Schema({
  _id     : Number,
  name    : String,
  age     : Number,
  stories : [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});

var storySchema = Schema({
  _creator : { type: Number, ref: 'Person' },
  title    : String,
  fans     : [{ type: Number, ref: 'Person' }]
});

Заполнение - это процесс автоматической замены указанных путей в документе документами из других коллекций. Мы можем заполнить один документ, несколько документов, простой объект, несколько простых объектов или все объекты, возвращаемые из запроса. Давайте посмотрим на некоторые примеры.

Лучше вы можете получить больше информации, пожалуйста, посетите: http://mongoosejs.com/docs/populate.html

1 голос
/ 23 апреля 2019

Я создал этот тест для справки, чтобы узнать, следует ли вам использовать тот или иной

http://indie -rok.github.io / embedded-vs-reference-mongo-db

1 голос
/ 05 ноября 2018

На самом деле, мне довольно любопытно, почему никто не говорил о спецификациях UML.Практическое правило заключается в том, что если у вас есть агрегация, то вы должны использовать ссылки.Но если это композиция, то связь сильнее, и вам следует использовать встроенные документы.

И вы быстро поймете, почему это логично.Если объект может существовать независимо от родителя, вы захотите получить к нему доступ, даже если родитель не существует.Поскольку вы просто не можете встроить его в несуществующий родительский объект, вы должны включить его в свою собственную структуру данных.И если родитель существует, просто свяжите их вместе, добавив ссылку на объект в родительском.

Не знаете, в чем разница между двумя отношениями?Вот ссылка, объясняющая их: Агрегация против композиции в UML

1 голос
/ 22 октября 2015

Если я хочу отредактировать указанный комментарий, как мне получить его содержание и его вопрос?

Если вы отслеживали количество комментариев и индекс комментария, который хотите изменить, вы можете использовать оператор точки ( SO, пример ).

Вы можете сделать, например,

db.questions.update(
    {
        "title": "aaa"       
    }, 
    { 
        "comments.0.contents": "new text"
    }
)

(как еще один способ редактирования комментариев внутри вопроса)

...