2017-08-16 3 views
3

Ich habe einen MongoDB 3.2 Server. Meine Sammlung umfasst Dokumente wie folgt aus:Warum gibt diese RegExp-Abfrage alle Ergebnisse zurück?

{ 
    "name": "string", 
    "explicitMods": [ 
     "+48 to Blah", 
     "-13% to Blah", 
     "12 to 18 to Blah" 
    ] 
} 

Wenn ich schreibe diese:

myCollection.find ({ "explicitMods":/schlechte Saite /})

ich null Ergebnisse, wie erwartet.

Allerdings, wenn ich schreibe dies:

myCollection.find ({ "explicitMods":/\ d + auf \ d + /})

ich alle Dokumente in der Sammlung. Dies ist unerwartet, da ich tatsächlich Dokumente mit Teilstrings wie 12 to 18 möchte. Wenn ich den Regexp zu /\d+ to \d+z/ ändere, stimmt es mit nichts überein.

+0

Können Sie Ihre Frage aktualisieren, um ein Beispieldokument bereitzustellen, das von der Abfrage zurückgegeben wird, aber nicht enthalten sein soll? – JohnnyHK

+1

Ihre Abfrage scheint zu funktionieren. Versuchen Sie also, explizite Nachrichten in diesem einen Dokument zu filtern? – Mikey

+0

Vielleicht müssen Sie Ihre Regexp entweder mit '^' am Anfang oder '$' am Ende des Ausdrucks verankern, wenn Sie keine anderen Zeichen vor oder nach Ihren "12 bis 18" haben wollen - mit anderen Worten, ein Ausdruck wie '/^\ d + zu \ d + $ /'. Das könnte helfen? –

Antwort

1

Die Abfrage Sie „richtig“ sind die Ausstellung die Dokumente zurückgibt, die tatsächlich den Zustand passen Sie es zu fragen. Das bedeutet, dass mindestens ein Arrayelement in der Eigenschaft, die Sie testen, mit der Bedingung in der Abfrage übereinstimmt.

Daraus können wir zwei mögliche Ergebnisse vermuten:

  1. Ihre Absicht ist es, nur die Dokumente zurück, wo alle Array-Einträge die Bedingung erfüllen.

  2. Ihre Absicht besteht darin, die Einträge aus dem "Array innerhalb des Dokuments" zu "filtern", wobei nur die Ergebnisse zurückgegeben werden, die die Bedingung erfüllen.

Aus diesen gibt es unterschiedliche Ansätze. Erstens gibt es in der Tat keinen solchen Abfrageoperator für MongoDB, der verlangt, dass "alle" Arrayelemente durch die gegebene Bedingung mit einer "regulären Abfrage" erfüllt sein müssen. Daher müssen Sie Logik in einer anderen Form anwenden.

Eine solche Option besteht darin, die JavaScript-Auswertung $where in einer Weise zu verwenden, die den Array-Inhalt überprüft. Hier können Sie Array.every() anwenden, um Ihren Zustand zusätzlich zu dem regulären Abfragefilter zu testen, da dieser tatsächlich einige nützliche Arbeit leistet.

Da Quelldokumente wie:

/* 1 */ 
{ 
    "_id" : ObjectId("5993a35be38f41729f1d6501"), 
    "name" : "string", 
    "explicitMods" : [ 
     "+48 to Blah", 
     "-13% to Blah", 
     "12 to 18 to Blah" 
    ] 
} 

/* 2 */ 
{ 
    "_id" : ObjectId("5993a35be38f41729f1d6502"), 
    "name" : "string", 
    "explicitMods" : [ 
     "12 to 18 to Blah" 
    ] 
} 

Wo Ihre Absicht, nur ist das „Dokument“ zurückzukehren, die „alle“ Array-Elemente übereinstimmt, können Sie die Anweisung ausführen:

db.myCollection.find({ 
    "explicitMods": /\d+ to \d+/, 
    "$where": function() { return this.explicitMods.every(e => /\d+ to \d+/.test(e)) } 
    } 
}) 

die nur zurückgibt das passende Dokument:

{ 
    "_id" : ObjectId("5993a35be38f41729f1d6502"), 
    "name" : "string", 
    "explicitMods" : [ 
     "12 to 18 to Blah" 
    ] 
} 

Im alternativen Fall zur Verwendung $where ermöglicht das Aggregationsframework von MongoDB Ausdrücke, die "native codierte Operatoren" verwenden, die im Allgemeinen schneller als mit JavaScript interpretierte Ausdrücke angewendet werden. Es gibt jedoch keinen solchen "logischen Operator", der für Aggregationsoperationen wie $redact äquivalent ist (siehe SERVER-11947) von $regex.

daher der einzige Ansatz hier ist stattdessen $match zu verwenden, um mit regelmäßigen Abfragebedingungen „nach“ die Array-Elemente denormalized wurden mit $unwind:

db.myCollection.aggregate([ 
    // Match "possible" documents 
    { "$match": { "explicitMods": /\d+ to \d+/ } }, 

    // unwind to denormalize 
    { "$unwind": "$explicitMods" }, 

    // Match on the "array" items now as documents 
    { "$match": { "explicitMods": /\d+ to \d+/ } }, 

    // Optionally "re-group" back to documents with only matching array items 
    { "$group": { 
    "_id": "$_id", 
    "name": { "$first": "$name" }, 
    "explicitMods": { "$push": "$explicitMods" } 
    }} 
]) 

Und dass man zurückkehren „beide“ Dokumente, sondern nur die mit passenden Array-Elemente:

/* 1 */ 
{ 
    "_id" : ObjectId("5993a35be38f41729f1d6501"), 
    "name" : "string", 
    "explicitMods" : [ 
     "12 to 18 to Blah" 
    ] 
} 

/* 2 */ 
{ 
    "_id" : ObjectId("5993a35be38f41729f1d6502"), 
    "name" : "string", 
    "explicitMods" : [ 
     "12 to 18 to Blah" 
    ] 
} 

natürlich können Sie eine „Variante“ über dieses Thema und „testen Sie die Länge“ des Arrays gegen die Filterbedingung anwenden, um zu entscheiden, welches Dokument Rückkehr:

db.myCollection.aggregate([ 
    { "$match": { "explicitMods": /\d+ to \d+/ } }, 
    { "$addFields": { "origSize": { "$size": "$explicitMods" } } }, 
    { "$unwind": "$explicitMods" }, 
    { "$match": { "explicitMods": /\d+ to \d+/ } }, 
    { "$group": { 
    "_id": "$_id", 
    "name": { "$first": "$name" }, 
    "origSize": { "$first": "$origSize" }, 
    "explicitMods": { "$push": "$explicitMods" }, 
    }}, 
    { "$redact": { 
    "$cond": { 
     "if": { 
     "$eq": [ 
      { "$size": "$explicitMods" }, 
      "$origSize" 
     ] 
     }, 
     "then": "$$KEEP", 
     "else": "$$PRUNE" 
    } 
    }} 
]) 

Aber während es tut dasselbe wie die ursprüngliche Option mit $where „native Operatoren“, die allgemein Kosten solcher Operationen mit als $unwind es Nützlichkeit fraglich macht, und daher wahrscheinlich wesentlich mehr Zeit in Anspruch nehmen und Ressourcen, um das Ergebnis als die ursprüngliche Abfrage zu produzieren.

Verwandte Themen