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

Дизайн схемы mongodb для блогов

Как бы вы разработали схему для блочного сайта с базами данных на основе документов (mongodb). Сайт имеет следующие объекты: Пользователь, Статья, Комментарий. Пользователь может добавлять комментарии к статье. Каждый Пользователь может также голосовать ровно один раз за комментарий.

Я хочу иметь возможность эффективно выполнять эти запросы:
1. получить статью A, комментарии к статье A и количество голосов за комментарии
2. получить все комментарии пользователя B по всем статьям
3. получить все комментарии Пользователь B проголосовал за

Моя первая попытка - разместить статьи и комментарии в отдельных сборниках, а комментарий может содержать список пользователей, голосовавших за него. Это упрощает запрос 1 и 2. И для 3 я добавил коллекцию Vote, которая отслеживает голоса пользователей.

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

Article {
  "user_id"
}

Comment {
   "user_id",
   "article_id",
   [user_voted],
}

Vote {
    "user_id",
    "comment_id",
}
4b9b3361

Ответ 1

Article {
  "_id" : "A",
  "title" : "Hello World",
  "user_id" : 12345,
  "text" : 'My test article',

  "comments" : [
    { 'text' : 'blah', 'user_id' : 654321, 'votes' : [987654]},
    { 'text' : 'foo', 'user_id' : 987654, 'votes' : [12345, 654321] },
    ...
  ]
}

Основная предпосылка заключается в том, что я вложен Comments внутри Article. Votes применяется только к Comment, поэтому они были сохранены как массив с каждым Comment. В этом случае я только что сохранил user_id. Если вы хотите сохранить дополнительную информацию (time_created и т.д.), Вы можете проголосовать за массив объектов:

... 'votes' : [ { user_id : 987654, ts : 78946513 } ] ...

Как эффективно выполнять ваши запросы:

  • получить статью A, комментарии к статье A и количеству голосов за комментарии
db.articles.find( { _id : 'A' } )

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

  • получить все комментарии пользователя B по всем статьям
db.articles.ensureIndex( { "comments.user_id" : 1 } )
db.articles.find( { "comments.user_id" : 987654 } ) // returns all document fields

Индекс позволит эффективно искать комментарии в документе.

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

db.articles.find( { "comments.user_id" : 987654 }, { "title" : 1, "comments.user_id" : 1 })
  • получить все комментарии Пользователь B проголосовал за
db.articles.ensureIndex( { "comments.votes" : 1 } )
db.articles.find( { "comments.votes" : 987654 } )

Опять же, это вернет все Статьи, а не только комментарии.

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

Получение списка "комментариев, за которые я проголосовал", не очень полезно без самого комментария. Конечно, комментарий не очень полезен без самой статьи (или, по крайней мере, только названия).

В большинстве случаев запрос №3 переходит в соединение от Votes до Comments до Articles. Если это так, то почему бы просто не вернуть статьи для начала?