MongoDB много-ко-многим Ассоциации

Как бы вы сделали много-ко-многим ассоциацию с MongoDB?

например, допустим у вас есть таблица пользователей и таблица ролей. У пользователей много ролей, а у ролей много пользователей. В SQL land вы создадите таблицу UserRoles.

Users:
    Id
    Name

Roles:
    Id
    Name

UserRoles:
    UserId
    RoleId

как обрабатываются такие же отношения в MongoDB?

4 ответов


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

{name:"Joe"
,roles:["Admin","User","Engineer"]
}

чтобы получить всех инженеров, используйте:

db.things.find( { roles : "Engineer" } );

если вы хотите сохранить роли в отдельных документах, вы можете включить _id документа в массив ролей вместо имени:

{name:"Joe"
,roles:["4b5783300334000000000aa9","5783300334000000000aa943","6c6793300334001000000006"]
}

и настроить роли, как:

{_id:"6c6793300334001000000006"
,rolename:"Engineer"
}

вместо того, чтобы пытаться моделировать в соответствии с нашим многолетним опытом работы с СУБД, я обнаружил, что гораздо проще моделировать решения для хранения документов с использованием MongoDB, Redis и других хранилищ данных NoSQL путем оптимизации для случаев использования чтения, будучи внимательным к атомарным операциям записи, которые должны поддерживаться случаями использования записи.

например, использование домена" пользователи В ролях":

  1. Роль - Создание, Чтение, Обновление, Удаление, Список Пользователи, Добавить пользователя, удалить пользователя, очистить всех пользователей, индекс пользователя или аналогично поддержке "пользователь в роли" (операции, такие как контейнер + собственные метаданные).
  2. пользователь-создание, чтение, обновление, удаление (операции CRUD, как самостоятельная сущность)

Это можно смоделировать как следующие шаблоны документов:

User: { _id: UniqueId, name: string, roles: string[] }
    Indexes: unique: [ name ]
Role: { _id: UniqueId, name: string, users: string[] }
    Indexes: unique: [ name ]

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

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

Я надеюсь, что это поможет преодолеть разрыв в отношении стороны чтения операций.

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

в случае пользователя в модели ролей операции, которые растягивают наше атомарное предотвращение записи блокировок, добавляют или удаляют пользователя из роли. В любом случае успешная операция приводит к обновлению как одного пользователя, так и одного документа роли. Если что-то не удается, это легко выполните очистку. Это одна из причин, по которой шаблон единицы работы появляется довольно много, где используются репозитории документов.

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

  1. список пользователей от роли.пользователи.
  2. повторите имена пользователей с шага 1, Удалите имя роли из User.роли.
  3. четкая роль.пользователи.

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


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

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

РЕЛЯЦИОННЫЙ МИР: структура данных > написать приложение, чтобы получить его
NOSQL WORLD: применение конструкции > данные по структуры соответственно

даже если данные реляционные, NoSQL по-прежнему является опцией. Например, отношения "один ко многим" не являются проблемой вообще и широко освещаются в MongoDB docs

РЕШЕНИЕ 2015 ДЛЯ 2010 Проблема

поскольку этот вопрос был опубликован, были серьезные попытки приблизить noSQL к SQL. Команда под руководством Янниса Папаконстантину из Калифорнийского университета (Сан-Диего) работает над вперед, реализация SQL++, которая вскоре может стать решением постоянных проблем, таких как опубликованная здесь.

на более практическом уровне выпуск Couchbase 4.0 означает, что впервые вы можете выполнять собственные соединения в 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()”]]
   }

или, что еще лучше, индексировать роль.имя поля в коллекции ролей, и Вам может не понадобиться встраивать ObjectID() либо.

другой пример: является ли информация обо всех ролях, запрашиваемых все время?

также может быть так, что пользователь входит в панель мониторинга и 90% времени выполняет задачи, связанные с ролью "инженер". Гибридное встраивание может быть сделано для этой конкретной роли в полном объеме и сохранить ссылки только для остальных.

{_id: ObjectID(),
  roles: [{name: “Engineer”, 
           property1: value1,
           property2: value2
          },   
          [“Administrator”, “ObjectId()”]
         ]
}

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


в случае, если сотрудник и компания субъект-объект попробуйте использовать следующую схему:

employee{
   //put your contract to employee
   contracts:{ item1, item2, item3,...}
}

company{
   //and duplicate it in company
   contracts:{ item1, item2, item3,...}
}