sql >> Base de Datos >  >> NoSQL >> MongoDB

Mongodb Consulta basada en la cantidad de campos en un registro

Todavía no es una buena consulta para ejecutar, pero hay una forma un poco más moderna de hacerlo a través de $objectToArray y $redact

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

Donde $objectToArray básicamente obliga al objeto a formar una matriz, como una combinación de Object.keys() y .map() lo haría en JavaScript.

Todavía no es una idea fantástica ya que requiere escanear toda la colección, pero al menos las operaciones del marco de agregación usan "código nativo" en lugar de la interpretación de JavaScript como es el caso usando $where .

Por lo tanto, generalmente es recomendable cambiar la estructura de datos y usar una matriz natural, así como también propiedades de "tamaño" almacenadas cuando sea posible para realizar las operaciones de consulta más efectivas.

Sí, es posible hacerlo, pero no de la mejor manera. La razón de esto es que esencialmente está usando un $where consulta de operador que utiliza la evaluación de JavaScript para hacer coincidir los contenidos. No es la forma más eficiente ya que nunca puede usar un índice y necesita probar todos los documentos:

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

Esto busca la condición que coincida con "tres" elementos, luego solo se devolverán dos de sus documentos enumerados:

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

O para "cinco" campos o más, puede hacer lo mismo:

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

Entonces, los argumentos para ese operador son efectivamente declaraciones de JavaScript que se evalúan en el servidor para devolver donde true .

Una forma más eficiente es almacenar el "recuento" de los elementos en el propio documento. De esta manera puede "indexar" este campo y las consultas son mucho más eficientes ya que cada documento de la colección seleccionado por otras condiciones no necesita ser escaneado para determinar la longitud:

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

Luego, para obtener los documentos con "cinco" elementos, solo necesita la consulta simple:

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

Esa es generalmente la forma más óptima. Pero otro punto es que la estructura general de "Objeto" con la que podría estar satisfecho de la práctica general no es algo con lo que MongoDB "juegue bien" en general. El problema es el "recorrido" de elementos en el objeto, y de esta manera MongoDB es mucho más feliz cuando usa una "matriz". E incluso en esta 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 }
    ]
}

Entonces, si realmente cambia a un formato de "matriz" como ese, entonces puede hacer una exacta longitud de una matriz con una versión de $size operador:

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

Ese operador puede trabajar para un exacto valor para una longitud de matriz, ya que es una disposición básica de lo que se puede hacer con este operador. Lo que no puede hacer como está documentado en un partido de "desigualdad". Para eso, necesita el "marco de agregación" para MongoDB, que es una mejor alternativa a las operaciones de JavaScript y 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
    }}
])

Así que esos son los suplentes. Hay un método "nativo" disponible para la agregación y un tipo de matriz. Pero es bastante discutible que la evaluación de JavaScript también sea "nativa" de MongoDB, solo que, por lo tanto, no está implementada en el código nativo.