MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Existe-t-il une solution de contournement pour autoriser l'utilisation d'une expression régulière dans le pipeline d'agrégation Mongodb

Cette question semble venir plusieurs fois sans solution. Je connais deux solutions possibles :solution 1 - utiliser mapReduce. mapReduce est la forme générale d'agrégation qui permet à l'utilisateur de faire tout ce qui est imaginable et programmable.

Voici la solution mongo shell utilisant mapReduceNous considérons la collection 'st' suivante.

{ "_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" }

on veut regrouper par foo, et pour chaque foo, compter le nombre de doc, ainsi que le nombre de doc avec bar contenant la sous-chaîne 'bar2'.c'est-à-dire :

foo1: nbdoc=1, n_match = 0
foo2: nbdoc=3, n_match = 2
foo3: nbdoc=1, n_match = 0
foo4: nbdoc=1, n_match = 1

Pour ce faire, définissez la fonction de carte suivante

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

et la fonction de réduction suivante

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

lancez mapduce et stockez le résultat dans la collection 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,
}

Enfin, nous pouvons interroger la collection map_reduce_result, le tour est joué ! la solution

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

solution 2- utiliser deux agrégations distinctes et fusionnerJe ne donnerai pas de détails sur cette solution car tout utilisateur mongo peut facilement le faire.étape 1 :faites l'agrégation, en ignorant la partie qui nécessite regex pour sommer.étape 2 :faites un deuxième regroupement d'agrégation sur la même clé que celle de l'étape 1.étape 1 du pipeline :faire correspondre l'expression régulière ;étape 2 :grouper sur la même clé qu'à la première étape et compter le nombre de doc dans chaque groupe {$sum :1};étape 3 :fusionnez le résultat des étapes 1 et 2 :pour chaque clé qui apparaît dans les deux résultats, ajoutez le nouveau champ, si la clé n'est pas présente dans le deuxième résultat, définissez la nouvelle clé sur 0.

Voilà ! une autre solution.