sql >> Databasteknik >  >> NoSQL >> MongoDB

Mongodb-fråga baserad på antalet fält i en post

Det är fortfarande inte en trevlig fråga att köra, men det finns ett lite modernare sätt att göra det via $objectToArray och $redact

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

Där $objectToArray tvingar i princip objektet till en arrayform, ungefär som en kombination av Object.keys() och .map() skulle göra det i JavaScript.

Det är fortfarande inte en fantastisk idé eftersom det kräver genomsökning av hela samlingen, men åtminstone aggregationsramverksoperationerna använder "native code" i motsats till JavaScript-tolkning som är fallet med $where .

Så det är fortfarande generellt tillrådligt att ändra datastruktur och använda en naturlig array samt lagrade "storleks"-egenskaper där det är möjligt för att göra de mest effektiva frågeoperationerna.

Ja det går att göra men inte på det snyggaste sättet. Anledningen till detta är att du i huvudsak använder en $where operatörsfråga som använder JavaScript-utvärdering för att matcha innehållet. Inte det mest effektiva sättet eftersom detta aldrig kan använda ett index och måste testa alla dokument:

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

Detta letar efter villkoret som matchar "tre" element, sedan skulle bara två av dina listade dokument returneras:

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

Eller för "fem" fält eller fler kan du göra ungefär samma sak:

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

Så argumenten till den operatören är faktiskt JavaScript-satser som utvärderas på servern för att returnera där true .

Ett mer effektivt sätt är att lagra "antal" av elementen i själva dokumentet. På så sätt kan du "indexera" det här fältet och frågorna blir mycket mer effektiva eftersom varje dokument i samlingen som väljs av andra villkor inte behöver skannas för att bestämma 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}

För att sedan få dokumenten med "fem" element behöver du bara den enkla frågan:

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

Det är i allmänhet den mest optimala formen. Men en annan poäng är att den allmänna "Objekt"-strukturen som du kanske är nöjd med från allmänpraktik inte är något som MongoDB "leker bra" med i allmänhet. Problemet är "traversering" av element i objektet, och på så sätt är MongoDB mycket gladare när du använder en "array". Och även i denna 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å om du faktiskt byter till ett sånt "array"-format så kan du göra en exakt längden på en array med en version av $size operatör:

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

Den operatören kan arbeta för en exakt värde för en matrislängd eftersom det är en grundläggande bestämmelse om vad som kan göras med denna operatör. Vad du inte kan göra som dokumenteras i en "ojämlik" match. För det behöver du "aggregationsramverket" för MongoDB, som är ett bättre alternativ till JavaScript och 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
    }}
])

Det är alltså suppleanterna. Det finns en "native" metod tillgänglig för aggregering och en matristyp. Men det är ganska tveksamt att JavaScript-utvärderingen också är "native" till MongoDB, bara därför inte implementerad i inbyggd kod.



  1. redis cluster reshard [ERR] Anropar MIGRATE:ERR Syntaxfel

  2. Hur säger man... matcha när fältet är ett nummer... i mongodb?

  3. MongoDB välj och sammanfoga fält

  4. Mongo med java - hitta sökfråga med batchstorlek