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.