Поскольку ваш 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}
]})
Это немного сложно, и объясняет:
- Во-первых, мы «раскручиваем» массив тегов, чтобы следующие шаги в конвейере обрабатывали документы, где «теги» - это скаляр - значение тега из массива - и все остальные поля документа (в частности,
_id
) дублируются для каждого размотанного элемента.
- Мы используем оператор проекции для преобразования тегов в именованные поля оценки. Выражение
$cond
/ $eq
для каждого примерно означает (для примера tag1score
) "если значение в документе в поле идентификатора тегов с идентификатором равно" a ", вернуть 5 и присвоить это значение новое поле tag1score
, иначе верните 0 и присвойте ему ". Это выражение будет повторяться для каждой комбинации тег / оценка в вашем someMap
. В этой точке конвейера каждый документ будет содержать N tagNscore
полей, но самое большее одно из них будет иметь ненулевое значение.
- Далее мы используем другой оператор проекции для создания поля
score
, значение которого является суммой полей tagNscore
в документе.
- Затем мы группируем документы по их
_id
и суммируем значение поля score
из предыдущего шага для всех документов в каждой группе.
- Мы сортируем по
score
, по убыванию (т.е. сначала набираем наибольшее количество баллов)
- Мы ограничиваемся только лучшими 10 баллами.
Я оставлю читателю в качестве упражнения, как преобразовать someMap
в правильный набор проекций на шаге 2 и правильный набор полей, чтобы добавить на шаге 3.
Это, по сути, тот же набор шагов, который должен пройти ваш код приложения или карта, но он имеет следующие явные преимущества: вместо сокращения карты структура агрегации полностью реализована в C ++ и быстрее и более параллельна, чем карта уменьшить; и в отличие от запроса всех документов к вашему приложению, структура агрегации работает с данными на стороне сервера, сохраняя нагрузку на сеть. Но, как и в случае двух других подходов, для этого все равно придется учитывать каждый документ, и он может ограничить набор результатов только после того, как для всех будет рассчитана оценка.