2012-07-03 3 views
7

Допустим, у меня есть такой сайт, как digg.com У меня есть куча статей, и люди могут голосовать за статьи, которые им нравятся.MongoDB Schema Design - Голосование по сообщениям

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

Как обычно, с MongoDB существует несколько способов реализации этого, но я не уверен, какой из них правильный.

  • сообщений документ, который содержит массив голосов - голосов сами документы, содержащие идентификатор пользователя, имя пользователя и дату голосования:
{ 
    "_id": "ObjectId(xxxx)", 
    "title": "Post Title", 
    "postdate": "21/02/2012+1345", 
    "summary": "Summary of Article", 

    "Votes": [ 
     { 
      "userid":ObjectId(xxxx), 
      "username": "Joe Smith", 
      "votedate": "03/03/2012+1436" 
     }, 
      ] 
    } 
  • раздельный сбор голосов, содержащие сведения о личности голосование и ссылку на пост, который был признан на:
{ 
    "_id": "ObjectId(xxxx)", 
    "postId": ObjectId(xxxx), 
    "userId": ObjectId(xxxx), 
    "votedate": "03/03/2012+1436" 
} 

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

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

Или я использую комбинацию из двух? Также я бы сделал этот тип агрегированного запроса в режиме реального времени, при загрузке каждой страницы. Или я просто запускаю запрос один раз в минуту и ​​сохраняю результаты в коллекции результатов запроса?

Как вы бы описали эту схему?

+0

связанный с этим вопрос: http://stackoverflow.com/questions/ 9296793/effective-document-format-to-store-vote-in-mongo-db –

+0

Связано также: http://stackoverflow.com/questions/7046462/best-way-to-model-a-voting-system-in -mongodb – wmassingham

ответ

9

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

Поскольку это одно обновление, вам гарантировано, что счетчик будет соответствовать количеству элементов в массиве.

Если количество агрегатов фиксировано и сайт очень занят, вы можете расширить эту парадигму и увеличить дополнительные счетчики, например, один за месяц, день и час, но это может выйти из-под контроля очень быстро. Таким образом, вместо этого вы можете использовать новый (доступный в версии 2.1.2 dev, будет выпущен в версии 2.2). Его проще использовать, чем Map/Reduce, и это позволит вам делать вычисления, которые вы хотите очень просто, особенно если вы позаботьтесь о сохранении ваших дат голосования как тип ISODate().

Типичный трубопровод для агрегирования запроса для топ-добытчиками голосования в этом месяце может выглядеть примерно так:

today = new Date(); 
thisMonth = new Date(today.getFullYear(),today.getMonth()); 
thisMonthEnd = new Date(today.getFullYear(),today.getMonth()+1); 

db.posts.aggregate([ 
    {$match: { "Votes.votedate": {$gte:thisMonth, $lt:thisMonthEnd} } }, 
    {$unwind: "$Votes" }, 
    {$match: { "Votes.votedate": {$gte:thisMonth, $lt:thisMonthEnd} } }, 
    {$group: { _id: "$title", votes: {$sum:1} } }, 
    {$sort: {"votes": -1} }, 
    {$limit: 10} 
]); 

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

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

db.posts.aggregate([ 
    {$match: { "Votes.votedate": {$gte:thisMonth, $lt:thisMonthEnd} } }, 
    {$unwind: "$Votes" }, 
    {$match: { "Votes.votedate": {$gte:thisMonth, $lt:thisMonthEnd} } }, 
    {$project: { "day" : { "$dayOfMonth" : "$Votes.votedate" } } }, 
    {$group: { _id: "$day", votes: {$sum:1} } }, 
    {$sort: {"votes": -1} }, 
    {$limit: 10} 
]); 
+0

, если вы в конечном итоге сохраняете голоса в своей собственной коллекции, а не встраиваете в сообщения, тогда вам не понадобится шаг «$ unwind», остальная часть агрегации в основном останется прежней. –

+0

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

+0

Я пытаюсь использовать решение для ссылочных документов, но оно не работает. В моем тесте у меня 6 сообщений, и только один из них имеет один голос. Все остальные не имеют записей в коллекции 'posts_votes'. Если я запустил '{$ group: {_id:" $ vote.post_id ", голосов: {$ sum: 1}}}' Я получаю одну запись с _id нулевого значения. Если я изменю '$ vote.post_id' на' $ title', он вернет все 6 сообщений каждый с одним голосом (должен быть только один пост с голосованием, остальные - ноль). Также попробовал это с добавлением массива '$ project', который делает переменную, используя' $ vote.post_id' – Nathan

0

Выбранная вами схема в значительной степени зависит от вашего прецедента. Если вы ожидаете много голосов/комментариев и хотите обработать их независимо от должности, к которой они принадлежат, вы можете оставить их в отдельной коллекции с postID как «ключ foriegn». Однако, если вы хотите загрузить все голоса при загрузке определенного сообщения, а сами голоса не имеют никакого значения без сообщения, в котором их размещают, затем перейдите к вложению (в вашем случай, первый) подход.

+0

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

Смежные вопросы