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

Mongodb Query basata sul numero di campi in un record

Non è ancora una bella query da eseguire, ma esiste un modo leggermente più moderno per farlo tramite $objectToArray e $redact

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

Dove $objectToArray fondamentalmente costringe l'oggetto a formare un array, proprio come una combinazione di Object.keys() e .map() sarebbe in JavaScript.

Non è ancora un'idea fantastica poiché richiede la scansione dell'intera raccolta, ma almeno le operazioni del framework di aggregazione utilizzano il "codice nativo" anziché l'interpretazione JavaScript, come nel caso dell'utilizzo di $where .

Quindi è generalmente consigliabile modificare la struttura dei dati e utilizzare un array naturale e le proprietà "size" memorizzate, ove possibile, al fine di rendere le operazioni di query più efficaci.

Sì, è possibile farlo ma non nel modo migliore. Il motivo è che stai essenzialmente utilizzando un $where query dell'operatore che utilizza la valutazione JavaScript per abbinare i contenuti. Non è il modo più efficiente in quanto questo non può mai utilizzare un indice e deve testare tutti i documenti:

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

Questo cerca la condizione corrispondente a "tre" elementi, quindi verranno restituiti solo due dei tuoi documenti elencati:

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

Oppure per "cinque" campi o più puoi fare più o meno lo stesso:

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

Quindi gli argomenti di quell'operatore sono effettivamente istruzioni JavaScript che vengono valutate sul server per restituire dove true .

Un modo più efficiente è memorizzare il "conteggio" degli elementi nel documento stesso. In questo modo puoi "indicizzare" questo campo e le query sono molto più efficienti in quanto ogni documento nella raccolta selezionata da altre condizioni non ha bisogno di essere scansionato per determinare la lunghezza:

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

Quindi per ottenere i documenti con "cinque" elementi è sufficiente la semplice query:

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

Questa è generalmente la forma più ottimale. Ma un altro punto è che la struttura generale "Oggetto" di cui potresti essere soddisfatto dalla pratica generale non è qualcosa con cui MongoDB "gioca bene" in generale. Il problema è l'"attraversamento" degli elementi nell'oggetto, e in questo modo MongoDB è molto più felice quando si usa un "array". E anche in questa forma:

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

Quindi, se passi effettivamente a un formato "array" come quello, puoi fare un esatto lunghezza di un array con una versione di $size operatore:

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

Quell'operatore può lavorare per un esatto valore per una lunghezza di matrice in quanto è una disposizione di base di ciò che può essere fatto con questo operatore. Ciò che non puoi fare è documentato in una corrispondenza "in-eguaglianza". Per questo è necessario il "framework di aggregazione" per MongoDB, che è un'alternativa migliore alle operazioni JavaScript e mapReduce:

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

Quindi queste sono le alternative. È disponibile un metodo "nativo" per l'aggregazione e un tipo di matrice. Ma è abbastanza discutibile che la valutazione JavaScript sia anche "nativa" per MongoDB, ma non quindi implementata nel codice nativo.