sql >> Database >  >> NoSQL >> MongoDB

Twee elementen vinden in een reeks documenten die in een bepaalde volgorde verschijnen

Als je dat soort beperking in de query wilt, heb je in principe twee opties, afhankelijk van wat je MongoDB-versie ondersteunt:

MongoDB 3.6

Gebruik bij voorkeur $expr "bovendien" aan alle normale zoekvoorwaarden om daadwerkelijk geldige documenten te selecteren:

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

Of overeenkomend met de "laatste" voorval:

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

Eerdere versies

Hetzelfde, maar zonder native operators moet je de JavaScript-evaluatie van $waar :

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

Of overeenkomend met de "laatste" voorval:

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

Als je dat nodig had in een aggregatiepijplijn, dan zou je gebruiken $redacteren en vergelijkbare logica als het eerste voorbeeld:

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

Het volstaat te zeggen dat de "vergelijkingslogica" niet echt eigen is aan "query-operatorexpressies" zelf, dus het enige deel dat "optimaal" is kan worden toegepast op een index met behulp van de $all query-operator in alle gevallen. De essentiële resterende logica is feitelijk van toepassing "na" dat de hoofdexpressie is geëvalueerd en "in aanvulling op" zodat er geen resultaten worden geretourneerd, behalve die welke voldoen aan de expressie met $expr of $where .

De basislogica van elk is in wezen het extraheren van de waarde van de "index" eigenschap van het "eerste" arraylid dat daadwerkelijk overeenkomt met de respectieve waarde in de "waarde" eigendom. Waar dit "minder dan" is, dan is de voorwaarde waar en dit voldoet aan het document dat wordt geretourneerd.

Houd er dus rekening mee dat ofwel "berekende evaluatie" overeenkomt met de efficiëntie van query-operators, en zonder te worden gebruikt "in combinatie" van andere query-operatorvoorwaarden die toegang hebben tot een "index", zal een "volledige verzamelingsscan" worden gestart.

Maar het algemene resultaat is zeker efficiënter dan alle overeenkomende items terug te sturen naar de eerste vraagvoorwaarde en ze vervolgens te weigeren op de cursor "na" terugkeer uit de database.

Zie ook documentatie voor $arrayElemAt , $indexOfArray , $lt en Array.find() voor JavaScript




  1. Redis Client List doel en beschrijving

  2. Hoe Redis Hashes te gebruiken

  3. Hoe krijg je een collectielijst in mangoest?

  4. mgo time.Tijd of booleaanse controle