Как бы вы сделали связь "многие-ко-многим" с MongoDB?
Например; скажем, у вас есть таблица "Пользователи" и таблица "Роли". У пользователей много ролей, и у ролей много пользователей. В области SQL вы создадите таблицу UserRoles.
Users:
Id
Name
Roles:
Id
Name
UserRoles:
UserId
RoleId
Как отношения такого же отношения обрабатываются в MongoDB?
В зависимости от ваших запросов вы можете поместить все в пользовательский документ:
{name:"Joe"
,roles:["Admin","User","Engineer"]
}
Чтобы получить всех Инженеров, используйте:
db.things.find( { roles : "Engineer" } );
Если вы хотите сохранить роли в отдельных документах, вы можете включить документ _id в массив ролей вместо имени:
{name:"Joe"
,roles:["4b5783300334000000000aa9","5783300334000000000aa943","6c6793300334001000000006"]
}
и установите такие роли, как:
{_id:"6c6793300334001000000006"
,rolename:"Engineer"
}
Вместо того, чтобы пытаться моделировать в соответствии с нашим многолетним опытом работы с RDBMS, мне было намного проще моделировать решения для репозитория документов с использованием MongoDB, Redis и других хранилищ данных NoSQL, оптимизируя для чтения варианты использования, будучи внимательным операций атомной записи, которые должны поддерживаться случаями использования записи.
Например, использование домена "Пользователи в ролях":
Это может быть смоделировано как следующие шаблоны документов:
User: { _id: UniqueId, name: string, roles: string[] }
Indexes: unique: [ name ]
Role: { _id: UniqueId, name: string, users: string[] }
Indexes: unique: [ name ]
Для поддержки высокочастотных применений, таких как функции, связанные с ролями, из пользовательского объекта User.Roles намеренно денормализуется, сохраняется на пользователе, а также на Role.Users, имеющих дублирующее хранилище.
Если это не так очевидно в тексте, но это тип мышления, который рекомендуется при использовании репозиториев документов.
Я надеюсь, что это поможет преодолеть разрыв в отношении прочитанной стороны операций.
Для стороны записи следует поощрять моделирование в соответствии с атомной записью. Например, если структура документа требует приобретения блокировки, обновления одного документа, затем другого и, возможно, большего количества документов, а затем освобождения блокировки, вероятно, модель потерпела неудачу. Просто потому, что мы можем создавать распределенные блокировки, это не значит, что мы должны использовать их.
В случае модели "Пользователь в ролях" операции, которые растягивают наше запрет на блокировку записи в ящики, - это добавление или удаление пользователя из роли. В любом случае успешная операция приводит к обновлению как одного пользователя, так и одного документа роли. Если что-то не удается, легко выполнить очистку. Это единственная причина, по которой шаблон Unit of Work появляется довольно часто, когда используются репозитории документов.
Операция, которая действительно расширяет наше запрет на блокировку записи атома, - это очистка роли, что приведет к тому, что многие пользовательские обновления удалит Role.name из массива User.roles. Эта операция очистки тогда обычно обескуражена, но при необходимости может быть реализована путем упорядочения операций:
В случае проблемы, которая, скорее всего, возникнет на шаге 2, откат прост, так как один и тот же набор имен пользователей с шага 1 может использоваться для восстановления или продолжения.
Я только что наткнулся на этот вопрос и, хотя он старый, я подумал, что было бы полезно добавить пару возможностей, не упомянутых в ответах. Кроме того, в последние несколько лет ситуация немного изменилась, поэтому стоит подчеркнуть, что SQL и NoSQL приближаются друг к другу.
Один из комментаторов поднял мудрый предостерегающий подход: "если данные реляционные, используйте реляционные". Однако этот комментарий имеет смысл только в реляционном мире, где схемы всегда появляются перед приложением.
ОТЛИЧИТЕЛЬНЫЙ МИР: Структурные данные > Напишите приложение, чтобы получить его
NOSQL WORLD: приложение для проектирования > Соответствующие структурные данные
Даже если данные являются реляционными, NoSQL по-прежнему является опцией. Например, отношения "один ко многим" не являются проблемой вообще и широко освещаются в Документах MongoDB
Поскольку этот вопрос был опубликован, были предприняты серьезные попытки приблизить noSQL к SQL. Команда, возглавляемая Яннисом Папаконстантину в Калифорнийском университете (Сан-Диего), работает над FORWARD, реализацией SQL ++, которая вскоре может стать решением постоянных проблем как тот, который вы отправили здесь.
На более практическом уровне выпуск Couchbase 4.0 означает, что в первый раз вы можете создавать собственные JOINs в NoSQL. Они используют свой собственный N1QL. Это пример JOIN
из их руководств:
SELECT usr.personal_details, orders
FROM users_with_orders usr
USE KEYS "Elinor_33313792"
JOIN orders_with_users orders
ON KEYS ARRAY s.order_id FOR s IN usr.shipped_order_history END
N1QL допускает большинство, если не все операции SQL, включая агрегирование, фильтрацию и т.д.
Если MongoDB по-прежнему остается единственным вариантом, я хотел бы вернуться к своей точке зрения, что приложение должно иметь приоритет над структурой данных. Ни один из ответов не упоминает гибридное вложение, в котором большинство запрашиваемых данных встроены в документ/объект, а ссылки хранятся в меньшем числе случаев.
Пример: может ли информация (кроме имени роли) ждать? может ускорить загрузку приложения, не запрашивая ничего, что пользователю еще не нужно?
Это может быть так, если пользователь входит в систему и ему/ей необходимо просмотреть все параметры для всех ролей, к которым он принадлежит. Тем не менее, пользователь является "инженером", и опции для этой роли редко используются. Это означает, что приложение должно показывать параметры для инженера в случае, если он захочет нажать на них.
Это может быть достигнуто с помощью документа, который сообщает приложению в начале (1), к каким ролям принадлежит пользователь, и (2), где можно получить информацию о событии, связанном с определенной ролью.
{_id: ObjectID(),
roles: [["Engineer", "ObjectId()"],
["Administrator", "ObjectId()"]]
}
Или, что еще лучше, проиндексируйте поле role.name в коллекции ролей, и вам может не понадобиться вставлять ObjectID().
Другой пример: это информация обо всех запрошенных роликах ВСЕ время?
Также может быть случай, когда пользователь входит в панель управления, а 90% времени выполняет задачи, связанные с ролью "Инженер". Гибридное вложение может быть выполнено для этой конкретной роли в полном объеме и содержать ссылки только для остальных.
{_id: ObjectID(),
roles: [{name: "Engineer",
property1: value1,
property2: value2
},
["Administrator", "ObjectId()"]
]
}
Будучи схематичным, это не просто характеристика NoSQL, но и может быть преимуществом в этом случае. Это совершенно верно для размещения разных типов объектов в свойстве "Роли" объекта пользователя.
если сотрудник и компания объект-объект попробуйте использовать следующую схему:
employee{
//put your contract to employee
contracts:{ item1, item2, item3,...}
}
company{
//and duplicate it in company
contracts:{ item1, item2, item3,...}
}