Join us at MongoDB.local London on 7 May to unlock new possibilities for your data. Use WEB50 to save 50%.
Register now >
Docs Menu
Docs Home
/ /
/ / /

Ejemplos de Map-Reduce

Nota

Pipeline de agregación como alternativa a Map-Reduce

An El pipeline de agregación ofrece un mejor rendimiento y usabilidad que una operación map-reduce.

Las operaciones de map-reduce pueden reescribirse utilizando etapas del pipeline de agregación como $group y $merge.

Para operaciones de map-reduce que requieren funcionalidad personalizada, MongoDB proporciona los $accumulator y los $function operadores de agregación. Utilice estos operadores para definir expresiones de agregación personalizadas en JavaScript.

En mongosh, el método db.collection.mapReduce() es un contenedor para el comando mapReduce. Los siguientes ejemplos utilizan el método db.collection.mapReduce().

Los ejemplos de esta sección incluyen alternativas de pipelines de agregación sin expresiones de agregación personalizadas. Para alternativas que usan expresiones personalizadas, consulta Ejemplos de traducción del antiguo map-reduce al pipeline de agregación.

Crea una colección de muestra orders con estos documentos:

db.orders.insertMany([
{ _id: 1, cust_id: "Ant O. Knee", ord_date: new Date("2020-03-01"), price: 25, items: [ { sku: "oranges", qty: 5, price: 2.5 }, { sku: "apples", qty: 5, price: 2.5 } ], status: "A" },
{ _id: 2, cust_id: "Ant O. Knee", ord_date: new Date("2020-03-08"), price: 70, items: [ { sku: "oranges", qty: 8, price: 2.5 }, { sku: "chocolates", qty: 5, price: 10 } ], status: "A" },
{ _id: 3, cust_id: "Busby Bee", ord_date: new Date("2020-03-08"), price: 50, items: [ { sku: "oranges", qty: 10, price: 2.5 }, { sku: "pears", qty: 10, price: 2.5 } ], status: "A" },
{ _id: 4, cust_id: "Busby Bee", ord_date: new Date("2020-03-18"), price: 25, items: [ { sku: "oranges", qty: 10, price: 2.5 } ], status: "A" },
{ _id: 5, cust_id: "Busby Bee", ord_date: new Date("2020-03-19"), price: 50, items: [ { sku: "chocolates", qty: 5, price: 10 } ], status: "A"},
{ _id: 6, cust_id: "Cam Elot", ord_date: new Date("2020-03-19"), price: 35, items: [ { sku: "carrots", qty: 10, price: 1.0 }, { sku: "apples", qty: 10, price: 2.5 } ], status: "A" },
{ _id: 7, cust_id: "Cam Elot", ord_date: new Date("2020-03-20"), price: 25, items: [ { sku: "oranges", qty: 10, price: 2.5 } ], status: "A" },
{ _id: 8, cust_id: "Don Quis", ord_date: new Date("2020-03-20"), price: 75, items: [ { sku: "chocolates", qty: 5, price: 10 }, { sku: "apples", qty: 10, price: 2.5 } ], status: "A" },
{ _id: 9, cust_id: "Don Quis", ord_date: new Date("2020-03-20"), price: 55, items: [ { sku: "carrots", qty: 5, price: 1.0 }, { sku: "apples", qty: 10, price: 2.5 }, { sku: "oranges", qty: 10, price: 2.5 } ], status: "A" },
{ _id: 10, cust_id: "Don Quis", ord_date: new Date("2020-03-23"), price: 25, items: [ { sku: "oranges", qty: 10, price: 2.5 } ], status: "A" }
])

Realice la operación map-reduce en la colección orders para agrupar por cust_id y calcular la suma de price para cada cust_id:

  1. Defina la función de mapeo para procesar cada documento de entrada:

    • En la función, this se refiere al documento que la operación de map-reduce está procesando.

    • La función asigna el price al cust_id para cada documento y emite el cust_id y el price.

    var mapFunction1 = function() {
    emit(this.cust_id, this.price);
    };
  2. Define la función de reducción correspondiente con dos argumentos keyCustId y valuesPrices:

    • El valuesPrices es un arreglo cuyos elementos son los valores price emitidos por la función map y agrupados por keyCustId.

    • La función reduce el arreglo valuesPrice a la suma de sus elementos.

    var reduceFunction1 = function(keyCustId, valuesPrices) {
    return Array.sum(valuesPrices);
    };
  3. Realiza map-reduce en todos los documentos de la colección orders utilizando la función de mapeo mapFunction1 y la función de reducción reduceFunction1:

    db.orders.mapReduce(
    mapFunction1,
    reduceFunction1,
    { out: "map_reduce_example" }
    )

    Esta operación envía los resultados a una colección llamada map_reduce_example. Si la colección map_reduce_example ya existe, la operación reemplazará el contenido con los resultados de esta operación de map-reduce.

  4. Query la colección map_reduce_example para verificar los resultados:

    db.map_reduce_example.find().sort( { _id: 1 } )

    La operación devuelve estos documentos:

    { "_id" : "Ant O. Knee", "value" : 95 }
    { "_id" : "Busby Bee", "value" : 125 }
    { "_id" : "Cam Elot", "value" : 60 }
    { "_id" : "Don Quis", "value" : 155 }

Utilizando los operadores de la pipeline de agregación disponibles, se puede reescribir la operación de map-reduce sin definir funciones personalizadas:

db.orders.aggregate([
{ $group: { _id: "$cust_id", value: { $sum: "$price" } } },
{ $out: "agg_alternative_1" }
])
  1. La etapa $group agrupa por el cust_id y calcula el campo value (Ver también $sum). El campo value contiene el total de price para cada cust_id.

    La etapa generó los siguientes documentos para la etapa siguiente:

    { "_id" : "Don Quis", "value" : 155 }
    { "_id" : "Ant O. Knee", "value" : 95 }
    { "_id" : "Cam Elot", "value" : 60 }
    { "_id" : "Busby Bee", "value" : 125 }
  2. A continuación, la $out escribe la salida en la colección agg_alternative_1. Alternativamente, podrías usar $merge en lugar de $out.

  3. Query la colección agg_alternative_1 para verificar los resultados:

    db.agg_alternative_1.find().sort( { _id: 1 } )

    La operación devuelve los siguientes documentos:

    { "_id" : "Ant O. Knee", "value" : 95 }
    { "_id" : "Busby Bee", "value" : 125 }
    { "_id" : "Cam Elot", "value" : 60 }
    { "_id" : "Don Quis", "value" : 155 }

Tip

Para una alternativa que utiliza expresiones de agregación personalizadas, consulte Ejemplos de traducción de map-reduce a la pipeline de agregación.

En el siguiente ejemplo, se verá una operación de map-reduce en la colección orders para todos los documentos que tengan un valor de ord_date mayor o igual a 2020-03-01.

La operación en el ejemplo:

  1. Agrupa por el campo item.sku y calcula el número de órdenes y la cantidad total ordenada para cada sku.

  2. Calcula la cantidad promedio por pedido para cada valor de sku y fusiona los resultados en la colección de salida.

Al fusionar resultados, si un documento existente tiene la misma clave que el nuevo resultado, la operación sobrescribe el documento existente. Si no existe ningún documento con la misma clave, la operación inserta el documento.

Ejemplo de pasos:

  1. Defina la función de mapeo para procesar cada documento de entrada:

    • En la función, this se refiere al documento que la operación de map-reduce está procesando.

    • Para cada elemento, la función asocia el sku con un nuevo objeto value que contiene el count de 1 y el elemento qty para la orden y emite el sku (almacenado en el key) y el value.

    var mapFunction2 = function() {
    for (var idx = 0; idx < this.items.length; idx++) {
    var key = this.items[idx].sku;
    var value = { count: 1, qty: this.items[idx].qty };
    emit(key, value);
    }
    };
  2. Define la función de reducción correspondiente con dos argumentos keySKU y countObjVals:

    • countObjVals es un arreglo cuyos elementos son los objetos asignados a los valores agrupados keySKU pasados por la función map a la función reducer.

    • La función reduce el arreglo countObjVals a un solo objeto reducedValue que contiene los campos count y qty.

    • En reducedVal, el campo count contiene la suma de los campos count de los elementos individuales del arreglo, y el campo qty contiene la suma de los campos qty de los elementos individuales del arreglo.

    var reduceFunction2 = function(keySKU, countObjVals) {
    reducedVal = { count: 0, qty: 0 };
    for (var idx = 0; idx < countObjVals.length; idx++) {
    reducedVal.count += countObjVals[idx].count;
    reducedVal.qty += countObjVals[idx].qty;
    }
    return reducedVal;
    };
  3. Define una función de finalización con dos argumentos key y reducedVal. La función modifica el objeto reducedVal para añadir un campo calculado llamado avg y devuelve el objeto modificado:

    var finalizeFunction2 = function (key, reducedVal) {
    reducedVal.avg = reducedVal.qty/reducedVal.count;
    return reducedVal;
    };
  4. Realiza la operación map-reduce en la colección orders utilizando las funciones mapFunction2, reduceFunction2 y finalizeFunction2:

    db.orders.mapReduce(
    mapFunction2,
    reduceFunction2,
    {
    out: { merge: "map_reduce_example2" },
    query: { ord_date: { $gte: new Date("2020-03-01") } },
    finalize: finalizeFunction2
    }
    );

    Esta operación utiliza el campo query para seleccionar solo aquellos documentos con ord_date mayores o iguales a new Date("2020-03-01"). Luego, da salida a los resultados en una colección map_reduce_example2.

    Si la colección map_reduce_example2 ya existe, la operación fusionará el contenido existente con los resultados de esta operación de map-reduce. Es decir, si un documento existente tiene la misma clave que el nuevo resultado, la operación sobrescribirá el documento existente. Si no existe un documento con la misma clave, la operación inserta el documento.

  5. Query la colección map_reduce_example2 para verificar los resultados:

    db.map_reduce_example2.find().sort( { _id: 1 } )

    La operación devuelve estos documentos:

    { "_id" : "apples", "value" : { "count" : 4, "qty" : 35, "avg" : 8.75 } }
    { "_id" : "carrots", "value" : { "count" : 2, "qty" : 15, "avg" : 7.5 } }
    { "_id" : "chocolates", "value" : { "count" : 3, "qty" : 15, "avg" : 5 } }
    { "_id" : "oranges", "value" : { "count" : 7, "qty" : 63, "avg" : 9 } }
    { "_id" : "pears", "value" : { "count" : 1, "qty" : 10, "avg" : 10 } }

Utilizando los operadores de la pipeline de agregación disponibles, se puede reescribir la operación de map-reduce sin definir funciones personalizadas:

db.orders.aggregate( [
{ $match: { ord_date: { $gte: new Date("2020-03-01") } } },
{ $unwind: "$items" },
{ $group: { _id: "$items.sku", qty: { $sum: "$items.qty" }, orders_ids: { $addToSet: "$_id" } } },
{ $project: { value: { count: { $size: "$orders_ids" }, qty: "$qty", avg: { $divide: [ "$qty", { $size: "$orders_ids" } ] } } } },
{ $merge: { into: "agg_alternative_3", on: "_id", whenMatched: "replace", whenNotMatched: "insert" } }
] )
  1. La etapa $match selecciona únicamente aquellos documentos con ord_date mayor o igual a new Date("2020-03-01").

  2. La etapa $unwind desglosa el documento por el campo de arreglo items para producir un documento por cada elemento del mismo. Por ejemplo:

    { "_id" : 1, "cust_id" : "Ant O. Knee", "ord_date" : ISODate("2020-03-01T00:00:00Z"), "price" : 25, "items" : { "sku" : "oranges", "qty" : 5, "price" : 2.5 }, "status" : "A" }
    { "_id" : 1, "cust_id" : "Ant O. Knee", "ord_date" : ISODate("2020-03-01T00:00:00Z"), "price" : 25, "items" : { "sku" : "apples", "qty" : 5, "price" : 2.5 }, "status" : "A" }
    { "_id" : 2, "cust_id" : "Ant O. Knee", "ord_date" : ISODate("2020-03-08T00:00:00Z"), "price" : 70, "items" : { "sku" : "oranges", "qty" : 8, "price" : 2.5 }, "status" : "A" }
    { "_id" : 2, "cust_id" : "Ant O. Knee", "ord_date" : ISODate("2020-03-08T00:00:00Z"), "price" : 70, "items" : { "sku" : "chocolates", "qty" : 5, "price" : 10 }, "status" : "A" }
    { "_id" : 3, "cust_id" : "Busby Bee", "ord_date" : ISODate("2020-03-08T00:00:00Z"), "price" : 50, "items" : { "sku" : "oranges", "qty" : 10, "price" : 2.5 }, "status" : "A" }
    { "_id" : 3, "cust_id" : "Busby Bee", "ord_date" : ISODate("2020-03-08T00:00:00Z"), "price" : 50, "items" : { "sku" : "pears", "qty" : 10, "price" : 2.5 }, "status" : "A" }
    { "_id" : 4, "cust_id" : "Busby Bee", "ord_date" : ISODate("2020-03-18T00:00:00Z"), "price" : 25, "items" : { "sku" : "oranges", "qty" : 10, "price" : 2.5 }, "status" : "A" }
    { "_id" : 5, "cust_id" : "Busby Bee", "ord_date" : ISODate("2020-03-19T00:00:00Z"), "price" : 50, "items" : { "sku" : "chocolates", "qty" : 5, "price" : 10 }, "status" : "A" }
    ...
  3. La etapa $group agrupa por el items.sku, calculando para cada sku:

    • El campo qty. El campo qty contiene la
      total de qty pedidos por cada items.sku (consultar $sum).
    • El arreglo orders_ids. El campo orders_ids contiene un
      arreglo de órdenes distintas _id para el items.sku (Consulte $addToSet) .
    { "_id" : "chocolates", "qty" : 15, "orders_ids" : [ 2, 5, 8 ] }
    { "_id" : "oranges", "qty" : 63, "orders_ids" : [ 4, 7, 3, 2, 9, 1, 10 ] }
    { "_id" : "carrots", "qty" : 15, "orders_ids" : [ 6, 9 ] }
    { "_id" : "apples", "qty" : 35, "orders_ids" : [ 9, 8, 1, 6 ] }
    { "_id" : "pears", "qty" : 10, "orders_ids" : [ 3 ] }
  4. La etapa $project remodela el documento de salida para que refleje la salida de map-reduce y tenga dos campos _id y value. Los $project sets:

  5. La etapa $unwind desglosa el documento por el campo de arreglo items para producir un documento por cada elemento del mismo. Por ejemplo:

    { "_id" : 1, "cust_id" : "Ant O. Knee", "ord_date" : ISODate("2020-03-01T00:00:00Z"), "price" : 25, "items" : { "sku" : "oranges", "qty" : 5, "price" : 2.5 }, "status" : "A" }
    { "_id" : 1, "cust_id" : "Ant O. Knee", "ord_date" : ISODate("2020-03-01T00:00:00Z"), "price" : 25, "items" : { "sku" : "apples", "qty" : 5, "price" : 2.5 }, "status" : "A" }
    { "_id" : 2, "cust_id" : "Ant O. Knee", "ord_date" : ISODate("2020-03-08T00:00:00Z"), "price" : 70, "items" : { "sku" : "oranges", "qty" : 8, "price" : 2.5 }, "status" : "A" }
    { "_id" : 2, "cust_id" : "Ant O. Knee", "ord_date" : ISODate("2020-03-08T00:00:00Z"), "price" : 70, "items" : { "sku" : "chocolates", "qty" : 5, "price" : 10 }, "status" : "A" }
    { "_id" : 3, "cust_id" : "Busby Bee", "ord_date" : ISODate("2020-03-08T00:00:00Z"), "price" : 50, "items" : { "sku" : "oranges", "qty" : 10, "price" : 2.5 }, "status" : "A" }
    { "_id" : 3, "cust_id" : "Busby Bee", "ord_date" : ISODate("2020-03-08T00:00:00Z"), "price" : 50, "items" : { "sku" : "pears", "qty" : 10, "price" : 2.5 }, "status" : "A" }
    { "_id" : 4, "cust_id" : "Busby Bee", "ord_date" : ISODate("2020-03-18T00:00:00Z"), "price" : 25, "items" : { "sku" : "oranges", "qty" : 10, "price" : 2.5 }, "status" : "A" }
    { "_id" : 5, "cust_id" : "Busby Bee", "ord_date" : ISODate("2020-03-19T00:00:00Z"), "price" : 50, "items" : { "sku" : "chocolates", "qty" : 5, "price" : 10 }, "status" : "A" }
    ...
  6. La etapa $group agrupa por el items.sku, calculando para cada sku:

    • El campo qty. El campo qty contiene el total de qty ordenado por cada items.sku usando $sum.

    • El arreglo orders_ids. El campo orders_ids contiene un arreglo de diferentes órdenes _id para el items.sku usando $addToSet.

    { "_id" : "chocolates", "qty" : 15, "orders_ids" : [ 2, 5, 8 ] }
    { "_id" : "oranges", "qty" : 63, "orders_ids" : [ 4, 7, 3, 2, 9, 1, 10 ] }
    { "_id" : "carrots", "qty" : 15, "orders_ids" : [ 6, 9 ] }
    { "_id" : "apples", "qty" : 35, "orders_ids" : [ 9, 8, 1, 6 ] }
    { "_id" : "pears", "qty" : 10, "orders_ids" : [ 3 ] }
  7. La etapa $project remodela el documento de salida para que refleje la salida de map-reduce y tenga dos campos _id y value. Los $project sets:

    • el value.count al tamaño del arreglo orders_ids utilizando $size.

    • value.qty al campo qty del documento de entrada.

    • el value.avg al número promedio de unidades por pedido utilizando $divide y $size.

    { "_id" : "apples", "value" : { "count" : 4, "qty" : 35, "avg" : 8.75 } }
    { "_id" : "pears", "value" : { "count" : 1, "qty" : 10, "avg" : 10 } }
    { "_id" : "chocolates", "value" : { "count" : 3, "qty" : 15, "avg" : 5 } }
    { "_id" : "oranges", "value" : { "count" : 7, "qty" : 63, "avg" : 9 } }
    { "_id" : "carrots", "value" : { "count" : 2, "qty" : 15, "avg" : 7.5 } }
  8. Finalmente, el $merge escribe la salida en la colección agg_alternative_3. Si un documento existente tiene la misma clave _id que el nuevo resultado, la operación sobrescribe el documento existente. Si no existe ningún documento con la misma clave, la operación inserta el documento.

  9. Query la colección agg_alternative_3 para verificar los resultados:

    db.agg_alternative_3.find().sort( { _id: 1 } )

    La operación devuelve los siguientes documentos:

    { "_id" : "apples", "value" : { "count" : 4, "qty" : 35, "avg" : 8.75 } }
    { "_id" : "carrots", "value" : { "count" : 2, "qty" : 15, "avg" : 7.5 } }
    { "_id" : "chocolates", "value" : { "count" : 3, "qty" : 15, "avg" : 5 } }
    { "_id" : "oranges", "value" : { "count" : 7, "qty" : 63, "avg" : 9 } }
    { "_id" : "pears", "value" : { "count" : 1, "qty" : 10, "avg" : 10 } }

Tip

Para una alternativa que utiliza expresiones de agregación personalizadas, consulte Ejemplos de traducción de map-reduce a la pipeline de agregación.

Volver

Simultaneidad