MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Existe uma solução alternativa para permitir o uso de um regex no pipeline de agregação do Mongodb


Esta questão parece vir muitas vezes sem solução. Existem duas soluções possíveis que eu conheço:solução 1- usando mapReduce. mapReduce é a forma geral de agregação que permite ao usuário fazer qualquer coisa imaginável e programável.

a seguir está a solução do shell mongo usando mapReduceConsideramos a seguinte coleção 'st'.
{ "_id" : ObjectId("51d6d23b945770d6de5883f1"), "foo" : "foo1", "bar" : "bar1" }
{ "_id" : ObjectId("51d6d249945770d6de5883f2"), "foo" : "foo2", "bar" : "bar2" }
{ "_id" : ObjectId("51d6d25d945770d6de5883f3"), "foo" : "foo2", "bar" : "bar22" }
{ "_id" : ObjectId("51d6d28b945770d6de5883f4"), "foo" : "foo2", "bar" : "bar3" }
{ "_id" : ObjectId("51d6daf6945770d6de5883f5"), "foo" : "foo3", "bar" : "bar3" }
{ "_id" : ObjectId("51d6db03945770d6de5883f6"), "foo" : "foo4", "bar" : "bar24" }

queremos agrupar por foo, e para cada foo, contar o número de doc, bem como o número de doc com bar contendo a substring 'bar2'.isto é:
foo1: nbdoc=1, n_match = 0
foo2: nbdoc=3, n_match = 2
foo3: nbdoc=1, n_match = 0
foo4: nbdoc=1, n_match = 1

Para fazer isso, defina a seguinte função de mapa
var mapFunction = function() {
  var key = this.foo;
  var nb_match_bar2 = 0;
  if( this.bar.match(/bar2/g) ){
    nb_match_bar2 = 1;
  }
  var value = {
    count: 1,
    nb_match: nb_match_bar2
  };

  emit( key, value );
};

e a seguinte função de redução
var reduceFunction = function(key, values) {

  var reducedObject = {
    count: 0,
    nb_match:0
  };
  values.forEach( function(value) {
    reducedObject.count += value.count;
    reducedObject.nb_match += value.nb_match;
  }
  );
  return reducedObject;
};

execute mapduce e armazene o resultado na coleção map_reduce_result
db.st.mapReduce(mapFunction, reduceFunction, {out:'map_reduce_result'})
{
  "result" : "map_reduce_result",
  "timeMillis" : 7,
  "counts" : {
    "input" : 6,
    "emit" : 6,
    "reduce" : 1,
    "output" : 4
},
"ok" : 1,
}

Finalmente, podemos consultar a coleção map_reduce_result, voila! a solução
> db.map_reduce_result.find()
{ "_id" : "foo1", "value" : { "count" : 1, "nb_match" : 0 } }
{ "_id" : "foo2", "value" : { "count" : 3, "nb_match" : 2 } }
{ "_id" : "foo3", "value" : { "count" : 1, "nb_match" : 0 } }
{ "_id" : "foo4", "value" : { "count" : 1, "nb_match" : 1 } }

solução 2- usando duas agregações separadas e mergeNão darei detalhes para esta solução, pois qualquer usuário do mongo pode fazê-lo facilmente.passo 1:faça a agregação, ignorando a parte que requer regex para somar.passo 2:faça um segundo agrupamento de agregação na mesma chave da etapa um. etapa 1 do pipeline:corresponda à expressão regular; etapa 2:agrupe na mesma chave da primeira etapa e conte o número de documentos em cada grupo {$sum:1};passo 3:mescla o resultado do passo 1 e 2:para cada chave que aparece em ambos os resultados adiciona o novo campo, caso a chave não esteja presente no segundo resultado defina a nova chave para 0.

Voilá! outra solução.