Olá pessoal, tudo bem?

Continuando o post anterior, onde apresentei o conceito básico do MapReduce, irei mostrar a seguir um exemplo desta técnica sendo aplicada no MongoDB.

Dependências

Para executar o exemplo a seguir, é necessário instalar o driver do MongoDB para C# através do Nuget.

Inicialização do MongoDB

Para este exemplo, irei iniciar o banco de dados utilizando os métodos SeedOrders  e SeedCustomers , apenas quando o mesmo estiver vazio.

O método SeedCustomer irá iniciar 500000 clientes na coleção Customers e, para fins de otimizar a performance, criar um índice para CustomerId. Sim, eu poderia ter usado o id do documento neste caso, e desta forma também não precisaria criar o índice em Customers, mas preferi o fazer para expor uma maior clareza do que estou fazendo.

O método SeedOrders irá inserir 5000000 (milhões!!!!) de ordens na coleção Orders. Da mesma forma como fiz em Customers, também precisarei criar um índice para CustomerId.

 Funções javascript para Map e Reduce

Como o MapReduce ocorre dentro do MongoDB, as funções devem ser escritas em Javascript. Então temos:

  • Uma função javascript para fazer o Map na coleção de clientes, onde chamo a função emit(key, value) ,  para mapear o id do customer a um objeto de resultado no formato que necessito. O this nesta função é o próprio documento do MongoDB que está sendo mapeado:

  • Uma função para o Reduce da coleção de clientes, que irá será aplicada sobre o resultado do mapeamento anterior. Esta função irá receber como parâmetro uma key, e os valores mapeados para aquela key. Como retorno, devolvo um objeto no formato necessário para fazer o merge com a coleção de ordens a seguir:

  • É necessário também uma função para mapear a coleção de ordens:

  • E uma função para reduzir o resultado do mapeamento de Ordens. É nesta função que a mágica acontece. Como argumentos, teremos como key o CustomerId, e como valores teremos o mapeamento de ordens e o resultado do Reduce de Customers! Como? Graças a uma opção do MongoDB (Reduce, a seguir). Para cada valor, verifico se o Name é válido e o atribuo ao resultado, caso contrário somo o total da ordem ao total geral:

 Opções do MapReduce

O MapReduce no MongoDB possui algumas opções que modificam o comportamento do resultado da função Reduce à ser aplicada:

  • Inline: Está função devolve o resultado do MapReduce de uma coleção diretamente na chamada do método, ou seja “inline”. Por este motivo, não permite a aplicação em duas coleções distintas;
  • Merge: A função merge irá depositar o resultado em uma coleção no MongoDB (o nome da coleção é informado nas opções). Se a coleção não existir ela será criada, caso contrário ela irá fundir o resultado com o existente na coleção, substituindo as keys que já existirem;
  • Replace: Semelhante a função merge, mas substitui a coleção inteira se ela existir;
  • Reduce: Também semelhante a função merge, mas se uma key já existir na coleção atual a mesma não será substituída, será aplicada a função reduce aos valores existentes e aos novos.

Aplicando as funções Map  e Reduce

Agora que já temos as funções javascript prontas, e conhecemos as opções do MapReduce, podemos aplicar ao nosso exemplo:

Primeiramente aplico o MapReduce a coleção de clientes, filtrando o CustomerId 5000 e utilizando a opção de saída Replace, para substituir a coleção Out se ela já existir. Em seguida, executo a função na coleção de ordens, também filtrando o CustomerId 5000 e seleciono a opção de saída Reduce, para mesclar os documentos existentes (gerados no reduce de customers) aos gerados na função Map executada.

O método GetFunction apenas lê a função javascript de um arquivo existente na solução:

O resultado, que sempre se dará neste formato { "_id": key, "value": value } , possui a key que filtramos e o valor reduzido com o nome do cliente e o total de ordens (5000,00 x 10 = 50000,00):

Output

 

Era isso pessoal, espero que tenham gostado!  Este exemplo está no Github.

Como sempre, se alguém possuir alguma dúvida, sugestão ou dica, será muito bem vindo!

Abraço!