MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Consulta Mongodb com base no número de campos em um registro


Ainda não é uma boa consulta para executar, mas há uma maneira um pouco mais moderna de fazer isso via $objectToArray e $redact
db.collection.aggregate([
  { "$redact": {
    "$cond": {
      "if": {
        "$eq": [
          { "$size": { "$objectToArray": "$value" } },
          3
        ]
      },
      "then": "$$KEEP",
      "else": "$$PRUNE"
    }
  }}
])

Onde $objectToArray basicamente coage o objeto em uma forma de array, muito parecido com uma combinação de Object.keys() e .map() faria em JavaScript.

Ainda não é uma ideia fantástica, pois requer a varredura de toda a coleção, mas pelo menos as operações da estrutura de agregação usam "código nativo" em oposição à interpretação de JavaScript, como é o caso de $where .

Portanto, ainda é geralmente aconselhável alterar a estrutura de dados e usar uma matriz natural, bem como propriedades de "tamanho" armazenadas, sempre que possível, para fazer as operações de consulta mais eficazes.

Sim, é possível fazer, mas não da maneira mais agradável. A razão para isso é que você está essencialmente usando um $where consulta de operador que usa avaliação JavaScript para corresponder ao conteúdo. Não é a maneira mais eficiente, pois isso nunca pode usar um índice e precisa testar todos os documentos:
db.collection.find({ "$where": "return Object.keys(this.value).length == 3" })

Isso procura a condição correspondente a "três" elementos, então apenas dois de seus documentos listados seriam retornados:
{ "_id" : "number1", "value" : { "a" : 1, "b" : 2, "f" : 5 } }
{ "_id" : "number2", "value" : { "e" : 2, "f" : 114, "h" : 12 } }

Ou para "cinco" campos ou mais, você pode fazer o mesmo:
db.numbers.find({ "$where": "return Object.keys(this.value).length >= 5" })

Portanto, os argumentos para esse operador são efetivamente instruções JavaScript que são avaliadas no servidor para retornar onde true .

Uma forma mais eficiente é armazenar a "contagem" dos elementos no próprio documento. Dessa forma, você pode "indexar" este campo e as consultas são muito mais eficientes, pois cada documento da coleção selecionado por outras condições não precisa ser digitalizado para determinar o comprimento:
{_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}

Então, para obter os documentos com "cinco" elementos, você só precisa da consulta simples:
db.collection.find({ "count": 5 })

Essa é geralmente a forma mais ideal. Mas outro ponto é que a estrutura geral de "objeto" com a qual você pode ficar feliz na prática geral não é algo com o qual o MongoDB "joga bem" em geral. O problema é "traversal" de elementos no objeto, e dessa forma o MongoDB fica muito mais feliz quando você usa um "array". E ainda nesta 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 }
    ]
}

Então, se você realmente mudar para um formato "array" como esse, poderá fazer um exato comprimento de uma matriz com uma versão do $size operador:
db.collection.find({ "values": { "$size": 5 } })

Esse operador pode trabalhar para um exato valor para um comprimento de matriz, pois isso é uma provisão básica do que pode ser feito com esse operador. O que você não pode fazer está documentado em uma correspondência "em igualdade". Para isso, você precisa da "estrutura de agregação" para MongoDB, que é uma alternativa melhor para as operações 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
    }}
])

Então esses são os suplentes. Existe um método "nativo" disponível para agregação e um tipo de array. Mas é bastante discutível que a avaliação do JavaScript também seja "nativa" do MongoDB, apenas não implementada em código nativo.