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

Mongodb-Abfrage basierend auf der Anzahl der Felder in einem Datensatz

Es ist immer noch keine schöne Abfrage, aber es gibt einen etwas moderneren Weg, dies über $objectToArray zu tun und $redact

db.collection.aggregate([
  { "$redact": {
    "$cond": {
      "if": {
        "$eq": [
          { "$size": { "$objectToArray": "$value" } },
          3
        ]
      },
      "then": "$$KEEP",
      "else": "$$PRUNE"
    }
  }}
])

Wobei $objectToArray zwingt das Objekt im Grunde in eine Array-Form, ähnlich wie eine Kombination aus Object.keys() und .map() würde in JavaScript.

Es ist immer noch keine fantastische Idee, da es das Scannen der gesamten Sammlung erfordert, aber zumindest die Aggregations-Framework-Operationen verwenden "nativen Code" im Gegensatz zur JavaScript-Interpretation, wie dies bei $where der Fall ist .

Daher ist es im Allgemeinen immer noch ratsam, die Datenstruktur zu ändern und ein natürliches Array sowie gespeicherte "Größen"-Eigenschaften zu verwenden, wo immer dies möglich ist, um die effektivsten Abfrageoperationen durchzuführen.

Ja, das ist möglich, aber nicht auf die schönste Art und Weise. Der Grund dafür ist, dass Sie im Wesentlichen einen <-Code verwenden>$wo Operator-Abfrage, die eine JavaScript-Auswertung verwendet, um den Inhalt abzugleichen. Nicht der effizienteste Weg, da dies niemals einen Index verwenden kann und alle Dokumente testen muss:

db.collection.find({ "$where": "return Object.keys(this.value).length == 3" })

Dadurch wird nach der Bedingung gesucht, die mit „drei“ Elementen übereinstimmt, dann würden nur zwei Ihrer aufgelisteten Dokumente zurückgegeben:

{ "_id" : "number1", "value" : { "a" : 1, "b" : 2, "f" : 5 } }
{ "_id" : "number2", "value" : { "e" : 2, "f" : 114, "h" : 12 } }

Oder für "fünf" Felder oder mehr können Sie ähnlich vorgehen:

db.numbers.find({ "$where": "return Object.keys(this.value).length >= 5" })

Die Argumente für diesen Operator sind also effektiv JavaScript-Anweisungen, die auf dem Server ausgewertet werden, um zurückzugeben, wo true ist .

Eine effizientere Möglichkeit besteht darin, die "Anzahl" der Elemente im Dokument selbst zu speichern. Auf diese Weise können Sie dieses Feld "indizieren" und die Abfragen sind viel effizienter, da nicht jedes Dokument in der Sammlung, die durch andere Bedingungen ausgewählt wurde, gescannt werden muss, um die Länge zu bestimmen:

{_id:'number1', value:{'a':1, 'b':2, 'f':5} count: 3},
{_id:'number2', value:{'e':2, 'f':114, 'h':12}, count: 3},
{_id:'number3', value:{'i':2, 'j':22, 'z':12, 'za':111, 'zb':114}, count: 5}

Um dann die Dokumente mit "fünf" Elementen zu erhalten, benötigen Sie nur die einfache Abfrage:

db.collection.find({ "count": 5 })

Das ist im Allgemeinen die optimalste Form. Aber ein weiterer Punkt ist, dass die allgemeine „Objekt“-Struktur, mit der Sie vielleicht aus der allgemeinen Praxis zufrieden sind, etwas ist, mit dem MongoDB im Allgemeinen nicht „gut spielt“. Das Problem ist das „Durchlaufen“ von Elementen im Objekt, und auf diese Weise ist MongoDB viel zufriedener, wenn Sie ein „Array“ verwenden. Und sogar in dieser Form:

{
    '_id': 'number1', 
    'values':[
        { 'key': 'a', 'value': 1 },
        { 'key': 'b', 'value': 2 }, 
        { 'key': 'f', 'value': 5 }
    ],
},
{
    '_id': 'number2', 
    'values':[
        { 'key': 'e', 'value': 2 }, 
        { 'key': 'f', 'value': 114 }, 
        { 'key': 'h', 'value': 12 }
    ],
},
{
    '_id':'number3', 
    'values': [
        { 'key': 'i', 'values': 2 }, 
        { 'key': 'j', 'values': 22 }, 
        { 'key': 'z'' 'values': :12 }, 
        { 'key': 'za', 'values': 111 },
        { 'key': 'zb', 'values': 114 }
    ]
}

Wenn Sie also tatsächlich zu einem solchen "Array"-Format wechseln, können Sie exakt machen Länge eines Arrays mit einer Version des $ Größe Betreiber:

db.collection.find({ "values": { "$size": 5 } })

Dieser Operator kann exakt arbeiten Wert für eine Array-Länge, da dies eine Grundvoraussetzung dafür ist, was mit diesem Operator getan werden kann. Was Sie nicht können, wird in einem "In-Equality"-Match dokumentiert. Dafür benötigen Sie das "Aggregation Framework" für MongoDB, das eine bessere Alternative zu JavaScript- und mapReduce-Operationen darstellt:

db.collection.aggregate([
    // Project a size of the array
    { "$project": {
        "values": 1,
        "size": { "$size": "$values" }
    }},
    // Match on that size
    { "$match": { "size": { "$gte": 5 } } },
    // Project just the same fields 
    {{ "$project": {
        "values": 1
    }}
])

Das sind also die Stellvertreter. Es gibt eine "native" Methode für die Aggregation und einen Array-Typ. Aber es ist ziemlich fraglich, ob die JavaScript-Evaluierung auch „nativ“ für MongoDB ist, nur deshalb nicht in nativem Code implementiert.