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
/ /
Map-Reduce
/ / /

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 los operadores del pipeline de agregación, como $group, $merge y otros.

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.

Enmongosh, el métododb.collection.mapReduce()envuelve el comandomapReduce. Los siguientes ejemplos utilizan el métododb.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 price a cust_id para cada documento y emite cust_id y 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 la matriz 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 $group etapa cust_id agrupa por y calcula el value campo (véase también).$sum El value campo contiene el total price de para cust_id cada.

    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 lo sobrescribe. Si no existe ningún documento con la misma clave, la operación lo inserta.

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 count campos de los elementos individuales de la matriz, y el campo qty contiene la suma de los qty campos de los elementos individuales de la matriz.

    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. Realice 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 los documentos con un valor ord_date mayor o igual a new Date("2020-03-01"). Luego, envía los resultados a 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
      matriz de órdenes distintos _id para el items.sku $addToSet(ver).
    { "_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 modifica el documento de salida para reflejar la salida de map-reduce y tener dos $project campos:_id valuey. La $project etapa establece:

  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 qty campo. El qty campo contiene el total de qty pedidos por cada items.sku $sumutilizando.

    • 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 modifica el documento de salida para reflejar la salida de map-reduce y tener dos $project campos:_id valuey. La $project etapa establece:

    • 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