#mongodb #mapreduce
#mongodb #mapreduce
Вопрос:
Я хотел бы сохранить только определенное подмножество коллекции. Я не нахожу никакой соответствующей информации об этом. Это трудно объяснить, поэтому я привел пример:
Допустим, у меня есть эта коллекция:
db.mycollection.save({ "category" : 1201, "score" : 0.5});
db.mycollection.save({ "category" : 1201, "score" : 0.4});
db.mycollection.save({ "category" : 1201, "score" : 0.3});
db.mycollection.save({ "category" : 1201, "score" : 0.5});
db.mycollection.save({ "category" : 1201, "score" : 0.1});
db.mycollection.save({ "category" : 1202, "score" : 0.5});
db.mycollection.save({ "category" : 1202, "score" : 0.6});
db.mycollection.save({ "category" : 1202, "score" : 0.1});
db.mycollection.save({ "category" : 1202, "score" : 0.3});
db.mycollection.save({ "category" : 1202, "score" : 0.1});
db.mycollection.save({ "category" : 1202, "score" : 0.4});
db.mycollection.save({ "category" : 1202, "score" : 0.3});
db.mycollection.save({ "category" : 1203, "score" : 0.8});
db.mycollection.save({ "category" : 1203, "score" : 0.4});
db.mycollection.save({ "category" : 1203, "score" : 0.7});
db.mycollection.save({ "category" : 1203, "score" : 0.3});
db.mycollection.save({ "category" : 1204, "score" : 0.2});
db.mycollection.save({ "category" : 1204, "score" : 0.8});
db.mycollection.save({ "category" : 1204, "score" : 0.7});
db.mycollection.save({ "category" : 1204, "score" : 0.9});
Моя цель — получить лучшие 3 строки из всех категорий (в отношении оценки).
В этом примере я пытаюсь получить такие результаты:
{ "category" : 1201, "score" : 0.5 }
{ "category" : 1201, "score" : 0.5 }
{ "category" : 1201, "score" : 0.4 }
{ "category" : 1202, "score" : 0.6 }
{ "category" : 1202, "score" : 0.5 }
{ "category" : 1202, "score" : 0.4 }
{ "category" : 1203, "score" : 0.8 }
{ "category" : 1203, "score" : 0.7 }
{ "category" : 1203, "score" : 0.4 }
{ "category" : 1204, "score" : 0.9 }
{ "category" : 1204, "score" : 0.8 }
{ "category" : 1204, "score" : 0.7 }
Но я действительно не знаю, как это сделать.
Я нашел обходной путь, запустив функцию map reduce, но это действительно очень медленно.
Это то, что я сделал:
var map = function()
{
emit(this.category, this.score);
}
var reduce = function(key, values)
{
var total = [];
values.forEach(function(value)
{
total.push(value);
});
total.sort();
total.reverse();
total = total.splice(0, 3);
return {scores: total};
}
db.mycollection.mapReduce(map, reduce, { out : "myoutput" } );
db.myoutput.find();
db.myoutput.drop();
Результат:
{ "_id" : 1201, "value" : { "scores" : [ 0.5, 0.5, 0.4 ] } }
{ "_id" : 1202, "value" : { "scores" : [ 0.6, 0.5, 0.4 ] } }
{ "_id" : 1203, "value" : { "scores" : [ 0.8, 0.7, 0.4 ] } }
{ "_id" : 1204, "value" : { "scores" : [ 0.9, 0.8, 0.7 ] } }
Это не совсем то, что я хотел, но оно выполняет свою работу.
Мой вопрос: это можно сделать без использования map-reduce? (Или с хорошей производительностью?)
PS: Извините за мой плохой английский. Я не владею свободно.
Редактировать:
Я, наконец, пришел с этим решением:
var map = function()
{
emit(this.category, this.score);
}
var reduce = function(key, values)
{
var total = [];
values.forEach(function(value)
{
if (value instanceof Array)
total.concat(value);
else if (value instanceof Object)
{
if (value.scores instanceof Array)
total.concat(value.scores);
else
total.push(value.scores);
}
else
total.push(value);
});
total.sort(function (a,b) { return b - a} );
total = total.splice(0, 3);
return {scores: total};
}
Ответ №1:
Вы можете очень легко получить свой результат для данной категории
db.myCollection.find({category : 1204}).sort({score : -1}.limit(3)
это даст 3 лучших результата для данной категории
затем вы можете выполнить цикл категорий, но для этого потребуется много запросов (по одному на категорию).
Решение map reduce — единственный способ сделать это, и у вас, похоже, есть рабочее решение. Если вы хотите улучшить свою производительность, поработайте над функцией reduce, особенно над следующей частью, которая не очень хороша :
values.forEach(function(value)
{
total.push(value);
});
total.sort();
total.reverse();
total = total.splice(0, 3);