sql >> Databasteknik >  >> NoSQL >> MongoDB

Hitta två element i en uppsättning dokument som visas i en given ordning

Om du vill ha den sortens begränsningar i frågan, har du i princip två alternativ beroende på vad din MongoDB-version stöder:

MongoDB 3.6

Du skulle helst använda $expr "utöver" till alla normala frågevillkor för att faktiskt välja giltiga dokument:

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 matcha det "sista" förekomst:

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

Tidigare versioner

Samma sak, men utan inbyggda operatorer måste du använda JavaScript-utvärderingen av code>$where :

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 matcha det "sista" förekomst:

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

Om du behövde det i en aggregeringspipeline skulle du använda $redact och liknande logik som det första exemplet istället:

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 räcker med att säga att "jämförelselogiken" faktiskt inte är inbyggd i "frågeoperatoruttryck" själva, så den enda delen som "optimalt" kan appliceras på ett index använder $all frågeoperatör i alla fall. Den väsentliga återstående logiken tillämpas faktiskt "efter" att huvuduttrycket utvärderas och "utöver" så att inga resultat returneras annat än de som möter uttrycket med antingen $expr eller $where .

Den grundläggande logiken för var och en är i huvudsak att extrahera värdet av "index" egenskap från den "första" arraymedlemmen som faktiskt matchar respektive värde i "värde" fast egendom. Om detta är "mindre än", är villkoret true och detta uppfyller dokumentet som returneras.

Så notera att antingen "beräknad utvärdering" matchar effektiviteten hos frågeoperatorer, och utan att användas "i kombination" av andra frågeoperatorvillkor som kan komma åt ett "index", kommer en "full samlingsskanning" att initieras.

Men det övergripande resultatet är förvisso mer effektivt än att returnera alla matchande objekt till det första frågevillkoret och sedan avvisa dem på markören "efter" att ha återvänt från databasen.

Se även dokumentation för $arrayElemAt , $indexOfArray , $lt och Array.find() för JavaScript




  1. Vanliga MongoDB-intervjufrågor

  2. Hur frågar man mongodb från groovy/grails?

  3. få post med minst ett associerat objekt

  4. Hur man utesluter nollvärden från Mongoose-befolkningsfrågan