Mongo DB отношения между документами в разных коллекциях

Я еще не готов отпустить это, поэтому я передумал проблему и отредактировал Q (оригинал ниже).


Я использую mongoDB для проекта на выходные, и для этого требуются некоторые отношения в БД, в чем все это страдание:

У меня есть три коллекции:

Users
Lists
Texts

Пользователь может иметь тексты и списки - списки, содержащие "тексты". Тексты могут быть в нескольких списках.

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

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

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

var TextSchema = new Schema({
      _id: Number,
      name: String,
      inListID: { type : Array , "default" : [] },
      [...]

Также редко бывает, что тексты будут в МНОГИХ списках, поэтому массив не будет взрываться. Вопрос типа остается, хотя, есть шанс, что это масштабы или на самом деле лучший способ реализовать его с помощью mongoDB? Помогло бы ли это ограничить количество списков, текст которых может быть (возможно)? Есть ли рецепт для нескольких: много отношений?

Было бы здорово получить ссылки на проекты, где это было сделано и как оно было реализовано (несколько: многие отношения). Я не могу поверить, что все уклоняются от БД монго, как только нужны некоторые отношения.



Оригинальный вопрос

Я сломаю его в двух проблемах, которые я вижу до сих пор: 1) Предположим, что список состоит из 5 текстов. Как я могу ссылаться на тексты, содержащиеся в списке? Просто откройте массив и сохраните там текст _ids? Похоже, эти массивы могут вырасти до луны и назад, замедляя приложение? С другой стороны, тексты должны быть доступны без списка, поэтому внедрение не является вариантом. Что делать, если я хочу получить все тексты списка, содержащего 100 текстов.. звучит как два запроса и массив со 100 полями: -/. Таким образом, этот способ ссылки на правильный способ сделать это?

var ListSchema = new Schema({
  _id: Number,
  name: String,
  textids: { type : Array , "default" : [] },
  [...]

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

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

Мне также интересны общие мысли о наилучшей практике для такого рода ссылок (многие-ко многим?) и особенно масштабируемости/производительности.

Ответ 1

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

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

В конце концов, реляционные базы данных имеют только три отличия:

  • Ограничения внешнего ключа: вы должны управлять ими самостоятельно, поэтому существует некоторый риск для мертвых ссылок.
  • Изоляция транзакций. Поскольку транзакций с несколькими документами нет, существует вероятность создания недопустимых ограничений внешнего ключа, даже если код правильный (в том смысле, что он никогда не пытается создать мертвую ссылку), но просто прерывается во время выполнения, Кроме того, трудно проверить мертвые ссылки, потому что вы можете наблюдать за состоянием гонки.
  • Соединения: MongoDB не поддерживает соединения, хотя ручной подзапрос с $in действительно масштабирует до нескольких тысяч элементов в $in -clause, конечно, если ссылочные значения индексируются, конечно

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

Примером "по-настоящему" реляционного запроса может быть "Найди мне всех клиентов, которые купили продукты, которые получили 4-звездочные отзывы от клиентов, которые в июне достигли высоких оборотов". Если у вас нет очень специализированной схемы, которая по существу была построена для поддержки этого запроса, вам, скорее всего, нужно будет найти все заказы, сгруппировать их по идентификаторам клиентов, взять верхние результаты, использовать их для запроса оценок с помощью $in и используйте другой $in, чтобы найти фактических клиентов. Тем не менее, если вы можете ограничить себя вершиной, скажем, 10 000 клиентов в июне, это три раунда и несколько быстрых запросов $in.

Это, вероятно, будет в диапазоне 10-30 мс на обычном облачном оборудовании, если ваши запросы поддерживаются индексами в ОЗУ, а сеть не полностью перегружена. В этом примере все становится беспорядочным, если данные слишком скудны, то есть топ-10k пользователей вряд ли написаны > 4-звездочные обзоры, что заставит вас написать программную логику, которая достаточно умна, чтобы продолжать повторять первый шаг, который является сложным и медленным, но если это такой важный сценарий, в любом случае, вероятно, лучше подходит структура данных.

Ответ 2

Использование MongoDB со ссылками - это путь к проблемам производительности. Прекрасный пример того, что не использовать. Это отношение m:n, где m и n могут масштабироваться до миллионов. MongoDB работает хорошо, где мы имеем 1:n(few), 1:n(many), m(few):n(many). Но не в ситуациях, когда у вас есть m(many):n(many). Это, очевидно, приведет к двум запросам и большому количеству домашнего хозяйства.

Ответ 3

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

Используйте встроенные модели данных, когда: у вас есть модель "один-к-одному" или "один-ко-многим".
Для модели "многие-ко-многим" используются отношения с документами.

Я думаю, это ответ), но этот ответ дает много проблем, потому что:

  • Как уже упоминалось, mongo вообще не предоставляет транзакции.
  • И у вас нет ограничений внешнего ключа.
  • Даже если у вас есть ссылки (DBRefs) между документами, вы столкнетесь с удивительной проблемой, как разыменовать эти документы.

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

Я понятия не имею, как делаются ограничения внешнего ключа, и я не видел в этом направлении что-то в документации mongo, поэтому я думаю, что это потрясающая задача (и риск для проекта).

И последние ссылки mongo - это не соединение mysql, и вы не получаете все данные из родительской коллекции с данными из дочерней коллекции (например, все поля из таблицы и все поля из объединенной таблицы в mysql), вы получите просто ССЫЛКА к другому документу в другой коллекции, и вам нужно будет что-то сделать с этой ссылкой (разыменованием). Его можно легко получить в node путем обратного вызова, но только в том случае, если вам нужен только один текст из одного списка, но если вам нужны все тексты в одном списке - это ужасно, но если вам нужны все тексты в более чем одном списке - он стал кошмаром...

Возможно, это мой не лучший опыт... но я думаю, вы должны подумать об этом...

Ответ 4

Использование массива в MongoDB обычно не является предпочтительным и обычно не рекомендуется экспертами.

Вот решение, которое пришло мне в голову:

Каждый документ Users всегда уникален. Для отдельного документа в Users могут быть Lists и Texts. Таким образом, Lists и Texts имеют поле для USER ID, которое будет _id of Users.

Lists всегда есть владелец в Users, поэтому они хранятся как есть.

Владелец Texts может быть либо Users, либо List, поэтому вы должны также сохранить в нем поле идентификатора LIST, которое будет _id Lists.

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

Ответ 5

Написание ответа, как я хочу объяснить, как я буду исходить отсюда.

Принимая во внимание ответы здесь и мои собственные исследования по этой теме, на самом деле может быть прекрасно хранить эти ссылки (а не действительно отношения) в массиве, пытаясь сохранить релятивизм небольшим: менее 1000 полей очень вероятно в моем случай.

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

С текстовой схемой:

var textSchema = new Schema({
  _id: {type: Number, required: true, index: { unique: true }},
  ...
  inList: { type : [Number] , "default" : [], index: true }
});

Я могу просто получить все тексты в списке с этим запросом, где inList - это индексированный массив, содержащий _ids текстов в списке.

Text.find({inList: listID}, function(err, text) {
  ...      
});

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

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

@mnemosyn: спасибо за ссылку и указав, что это действительно не большое объединение, или, другими словами, просто очень простое отношение. Также некоторые цифры о том, как долго выполняются эти сложные операции (из-за аппаратной зависимости), являются большой помощью.
PS: Grüße aus Bielefeld.

То, что я нашел наиболее полезным во время своего собственного исследования было этим vid, где Элвин Ричардс также говорит о связях "многие ко многим" примерно в мин., 17. Здесь я получил идею сделать одностороннее отношение, чтобы сохранить себе работу, очищающую мертвые ссылки.

Спасибо за помощь. 👍