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

Finden von zwei Elementen in einem Array von Dokumenten, die in einer bestimmten Reihenfolge erscheinen

Wenn Sie diese Art von Einschränkung in der Abfrage wünschen, haben Sie grundsätzlich zwei Möglichkeiten, je nachdem, was Ihre MongoDB-Version unterstützt:

MongoDB 3.6

Sie würden vorzugsweise $expr verwenden "zusätzlich" zu allen normalen Abfragebedingungen, um tatsächlich gültige Dokumente auszuwählen:

var A = 10, B = 40;

Model.find({
  "subDocs.value": { "$all": [A, B] },
  "$expr": {
    "$lt": [
      { "$arrayElemAt": [
        "$subDocs.index",
        { "$indexOfArray": [ "$subDocs.value", A ]}
      ]},
      { "$arrayElemAt": [
        "$subDocs.index",
        { "$indexOfArray": [ "$subDocs.value", B ]}  
      ]}
    ]
  }
})

Oder passend zum "letzten" Auftreten:

Model.find({
  "subDocs.value": { "$all": [A, B] },
  "$expr": {
    "$lt": [
      { "$arrayElemAt": [
        "$subDocs.index",
        { "$subtract": [
          { "$subtract": [{ "$size": "$subDocs.value" }, 1 ] },
          { "$indexOfArray": [ { "$reverseArray": "$subDocs.value" }, A ] }
        ]}
      ]},
      { "$arrayElemAt": [
        "$subDocs.index",
        { "$subtract": [
          { "$subtract": [{ "$size": "$subDocs.value" }, 1 ] },
          { "$indexOfArray": [ { "$reverseArray": "$subDocs.value" }, B ] }
        ]}
      ]}
    ]
  }
})

Frühere Versionen

Dasselbe, aber ohne native Operatoren müssen Sie die JavaScript-Evaluierung von $wo :

var A = 10, B = 40;

Model.find({
  "subDocs.value": { "$all": [A, B] },
  "$where": `this.subDocs.find( e => e.value === ${A}).index
      < this.subDocs.find( e => e.value === ${B}).index`
})

Oder passend zum "letzten" Auftreten:

Model.find({
  "subDocs.value": { "$all": [10,40] },
  "$where": `let arr = this.subDocs.reverse();
      return arr.find( e => e.value === ${A}).index
        > arr.find( e => e.value === ${B}).index`
})

Wenn Sie dies in einer Aggregationspipeline benötigen, verwenden Sie $schwärzen und stattdessen eine ähnliche Logik wie im ersten Beispiel:

var A = 10, B = 40;

Model.aggregate([
  { "$match": { "subDocs.value": { "$all": [A, B] } } },
  { "$redact": {
    "$cond": {
      "if": {
        "$lt": [
          { "$arrayElemAt": [
            "$subDocs.index",
            { "$indexOfArray": [ "$subDocs.value", A ]}
          ]},
          { "$arrayElemAt": [
            "$subDocs.index",
            { "$indexOfArray": [ "$subDocs.value", B ]}  
          ]}
        ]
      },
      "then": "$$KEEP",
      "else": "$$PRUNE"
    }
  }}
])

Es genügt zu sagen, dass die "Vergleichslogik" eigentlich nicht in "Abfrageoperatorausdrücken" selbst enthalten ist, also der einzige Teil, der "optimal" ist kann auf einen Index angewendet werden, indem der $all Abfrageoperator in allen Fällen. Die wesentliche verbleibende Logik gilt tatsächlich "nach" der Auswertung dieses Hauptausdrucks und "zusätzlich zu", damit keine anderen Ergebnisse zurückgegeben werden als diejenigen, die den Ausdruck entweder mit $expr oder $where .

Die grundlegende Logik von jedem besteht im Wesentlichen darin, den Wert des "index" zu extrahieren -Eigenschaft aus dem "ersten" Arraymitglied, das tatsächlich mit dem jeweiligen Wert in "value" übereinstimmt Eigentum. Wo dies "kleiner als" ist, dann ist die Bedingung true und dies erfüllt das zurückgegebene Dokument.

Beachten Sie also, dass entweder "berechnete Auswertung" der Effizienz von Abfrageoperatoren entspricht und ohne "in Kombination" mit anderen Abfrageoperatorbedingungen, die auf einen "Index" zugreifen können, ein "vollständiger Sammlungsscan" eingeleitet wird.

Aber das Gesamtergebnis ist sicherlich effizienter, als alle übereinstimmenden Elemente an die erste Abfragebedingung zurückzugeben und sie dann am Cursor "nach" der Rückkehr aus der Datenbank zurückzuweisen.

Siehe auch Dokumentation für $arrayElemAt , $indexOfArray , $lt und Array.find() für JavaScript