sql >> Database teknologi >  >> NoSQL >> MongoDB

At finde to elementer i en række dokumenter, der vises i en given rækkefølge

Hvis du ønsker den slags begrænsninger i forespørgslen, har du grundlæggende to muligheder afhængigt af, hvad din MongoDB-version understøtter:

MongoDB 3.6

Du vil helst bruge $expr "udover" til alle normale forespørgselsbetingelser for faktisk at vælge gyldige dokumenter:

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

Eller matche det "sidste" forekomst:

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

Tidligere versioner

Det samme, men uden indbyggede operatører skal du bruge JavaScript-evalueringen af ​​>$hvor :

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

Eller matche det "sidste" forekomst:

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

Hvis du havde brug for det i en aggregeringspipeline, ville du bruge $redact og lignende logik til det første eksempel i stedet:

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"
    }
  }}
])

Det er tilstrækkeligt at sige, at "sammenligningslogikken" faktisk ikke er hjemmehørende i "forespørgselsoperatorudtryk" selv, så den eneste del, der "optimalt" kan anvendes på et indeks, bruger $all forespørgselsoperatør i alle tilfælde. Den væsentlige resterende logik gælder faktisk "efter" at hovedudtrykket er evalueret og "i tillæg til" for at der ikke returneres andre resultater end dem, der møder udtrykket med enten $expr eller $where .

Den grundlæggende logik for hver enkelt er i det væsentlige at udtrække værdien af ​​"indeks" egenskab fra det "første" array-medlem, der faktisk matcher den respektive værdi i "værdi" ejendom. Hvor dette er "mindre end", så er betingelsen sand og dette opfylder det dokument, der returneres.

Så bemærk, at enten "beregnet evaluering" matcher effektiviteten af ​​forespørgselsoperatorer, og uden at blive brugt "i kombination" af andre forespørgselsoperatorbetingelser, som er i stand til at få adgang til et "indeks", vil en "fuld samlingsscanning" blive startet.

Men det overordnede resultat er bestemt mere effektivt end at returnere alle matchende elementer til den første forespørgselsbetingelse og derefter afvise dem på markøren "efter" returnering fra databasen.

Se også dokumentation for $arrayElemAt , $indexOfArray , $lt og Array.find() til JavaScript




  1. mongoose forespørgsel samme felt med forskellige værdier

  2. MongoDb-forbindelse afvist

  3. Mongorestore til en anden database

  4. MongoDB:Unik nøgle i indlejret dokument