/ / MongoDB, MapReduce y clasificación - mongodb, mongodb-php, mongodb-query

MongoDB, MapReduce y clasificación - mongodb, mongodb-php, mongodb-query

Podría estar un poco más loco porque todavía estoy aprendiendo los entresijos de MongoDB, pero aquí va.

Ahora mismo estoy trabajando en una herramienta para buscar / filtrara través de un conjunto de datos, ordénelo por un punto de datos arbitrario (por ejemplo, popularidad) y luego agrúpelo por una identificación. La única forma en que veo que puedo hacer esto es a través de la funcionalidad MapReduce de Mongo.

No puedo usar .group () porque estoy trabajando con más de 10,000 claves y también necesito poder ordenar el conjunto de datos.

Mi código MapReduce funciona bien, excepto por una cosa: la clasificación. La clasificación simplemente no quiere funcionar en absoluto.

db.runCommand ({ "mapreduce": "productos", "mapa": función () { emitir({ product_id: this.product_id, popularidad: this.popularity }, 1); }, "reducir": función (clave, valores) { var sum = 0; valores.porCada (función (v) { suma + = v; });  suma de retorno; }, "query": {category_id: 20}, "fuera": {en línea: 1}, "sort": {popularidad: -1} });

Ya tengo un índice descendente en el punto de datos de popularidad, por lo que definitivamente no funciona debido a la falta de eso:

{"v": 1, "clave": {"popularidad": -1}, "ns": "app.products", "nombre": "Popularidad_-1"}

Simplemente no puedo entender por qué no quiere ordenar.

En lugar de incluir el conjunto de resultados, no puedo enviarlo a otra colección y luego ejecutar un .find (). Sort ({popularidad: -1}) en eso debido a la forma en que esta función va a funcionar.

Respuestas

14 para la respuesta № 1

En primer lugar, Mongo map / reduce no está diseñado para usarse como una herramienta de consulta (como lo está en CouchDB), está diseñado para que ejecute tareas en segundo plano. Lo uso en el trabajo para analizar datos de tráfico.

Sin embargo, lo que está haciendo mal es que está aplicando el tipo () a su entrada, pero es inútil porque cuando map() se realiza la etapa, los documentos intermedios se ordenan por cada keys. Debido a que su clave es un documento, se está ordenando por product_id, popularity.

Así es como generé mi conjunto de datos

function generate_dummy_data() {
for (i=2; i < 1000000; i++) {
db.foobar.save({
_id: i,
category_id: parseInt(Math.random() * 30),
popularity:    parseInt(Math.random() * 50)
})
}
}

Y esta es mi tarea de mapa / reducir:

var data = db.runCommand({
"mapreduce": "foobar",
"map": function() {
emit({
sorting: this.popularity * -1,
product_id: this._id,
popularity: this.popularity,
}, 1);
},
"reduce": function(key, values) {
var sum = 0;
values.forEach(function(v) {
sum += v;
});

return sum;
},
"query": {category_id: 20},
"out": {inline: 1},
});

Y este es el resultado final (muy largo para pegarlo aquí):

http://cesarodas.com/results.txt

Esto funciona porque ahora estamos ordenando por sorting, product_id, popularity. Puedes jugar con la clasificación como quieras solo recuerda que la clasificación final es por key independientemente de usted cómo se ordena su entrada.

De todos modos, como dije antes, debes evitar hacerLas consultas con Map / Reduce se diseñaron para el procesamiento en segundo plano. Si yo fuera usted, diseñaría mis datos de tal manera que pudiera acceder a ellos con consultas simples, siempre hay una compensación en este caso complejas inserciones / actualizaciones para tener consultas simples (así es como veo MongoDB).


8 para la respuesta № 2

Como se señaló en la discusión sobre la pregunta original:

  • Map / Reduce con salida en línea actualmente no puede usar un explícito sort clave (ver SERVIDOR-3973) Las posibles soluciones incluyen confiar en el orden de las teclas emitidas (ver la respuesta de @crodas), enviar a una colección y consultar esa colección con un orden de clasificación, o ordenar los resultados en su aplicación usando algo como usort ().

  • La preferencia de OP es obtener resultados en línea en lugar de crear / eliminar colecciones temporales.

  • los Marco de agregacion en MongoDB 2.2 (actualmente un candidato de lanzamiento de producción) proporcionaría una solución adecuada.

Aquí hay un ejemplo de una consulta similar al Mapa / Reducir original, pero en lugar de usar el Marco de agregación:

db.products.aggregate(
{ $match: { category_id: 20 }},
{ $group : {
_id : "$product_id",
"popularity" : { $sum : "$popularity" },
}},
{ $sort: { "popularity": -1 }}
)

.. y salida de muestra:

{
"result" : [
{
"_id" : 50,
"popularity" : 139
},
{
"_id" : 150,
"popularity" : 99
},
{
"_id" : 123,
"popularity" : 55
}
],
"ok" : 1
}