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

Mongodb-forespørgsel baseret på antallet af felter i en post

Det er stadig ikke en god forespørgsel at køre, men der er en lidt mere moderne måde at gøre det på via $objectToArray og $redact

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

Hvor $objectToArray dybest set tvinger objektet til en matrixform, meget som en kombination af Object.keys() og .map() ville i JavaScript.

Det er stadig ikke en fantastisk idé, da det kræver scanning af hele samlingen, men i det mindste bruger aggregeringsrammeoperationerne "native code" i modsætning til JavaScript-fortolkning, som det er tilfældet med $where .

Så det er stadig generelt tilrådeligt at ændre datastruktur og bruge et naturligt array samt lagrede "størrelses"-egenskaber, hvor det er muligt for at lave de mest effektive forespørgselsoperationer.

Ja det er muligt at gøre, men ikke på den pæneste måde. Grunden til dette er, at du i det væsentlige bruger en $where operatørforespørgsel, som bruger JavaScript-evaluering til at matche indholdet. Ikke den mest effektive måde, da dette aldrig kan bruge et indeks og skal teste alle dokumenter:

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

Dette leder efter betingelsen, der matcher "tre" elementer, så ville kun to af dine angivne dokumenter blive returneret:

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

Eller for "fem" felter eller flere kan du gøre meget det samme:

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

Så argumenterne til denne operatør er faktisk JavaScript-sætninger, der evalueres på serveren for at returnere hvor true .

En mere effektiv måde er at gemme "optællingen" af elementerne i selve dokumentet. På denne måde kan du "indeksere" dette felt, og forespørgslerne er meget mere effektive, da hvert dokument i samlingen valgt af andre betingelser ikke skal scannes for at bestemme længden:

{_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}

Så for at få dokumenterne med "fem" elementer behøver du kun den simple forespørgsel:

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

Det er generelt den mest optimale form. Men en anden pointe er, at den generelle "Objekt"-struktur, som du måske er tilfreds med fra almen praksis, ikke er noget, som MongoDB "spiller godt" med generelt. Problemet er "traversering" af elementer i objektet, og på denne måde er MongoDB meget gladere, når du bruger et "array". Og endda i denne 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 }
    ]
}

Så hvis du faktisk skifter til sådan et "array"-format, kan du gøre en nøjagtig længden af ​​et array med én version af $size operatør:

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

Denne operatør kan arbejde for en nøjagtig værdi for en matrixlængde, da det er en grundlæggende bestemmelse af, hvad der kan gøres med denne operatør. Hvad du ikke kan gøre som er dokumenteret i et "ulige" match. Til det har du brug for "aggregeringsrammerne" til MongoDB, som er et bedre alternativ til JavaScript og mapReduce operationer:

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

Så det er suppleanterne. Der er en "native" metode tilgængelig for aggregering og en matrixtype. Men det kan ret argumenteres, at JavaScript-evalueringen også er "native" til MongoDB, bare derfor ikke implementeret i indbygget kode.



  1. Hvordan kan jeg indlæse data fra mongodb-samling til pandas' DataFrame?

  2. Cloudera operationel databasereplikering i en nøddeskal

  3. Indstil redis nøgle/værdi med camel-redis

  4. Bruger erklæring ikke fundet i mongodb c++ driver