Menu Docs
Página inicial do Docs
/
Manual do MongoDB
/ /

Exemplos de map-reduce

Observação

Pipeline de Agregação como Alternativa à Redução de Mapa

Um pipeline de agregação fornece melhor desempenho e usabilidade do que uma operaçãode redução de mapa.

As operações de redução de mapa podem ser reescritas usando pipeline de agregação, como $group e $merge.

Para operações de map-reduce que exigem funcionalidade personalizada, o MongoDB fornece os operadores de agregação $accumulator e $function. Use esses operadores para definir expressões de agregação personalizadas no JavaScript.

No mongosh, o método db.collection.mapReduce() é um encapsulador do comando mapReduce. Os exemplos a seguir utilizam o método db.collection.mapReduce().

Os exemplos nesta seção incluem alternativas de aggregation pipelines sem expressões de agregação customizadas. Para alternativas que usam expressões personalizadas, consulte Exemplos de tradução do map-reduce para o aggregation pipeline.

Crie uma coleção de amostra orders com estes 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" }
])

Execute a operação map-reduce na collection orders para agrupar pelo cust_id e calcule a soma do price para cada cust_id:

  1. Defina a função do mapa para processar cada documento de entrada:

    • Na função, o this refere-se ao documento que a operação de redução de mapa está processando.

    • A função mapeia o price para o cust_id para cada documento e emite o cust_id e price.

    var mapFunction1 = function() {
    emit(this.cust_id, this.price);
    };
  2. Defina a função de redução correspondente com dois argumentos keyCustId e valuesPrices:

    • O valuesPrices é uma array cujos elementos são os valores price emitidos pela função de mapa e agrupados por keyCustId.

    • A função reduz a array valuesPrice à soma de seus elementos.

    var reduceFunction1 = function(keyCustId, valuesPrices) {
    return Array.sum(valuesPrices);
    };
  3. Executar a redução de mapa em todos os documentos na coleção do orders utilizando a função de mapa do mapFunction1 e a função de redução do reduceFunction1 :

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

    Esta operação gera resultados para uma coleção denominada map_reduce_example. Se a coleção map_reduce_example já existir, a operação substituirá o conteúdo pelos resultados desta operação de redução de mapa.

  4. Consulte a coleção map_reduce_example para verificar os resultados:

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

    A operação retorna estes documentos:

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

Usando os operadores de aggregation pipeline disponíveis, você pode reescrever a operação de map-reduce sem definir funções personalizadas:

db.orders.aggregate([
{ $group: { _id: "$cust_id", value: { $sum: "$price" } } },
{ $out: "agg_alternative_1" }
])
  1. O estágio $group agrupa pelo cust_id e calcula o campo value (consulte também $sum). O campo value contém o total de price para cada cust_id.

    O estágio envia os seguintes documentos para o próximo estágio:

    { "_id" : "Don Quis", "value" : 155 }
    { "_id" : "Ant O. Knee", "value" : 95 }
    { "_id" : "Cam Elot", "value" : 60 }
    { "_id" : "Busby Bee", "value" : 125 }
  2. Em seguida, o $out grava a saída na coleção agg_alternative_1. Alternativamente, você pode utilizar $merge ao invés de $out.

  3. Consulte a coleção agg_alternative_1 para verificar os resultados:

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

    A operação retorna os seguintes documentos:

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

Dica

Veja também:

Para obter uma alternativa que usa expressões de agregação personalizadas, consulte Map-Reduce to Aggregation Pipeline Translation Examples.

No exemplo seguinte, você visualizará uma operação de map-reduce na collection orders para todos os documentos que têm um valor ord_date maior ou igual a 2020-03-01.

A operação no exemplo:

  1. Agrupa pelo campo item.sku e calcula o número de pedidos e a quantidade total solicitada para cada sku.

  2. Calcula a quantidade média por pedido para cada valor sku e mescla os resultados na coleta de saída.

Ao mesclar resultados, se um documento existente tiver a mesma chave que o novo resultado, a operação substituirá o documento existente. Se não houver nenhum documento existente com a mesma chave, a operação inserirá o documento.

Etapas de exemplo:

  1. Defina a função do mapa para processar cada documento de entrada:

    • Na função, o this refere-se ao documento que a operação de redução de mapa está processando.

    • Para cada item, a função associa o sku a um novo objeto value que contém o count de 1 e o item qty para o pedido e emite o sku (armazenado no key) e o 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. Defina a função de redução correspondente com dois argumentos keySKU e countObjVals:

    • countObjVals é uma matriz cujos elementos são os objetos mapeados para os valores agrupados do keySKU passados pela função de mapa para a função redutor.

    • A função reduz a matriz countObjVals para um único objeto reducedValue que contém os campos count e qty.

    • Em reducedVal, o campo count contém a soma dos campos count dos elementos individuais da array e o campo qty contém a soma dos campos qty dos elementos individuais da array.

    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. Defina uma função de finalização com dois argumentos key e reducedVal. A função modifica o objeto reducedVal para adicionar um campo calculado denominado avg e retorna o objeto modificado:

    var finalizeFunction2 = function (key, reducedVal) {
    reducedVal.avg = reducedVal.qty/reducedVal.count;
    return reducedVal;
    };
  4. Execute a operação de redução de mapa na coleção do orders utilizando as funções mapFunction2, reduceFunction2 e finalizeFunction2:

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

    Esta operação utiliza o campo query para selecionar apenas os documentos com ord_date maior ou igual a new Date("2020-03-01"). Em seguida, ele envia os resultados para uma coleção map_reduce_example2.

    Se a coleção map_reduce_example2 já existir, a operação fundirá o conteúdo existente com os resultados desta operação de redução de mapa. Ou seja, se um documento existente tiver a mesma chave que o novo resultado, a operação substituirá o documento existente. Se não houver nenhum documento existente com a mesma chave, a operação inserirá o documento.

  5. Consulte a coleção map_reduce_example2 para verificar os resultados:

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

    A operação retorna estes 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 } }

Usando os operadores de aggregation pipeline disponíveis, você pode reescrever a operação de map-reduce sem definir funções 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. A etapa $match seleciona apenas os documentos com ord_date maior ou igual a new Date("2020-03-01").

  2. O estágio $unwind divide o documento pelo campo de array items para gerar um documento para cada elemento da array. Por exemplo:

    { "_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. Os $group grupos de estágio pelo items.sku, calculando para cada sku:

    • O campo qty. O campo qty contém o
      total qty solicitado por cada items.sku (consulte $sum).
    • A matriz orders_ids. O campo orders_ids contém um
      array de _id de ordem distinta para o items.sku (veja $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. O estágio $project remodela o documento de saída para espelhar a saída do map-reduce para ter dois campos _id e value. Os conjuntos $project :

  5. O estágio $unwind divide o documento pelo campo de array items para gerar um documento para cada elemento da array. Por exemplo:

    { "_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. Os $group grupos de estágio pelo items.sku, calculando para cada sku:

    • O campo qty. O campo qty contém o total de qty ordenados por cada items.sku utilizando $sum.

    • A array orders_ids. O campo orders_ids contém uma array de ordem distinta _id para o items.sku utilizando $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. O estágio $project remodela o documento de saída para espelhar a saída do map-reduce para ter dois campos _id e value. Os conjuntos $project :

    • o value.count para o tamanho da array orders_ids usando $size.

    • o value.qty para o campo qty do documento de entrada.

    • o value.avg para o número médio de quantidade por pedido usando $divide e $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, o $merge grava a saída na coleção agg_alternative_3. Se um documento existente tiver a mesma chave _id que o novo resultado, a operação substituirá o documento existente. Se não houver nenhum documento existente com a mesma chave, a operação inserirá o documento.

  9. Consulte a coleção agg_alternative_3 para verificar os resultados:

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

    A operação retorna os seguintes 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 } }

Dica

Veja também:

Para obter uma alternativa que usa expressões de agregação personalizadas, consulte Map-Reduce to Aggregation Pipeline Translation Examples.

Voltar

Concurrency