Подтвердить что ты не робот

Отношения в документарно-ориентированной базе данных?

Я интересуюсь документами-ориентированными базами данных, и я хотел бы играть с MongoDB. Таким образом, я начал довольно простой проект (трекер ошибок), но у меня есть трудные времена, думающие нерелятивно.

Мои проблемы:

  • У меня есть два объекта, которые относятся друг к другу (например, issue = {code:"asdf-11", title:"asdf", reporter:{username:"qwer", role:"manager"}} - здесь у меня есть пользователь, связанный с проблемой). Должен ли я создать другой документ "пользователь" и ссылаться на него в документе "проблема" по его идентификатору (например, в реляционных базах данных) или оставить все пользовательские данные в поддоку?

  • Если у меня есть объекты (поддокументы) в документе, могу ли я обновить их все в одном запросе?

4b9b3361

Ответ 1

Я совершенно новичок в документах, ориентированных на базы данных, и сейчас я пытаюсь разработать своего рода CMS с помощью node.js и mongodb, поэтому я столкнусь с теми же проблемами, что и вы.

В результате проб и ошибок я нашел это эмпирическое правило: я создаю коллекцию для каждого объекта, который может быть "субъектом" для моих запросов, в то же время встраивая остальные в другие объекты.

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

Ответ 2

Красота mongodb и другого продукта "NoSQL" заключается в том, что нет никакой схемы для проектирования. Я использую MongoDB, и мне это нравится, не нужно писать SQL-запросы и ужасные запросы JOIN! Поэтому, чтобы ответить на ваши два вопроса.

1 - Если вы создаете несколько документов, вам нужно сделать два вызова в БД. Не сказать это плохо, но если вы можете бросить все в один документ, почему бы и нет? Я помню, когда я использовал MySQL, я бы создал таблицу "блог" и таблицу "комментарии". Теперь я добавляю комментарии к записи в том же сборнике (aka table) и продолжаю строить на нем.

2 - Да...

Ответ 3

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

Для вашего примера

issue = {code:"asdf-11", title:"asdf", reporter:{username:"qwer", role:"manager"}}

Я сделал бы проблему и репортера каждый свой собственный документ и обратился к репортеру в проблеме. Вы также можете ссылаться на список проблем в репортере. Таким образом, вы не будете дублировать репортеров в проблемах, вы можете запросить их каждый отдельно, вы можете запросить репортера по проблеме, и вы можете запросить проблемы у репортера. Если вы вставляете репортера в проблему, вы можете запросить только один способ, репортер по проблеме.

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

Ответ 4

Конструкция схемы в документах-ориентированных БД может показаться сложной на первый взгляд, но при создании моего запуска с Symfony2 и MongoDB я обнаружил, что 80% времени похоже на реляционную БД.


Сначала подумайте, как обычный db:

Чтобы начать, просто создайте свою схему, как и с реляционным Db:

Каждый Entity должен иметь свои собственные Collection, , особенно если вам нужно будет разбивать на него документы.

(в Mongo вы можете несколько разбивать вложенные массивы документов, но возможности ограничены)


Затем просто удалите слишком сложную нормализацию:

  • Мне нужна отдельная таблица категорий? (просто напишите категорию в свойстве column/как строку или встроенный документ)
  • Можно ли хранить комментарии непосредственно в виде коллекции Int в коллекции автора? (затем обновить счетчик событием, например, в Doctrine ODM)

Встроенные документы:

Используйте встроенные документы только для:

  • clearness (вложенные документы вроде: addressInfo, billingInfo в коллекции пользователей)
  • для хранения тегов/категорий (например: [ name: "Sport", parent: "Hobby", page: "/sport" ])
  • для хранения простых нескольких значений (например, в User коллекции: список специальностей, список личных веб-сайтов)

Не используйте их, если:

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

Повторяющиеся значения для подсчета коллекции и прекомпутов:

Дублируйте значения столбцов/атрибутов из коллекции в другую, если вам нужно выполнить запрос с каждым значением в условиях где. (помните, что нет join s)

например: В коллекции билетов также указывается имя автора (а не только ID)

Кроме того, если вам нужен счетчик (количество билетов, открываемых пользователем, по категории, ecc), предварительно скопируйте их.


Вставить ссылки:

Если у вас есть ссылка "один-ко-многим" или "много-ко-многим", используйте встроенный массив со списком идентификаторов документов, на которые ссылаются (см. MongoDB DB Ref).

Вам понадобится снова использовать Событие, чтобы удалить идентификатор, если удаленный документ будет удален. (Существует расширение для Doctrine ODM, если вы его используете: Reference Integrity)

Этот тип ссылок напрямую управляется Doctrine ODM: Ссылка Многие


Легко исправить ошибки:

Если вы обнаружите, что ошиблись в дизайне схемы, вы можете просто исправить ее несколькими строками Javascript для запуска непосредственно в консоли Mongo.

(хранимые процедуры упрощены: нет необходимости в сложных сценариях миграции)

Waring: не используйте Doctrine ODM Migrations, вы пожалеете об этом позже.

Ответ 5

Мне нравится MongoDB, но я должен сказать, что я буду использовать его намного более трезво в своем следующем проекте.

В частности, мне не хватило удачи в установке Embedded Document, поскольку люди обещают.

Встроенный документ представляется полезным для композиции (см. UML Composition), но не для агрегации. Листовые узлы велики, все, что находится в середине графика объекта, не должно быть встроенным документом. Это сделает поиск и подтверждение ваших данных более сложными, чем вы хотели бы.

Одна вещь, которая в MonoDB абсолютно лучше, - это ваши отношения "многие-к-х". Вы можете сделать много-ко-многим с двумя таблицами, и можно представить отношение "один-к-одному" в любой таблице. То есть вы можете поместить 1 ключ в N строк или N ключей в 1 строку или и то, и другое. Примечательно, что запросы для выполнения заданий (пересечение, объединение, непересекающийся набор и т.д.) На самом деле понятны вашим коллегам. Я никогда не был доволен этими запросами в SQL. Мне часто приходится соглашаться на "два других человека, которые это поймут".

Если у вас когда-либо были ваши данные, они стали очень большими, вы знаете, что вставки и обновления могут быть ограничены тем, сколько индексов стоит. Вам нужно меньше индексов в MongoDB; индекс для A-B-C может использоваться для запроса A, A и B, или и B и C (но не B, C, B и C или и C). Кроме того, возможность инвертировать отношения позволяет перемещать некоторые индексы во вторичные таблицы. Мои данные не получили достаточно большой возможности, но я надеюсь, что это поможет.

Ответ 6

Отмените этот ответ, поскольку исходный ответ принял неверное отношение из-за неправильного чтения.

issue = {code: "asdf-11", название: "asdf", репортер: {имя пользователя: "qwer", роль: "manager" }}

Что касается того, является ли вложение некоторой важной информации о пользователе (создателе) билета, это мудрое решение или нет, зависит от специфики системы.

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

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

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

Есть одна вопиющая проблема с этой схемой, и это дублирование изменения повторяющихся данных (нормализованная форма).

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

Чтобы снова противоречить себе, если бы у вас было всего 100 билетов (что-то управляемое) для каждого пользователя, то операция обновления, вероятно, не была бы слишком плоха и, по сути, была бы легко управляемой для базы данных; плюс из-за отсутствия движения (надеюсь) документов, это будет полностью обновлено.

Итак, нужно ли это встраивать или нет, сильно зависит от ваших запросов и документов и т.д. Однако я бы сказал, что эта схема не является хорошей идеей; в частности, из-за дублирования изменения данных во многих корневых документах. Технически, да, вы могли бы избежать этого, но я бы не стал пытаться.

Я бы разделил два.

Если у меня есть объекты (поддокументы) в документе, могу ли я обновить их все в одном запросе?

Как и стиль отношений в моем первоначальном ответе, да и легко.

Например, обновите роль Nigel до MD (как было намечено ранее) и измените статус билета на завершенный:

db.tickets.update({'reporter.username':'Nigel'},{$set:{'reporter.role':'MD', status: 'completed'}})

Таким образом, единственная схема документа делает CRUD проще в этом случае.

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

Снова надеемся, что это имеет смысл, и я ничего не оставил. НТН


Оригинальный ответ

здесь у меня есть пользователь, связанный с проблемой). Должен ли я создать другой документ "пользователь" и ссылаться на него в документе "проблема" по его идентификатору (например, в реляционных базах данных) или оставить все пользовательские данные в поддоку?

Это важный вопрос и требует предварительного знания, прежде чем продолжить.

Первое, что нужно учитывать, это размер проблемы:

issue = {code:"asdf-11", title:"asdf", reporter:{username:"qwer", role:"manager"}}

Не очень большой, и поскольку вам больше не нужна информация reporter (которая будет в корневом документе), она может быть меньше, однако проблемы никогда не будут такими простыми. Если вы посмотрите на MongoDB JIRA, например: https://jira.mongodb.org/browse/SERVER-9548 (как случайная страница, которая подтверждает мою точку зрения) содержимое "билета" "на самом деле может быть довольно значительным.

Единственный способ получить истинную выгоду от встраивания билетов будет, если вы сможете хранить ВСЕ пользовательскую информацию в одном блоке 16 МБ условного тиража, который является максимальным размером документа BSON (наложенным mongod в настоящее время).

Я не думаю, что вы сможете хранить все билеты под одним пользователем.

Даже если вам нужно было сменить билет на, может быть, код, название и описание, которые вы все еще можете пострадать от проблемы "швейцарского сыра", вызванные регулярными обновлениями и изменениями в документах в MongoDB, как и раньше: http://www.10gen.com/presentations/storage-engine-internals является хорошей ссылкой на то, что я имею в виду.

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

Вы можете, конечно, немного исправить эту проблему, используя силу распределения двух размеров: http://docs.mongodb.org/manual/reference/command/collMod/#usePowerOf2Sizes, которая будет делать именно то, что она говорит на олове.

Хорошо, гипотетически, если бы у вас были только code и title, то да, вы могли бы хранить билеты в качестве поддокументов у пользователя root без особых проблем, однако это то, что сводится к особенностям того, что цессионарий щедрот не упомянул.

Если у меня есть объекты (поддокументы) в документе, могу ли я обновить их все в одном запросе?

Да, довольно легко. Это одна вещь, которая становится проще с внедрением. Вы можете использовать такой запрос, как:

db.users.update({user_id:uid,'tickets.code':'asdf-1'}, {$set:{'tickets.$.title':'Oh NOES'}})

Однако, обратите внимание, что вы можете обновлять только один поддокумент за один раз с помощью оператора позиционирования. Таким образом, это означает, что вы не можете в одной атомной операции обновить все даты билетов на одного пользователя до 5 дней в будущем.

Что касается добавления нового билета, это довольно просто:

db.users.update({user_id:uid},{$push:{tickets:{code:asdf-1,title:"Whoop"}}})

Итак, вы можете просто, в зависимости от ваших запросов, обновить все данные пользователей за один раз.

Это был довольно длинный ответ, поэтому, надеюсь, я ничего не пропустил, надеюсь, что это поможет.