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

Filtrar los resultados por el valor del campo de entrada de la última matriz

El kilometraje puede variar en esto y bien puede resultar que "actualmente" el proceso que está siguiendo resulte ser al menos "más adecuado". Pero probablemente podamos hacerlo de manera más eficiente.

Qué podrías hacer ahora

Siempre que sus matrices ya estén "ordenadas" mediante el $sort modificador con $push , entonces probablemente puedas hacer esto:

db.somedb.find(
  { 
    "partn.is_partner": true,
    "$where": function() {
      return this.partn.slice(-1)[0].is_partner == true;
    }
  },
  { "partn": { "$slice": -1 } }
)

Entonces, mientras partn,is_partner está "indexado", esto sigue siendo bastante eficiente ya que la condición de consulta inicial se puede cumplir usando un índice. La parte que no puede es $where cláusula aquí que utiliza la evaluación de JavaScript.

Pero lo que esa segunda parte en el $where está haciendo es simplemente "cortar" el último elemento de la matriz y probar su valor de is_partner propiedad para ver si es verdad. Solo si también se cumple esa condición se devuelve el documento.

También está el $slice operador de proyección Esto hace lo mismo al devolver el último elemento de la matriz. Las coincidencias falsas ya están filtradas, por lo que solo muestra el último elemento donde es verdadero.

Combinado con el índice como se mencionó, entonces esto debería ser bastante rápido dado que los documentos ya se han seleccionado y la condición de JavaScript solo filtra el resto. Tenga en cuenta que sin otro campo con una condición de consulta estándar que coincida, un $where cláusula no puede utilizar un índice. Así que siempre trate de usar "moderado" con otras condiciones de consulta en su lugar.

Qué puedes hacer en el futuro

Next Up, aunque no está disponible en el momento de escribir este artículo, pero sin duda en un futuro próximo estará el $slice operador para el marco de agregación. Esto se encuentra actualmente en la rama de desarrollo, pero aquí hay un vistazo de cómo funciona:

db.somedb.aggregate([
  { "$match": { "partn.is_partner": true } },
  { "$redact": {
    "$cond": {
      "if": { 
        "$anyElementTrue": {
          "$map": {
            "input": { "$slice": ["$partn",-1] },
            "as": "el",
            "in": "$$el.is_partner"
          }
        }
      },
      "then": "$$KEEP",
      "else": "$$PRUNE"
    }
  }},
  { "$project": {
      "partn": { "$slice": [ "$partn",-1 ] }
  }}
])

Combinando ese $slice dentro de un $redact La etapa aquí permite filtrar los documentos con una condición lógica, probando el documento. En este caso, el $slice produce una matriz de un solo elemento que se envía a $map para simplemente extraer el único is_partner valor (todavía como una matriz). Como, en el mejor de los casos, sigue siendo una matriz de un solo elemento, la otra prueba es $anyElementTrue lo que hace que este sea un resultado booleano singular, adecuado para $cond .

El $redact here decide sobre ese resultado si $$KEEP o $$PRUNE el documento a partir de los resultados. Luego usamos $slice de nuevo en el proyecto para devolver el último elemento de la matriz después del filtrado.

Eso resulta ser más o menos exactamente lo que hace la versión de JavaScript, con la excepción de que utiliza todos los operadores codificados nativos y, por lo tanto, debería ser un poco más rápido que la alternativa de JavaScript.

Ambos formularios devuelven su primer documento como se esperaba:

{
    "_id" : 0,
    "partn" : [
            {
                    "date" : ISODate("2015-07-28T00:59:14.963Z"),
                    "is_partner" : true
            },
            {
                    "date" : ISODate("2015-07-28T01:00:32.771Z"),
                    "is_partner" : false
            },
            {
                    "date" : ISODate("2015-07-28T01:15:29.916Z"),
                    "is_partner" : true
            },
            {
                    "date" : ISODate("2015-08-05T13:48:07.035Z"),
                    "is_partner" : false
            },
            {
                    "date" : ISODate("2015-08-05T13:50:56.482Z"),
                    "is_partner" : true
            }
    ]
}

El gran problema aquí con ambos es que su matriz ya debe estar ordenada, por lo que la fecha más reciente es la primera. Sin eso, necesita el marco de agregación para $sort la matriz, tal como lo está haciendo ahora.

No es realmente eficiente, por eso debe "ordenar previamente" su matriz y mantener el orden en cada actualización.

Como un truco útil, esto en realidad reordenará todos los elementos de la matriz en todos los documentos de la colección en una declaración simple:

db.somedb.update(
    {},
    { "$push": { 
        "partn": { "$each": [], "$sort": { "date": 1 } }
    }},
    { "multi": true }
)

Entonces, incluso si no está "empujando" un nuevo elemento en una matriz y simplemente actualizando una propiedad, siempre puede aplicar esa construcción básica para mantener la matriz ordenada como lo desea.

Vale la pena considerarlo, ya que debería hacer las cosas mucho más rápidas.