2011-04-10 24 views
8

que estoy tratando de hacer un mapa sencillo reducir en la cáscara Mongo, pero la función de reducir nunca se llama. Este es mi código:Mongo DB Mapa/Reducir - Reducir imposible encontrar llamada

db.sellers.mapReduce( 
    function(){ emit(this._id, 'Map') } , 
    function(k,vs){ return 'Reduce' }, 
    { out: { inline: 1}}) 

Y el resultado es

{ 
"results" : [ 
    { 
     "_id" : ObjectId("4da0bdb56bd728c276911e1a"), 
     "value" : "Map" 
    }, 
    { 
     "_id" : ObjectId("4da0df9a6bd728c276911e1b"), 
     "value" : "Map" 
    } 
], 
"timeMillis" : 0, 
"counts" : { 
    "input" : 2, 
    "emit" : 2, 
    "output" : 2 
}, 
"ok" : 1, 

}

¿Cuál es incorrecto?

estoy usando MongoDB 1.8.1 32 bits en Ubuntu 10.10

Respuesta

17

El propósito de reduce es, ekhem, reducir el conjunto de valores asociados con una tecla determinada en un valor de uno (resultados agregados) . Si solo emite un valor por cada tecla MapReduce, no es necesario reducir, todo el trabajo está hecho. Pero si usted emite dos pares para un determinado _id, reduzca será llamado:

emit(this._id, 'Map1'); 
emit(this._id, 'Map2'); 

esto exigirá reducir con los siguientes parámetros:

reduce(_id, ['Map1', 'Map2']) 

Lo más probable es que usted tendrá que usar _id para la llave de MapReduce cuando se filtra el conjunto de datos: emit solo cuando el registro dado cumple alguna condición. Pero de nuevo, reduce no se llamará en este caso, que se espera.

+0

Tienes razón Tomasz. Escogí este ejemplo de los documentos de MongoDB. Ayudaría a Map/Reduce noobs como yo mucho si esto se mencionara claramente. ¡Un humilde "cheque verde" para ti! – Adil

+3

Aún se puede deducir que reducir recibirá, usando su ejemplo, reducir (_id, ['Map1']) en el caso de solo 1 elemento emitido. No pasar reduce las interrupciones de mi conjunto de resultados. –

+3

Creo que no llamar a reducir en valores individuales es una opción de implementación extraña. Por lo tanto, cada vez que cambie la estructura del resultado de 'map' también tendrá que ajustar' reduce' para obtener resultados uniformes en caso de resultados de 'mapa' de valor único. – Chaquotay

6

Bueno, el MongoDB no llama a la función Reducir en una tecla, si sólo hay un valor para ella.

En mi opinión, esto es malo. Debería dejarse a mi código de reductor para decidir si omitir un valor singular o realizar alguna operación en él.

Ahora, si tengo que hacer alguna operación en valor singular, termino escribiendo la función de finalización y en la finalización, intento diferenciar qué valor ha pasado por el reductor o no.

Estoy muy seguro, no sucede así en el caso de Hadoop.

+0

¡Gracias! esto es muy contrario a la intuición, parte de los trabajos de los reductores es construir el artículo de una cierta manera, que bien podría ser diferente. –

1

Mapa reducir recogerá los valores con una clave común en un solo valor.

En este caso no hay nada que hacer, porque cada valor emitido por mapa tiene una clave diferente. No se necesita reducción.

db.sellers.mapReduce( 
    function(){ emit(this._id, 'Map') } , 
    function(k,vs){ return 'Reduce' }, 
    { out: { inline: 1}}) 

Esto no es del todo claro de la lectura de la documentación.

Si quisiera llamar a reducir, usted podría codificar un identificador de la siguiente manera:

db.sellers.mapReduce( 
    function(){ emit(1, 'Map') } , 
    function(k,vs){ return 'Reduce' }, 
    { out: { inline: 1}}) 

Ahora se reducirá todos los valores emitidos por el mapa hasta que sólo queda uno.

0

También se debe mencionar que, según el documentation, "MongoDB puede invocar la función de reducción más de una vez para la misma clave. En este caso, la salida anterior de la función reducir para esa clave se convertirá en una de las valores de entrada a la siguiente invocación de función de reducción para esa clave. ".

Además, reduce debe ser asociativa, conmutativa y idempotente:

reduce(key, [ C, reduce(key, [ A, B ]) ]) == reduce(key, [ C, A, B ]) 
reduce(key, [ reduce(key, valuesArray) ]) == reduce(key, valuesArray) 
reduce(key, [ A, B ]) == reduce(key, [ B, A ]) 

Por lo tanto, significa que la función reduce debe estar listo para recibir el objeto que es el resultado de la invocación anterior de la misma. Lo cual (al menos para mí, personalmente) significa que la mejor forma de implementar mapReduce es hacer que la función map (si es posible) emita valores en el mismo formato que devuelve la función reduce. Entonces la función reduce se puede implementar para admitir solo un formato de entrada. Y, como resultado, incluso si solo hay un objeto emitido por map (y como resultado se omite la invocación de reduce), en el resultado final de mapReduce, el valor de las claves para las que nunca se llamó reduce aún estar en el mismo formato que el valor para el resto de las teclas.

Por ejemplo, si tenemos la siguiente estructura de documento:

{ 
    "foo": <some_string>, 
    "status": ("foo"|"bar") 
} 

la función map puede ser como sigue:

function() { 
    var value = { 
     "num_total": 1, 
     "num_foos": 0, 
     "num_bars": 0 
    }; 

    if (this.status == "foo") { 
     value["num_foos"] += 1; 
    } 

    if (this.status == "bar") { 
     value["num_bars"] += 1; 
    } 

    emit(this.foo, value); 
} 

y la función reduce será:

function(key, values) { 
    var reduced = { 
     "num_total": 0, 
     "num_foos": 0, 
     "num_bars": 0 
    }; 

    values.forEach(function(val) { 
     reduced["num_total"] += val["num_total"]; 
     reduced["num_foos"] += val["num_foos"]; 
     reduced["num_bars"] += val["num_bars"]; 
    }); 

    return reduced; 
} 
Cuestiones relacionadas