sql >> Base de Datos >  >> NoSQL >> MongoDB

¿Existe una solución para permitir el uso de una expresión regular en la canalización de agregación de Mongodb?

Esta pregunta parece surgir muchas veces sin solución. Hay dos soluciones posibles que conozco:solución 1:usar mapReduce. mapReduce es la forma general de agregación que permite al usuario hacer cualquier cosa imaginable y programable.

la siguiente es la solución de mongo shell usando mapReduce. Consideramos la siguiente colección '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, y para cada foo, contar el número de doc, así como el número de doc con bar que contiene la subcadena 'bar2'. Es decir:

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 hacer eso, defina la siguiente función 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 );
};

y la siguiente función de reducción

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;
};

ejecuta mapduce y almacena el resultado en la colección 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 la colección map_reduce_result, ¡voilá! la solución

> 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 } }

solución 2:usar dos agregaciones separadas y fusionar No daré detalles de esta solución, ya que cualquier usuario de mongo puede hacerlo fácilmente. Paso 1:haga la agregación, ignorando la parte que requiere expresiones regulares para sumar. Paso 2:haga una segunda agrupación de agregación en la misma clave que la del primer paso. etapa 1 de la canalización:hacer coincidir la expresión regular; etapa 2:agrupar en la misma clave que en el primer paso y contar el número de documentos en cada grupo {$sum:1};paso 3:combine el resultado de los pasos 1 y 2:para cada clave que aparece en ambos resultados, agregue el nuevo campo, si la clave no está presente en el segundo resultado, establezca la nueva clave en 0.

¡Voila! otra solución.