Применение функции и сортировка в MongoDB без MapReduce - PullRequest
0 голосов
/ 09 февраля 2012

У меня интересная проблема. У меня есть рабочая M / R-версия этого, но это не очень жизнеспособное решение в маломасштабной среде, так как это слишком медленно и запрос должен выполняться в режиме реального времени.

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

Вот функция, которую я хотел бы применить к каждому документу в псевдокоде.

var score = 0;
foreach(tag in document.Tags) {
    score += someMap[tag];
}
return score;

1 Ответ

3 голосов
/ 09 февраля 2012

Поскольку ваш someMap меняется каждый раз, я не вижу другой альтернативы, кроме как оценить все документы и вернуть наиболее высоко оцененные. Какой бы метод вы ни выбрали для этого типа операций, вам придется учитывать все документы в коллекции, которая будет медленной и будет становиться все более и более дорогостоящей по мере роста сканируемой коллекции.

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

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

Наконец, если вы хотите дождаться выпуска MongoDB 2.2 (что должно произойти в течение нескольких месяцев), вы можете использовать новую структуру агрегирования вместо сокращения карты. Вам придется массировать someMap, чтобы сгенерировать правильные шаги конвейера. Вот пример того, как это могло бы выглядеть, если бы someMap были {"a": 5, "b": 2}:

db.runCommand({aggregate: "foo",
    pipeline: [
        {$unwind: "$tags"},
        {$project: {
            tag1score: {$cond: [{$eq: ["$tags", "a"]}, 5, 0]},
            tag2score: {$cond: [{$eq: ["$tags", "b"]}, 3, 0]}}
        },
        {$project: {score: {$add: ["$tag1score", "$tag2score"]}}},
        {$group: {_id: "$_id", score: {$sum: "$score"}}},
        {$sort: {score: -1}},
        {$limit: 10}
    ]})

Это немного сложно, и объясняет:

  1. Во-первых, мы «раскручиваем» массив тегов, чтобы следующие шаги в конвейере обрабатывали документы, где «теги» - это скаляр - значение тега из массива - и все остальные поля документа (в частности, _id) дублируются для каждого размотанного элемента.
  2. Мы используем оператор проекции для преобразования тегов в именованные поля оценки. Выражение $cond / $eq для каждого примерно означает (для примера tag1score) "если значение в документе в поле идентификатора тегов с идентификатором равно" a ", вернуть 5 и присвоить это значение новое поле tag1score, иначе верните 0 и присвойте ему ". Это выражение будет повторяться для каждой комбинации тег / оценка в вашем someMap. В этой точке конвейера каждый документ будет содержать N tagNscore полей, но самое большее одно из них будет иметь ненулевое значение.
  3. Далее мы используем другой оператор проекции для создания поля score, значение которого является суммой полей tagNscore в документе.
  4. Затем мы группируем документы по их _id и суммируем значение поля score из предыдущего шага для всех документов в каждой группе.
  5. Мы сортируем по score, по убыванию (т.е. сначала набираем наибольшее количество баллов)
  6. Мы ограничиваемся только лучшими 10 баллами.

Я оставлю читателю в качестве упражнения, как преобразовать someMap в правильный набор проекций на шаге 2 и правильный набор полей, чтобы добавить на шаге 3.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...