MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Gibt es eine Problemumgehung, um die Verwendung eines regulären Ausdrucks in der Mongodb-Aggregationspipeline zu ermöglichen?

Diese Frage scheint oft ohne Lösung zu kommen. Es gibt zwei mögliche Lösungen, die ich kenne:Lösung 1 – mit mapReduce. mapReduce ist die allgemeine Form der Aggregation, mit der Benutzer alles erdenkliche und programmierbare tun können.

Folgendes ist die Mongo-Shell-Lösung, die mapReduce verwendet. Wir betrachten die folgende 'st'-Sammlung.

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

Wir möchten nach foo gruppieren und für jedes foo die Anzahl der Dokumente sowie die Anzahl der Dokumente mit Balken zählen, die den Teilstring „bar2“ enthalten. Das heißt:

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

Definieren Sie dazu die folgende Kartenfunktion

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

und die folgende Reduce-Funktion

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

Führen Sie mapduce aus und speichern Sie das Ergebnis in der Sammlung 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,
}

Schließlich können wir die Sammlung map_reduce_result abfragen, voila! die Lösung

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

Lösung 2 - Verwenden von zwei separaten Aggregationen und Zusammenführen Ich werde keine Details für diese Lösung angeben, da jeder Mongo-Benutzer dies problemlos tun kann. Schritt 1:Führen Sie die Aggregation durch und ignorieren Sie den Teil, für den Regex summiert werden muss. Schritt 2:Führen Sie eine zweite Aggregationsgruppierung durch auf demselben Schlüssel wie in Schritt eins. Stufe 1 der Pipeline:Übereinstimmung mit dem regulären Ausdruck; Stufe 2:Gruppe auf demselben Schlüssel wie im ersten Schritt und Zählen der Anzahl von Dokumenten in jeder Gruppe {$sum:1};Schritt 3:Führen Sie das Ergebnis von Schritt 1 und 2 zusammen:Fügen Sie für jeden Schlüssel, der in beiden Ergebnissen erscheint, das neue Feld hinzu, wenn der Schlüssel nicht im zweiten Ergebnis vorhanden ist, setzen Sie den neuen Schlüssel auf 0.

Voila! andere Lösung.