es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Consulta de MongoDB para el último documento incrustado con un atributo específico en un array de documentos.

Tengo una base de datos de MongoDB (alojada como un Cosmos DB), donde mis documentos tienen una matriz de documentos incrustados, algo así:

{
    "computer": "comp01",
    "reports": [
        {
            "os": "windows 10",
            "manufacturer": "dell"
        },
        {
            "os": "windows 11",
            "hdd": "500GB"
        }
    ]
}

{
    "computer": "comp02",
    "reports": [
        {
            "os": "windows 8",
            "manufacturer": "hp",
            "hdd": "240GB"
        },
        {
            "os": "Manjaro"
        },
        {
            "hdd": "300GB"
        }
    ]
}

Quiero hacer una consulta en la que solo se incluyan ciertos documentos en la matriz en los resultados. Los documentos incrustados devueltos deberían ser aquellos en los que está presente un atributo específico. Por ejemplo, para “os”, debería ser el último documento que contiene el atributo:

[
    {
        "computer": "comp01",
        "reports": [
            {
                "os": "windows 11",
                "hdd": "500GB"
            }
        ]
    },
    {
        "computer": "comp02",
        "reports": [
            {
                "os": "Manjaro"
            }
        ]
    }
]

Alternativamente, solo el valor “os” en los documentos incrustados, sin otros atributos o documentos vacíos:

[
    {
        "computer": "comp01",
        "reports": [
            {
                "os": "windows 10"
            },
            {
                "os": "windows 11"
            }
        ]
    },
    {
        "computer": "comp02",
        "reports": [
            {
                "os": "windows 8"
            },
            {
                "os": "Manjaro"
            }
        ]
    }
]

O esto, con solo el último valor del atributo:

[
    {
        "computer": "comp01",
        "reports": [
            {
                "os": "windows 11"
            }
        ]
    },
    {
        "computer": "comp02",
        "reports": [
            {
                "os": "Manjaro"
            }
        ]
    }
]

He intentado varias cosas que hacen parte de esto, pero siempre parece que hay algo que no cuadra. Mis intentos más destacados (los más cercanos a los resultados deseados) son:

  1. Proyección:
db.collection.find({}, {"reports.os":1 })

Esto devuelve documentos vacíos para los documentos de la matriz, que no he podido encontrar una forma de deshabilitar. Es decir:

[
    {
        "computer": "comp01",
        "reports": [
            {
                "os": "windows 10"
            },
            {
                "os": "windows 11"
            }
        ]
    },
    {
        "computer": "comp02",
        "reports": [
            {
                "os": "windows 8"
            },
            {
                "os": "Manjaro"
            },
            { }
        ]
    }
]
  1. ElemMatch:
db.collection.find({}, {reports: {$elemMatch: {"os":{$exists: true}}}})

Esto devuelve todo el documento del primer documento incrustado que se corresponde. Esto podría ser aceptable si fuera posible devolver la última coincidencia en su lugar. Sin embargo, parece que esto no es posible.

  1. Slices:
db.collection.find({}, {"computer":1, reports: {$slice:-1}})

Con algo así he sido capaz de obtener el último documento en la matriz, lo cual podría ser aceptable para mi caso de uso si pudiera asegurarme de que el atributo buscado estuviera en él. El problema aquí, en el ejemplo de datos en cuestión, sería que no podría extraer el atributo “os” para la computadora comp02.

Agradecería mucho si alguien pudiera ayudar a arrojar algo de luz sobre cómo obtener los datos en una o más de las tres formas explicadas al principio, o si supiera, con certeza, si lo que estoy intentando hacer es imposible en las consultas de MongoDB.

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Para lograr el resultado deseado, se puede utilizar la tubería de agregación de MongoDB para manipular los datos antes de devolverlos. Aquí hay una consulta de ejemplo para cada uno de los tres resultados deseados:

    1. Para obtener solo el último documento incrustado que contiene el atributo os:
    db.collection.aggregate([
      { $unwind: "$reports" }, // desenrollar el array
      { $match: { "reports.os": { $exists: true } } }, // solo incluir documentos con el campo 'os'
      { $sort: { "reports._id": -1 } }, // ordenar por _id descendente para obtener el último informe
      { $group: { _id: "$computer", reports: { $first: "$reports" } } } // agrupar por equipo y obtener el primer (último) informe
    ])
    

    Esto devolverá:

    [
      {
        "_id": "comp02",
        "reports": { "os": "Manjaro" }
      },
      {
        "_id": "comp01",
        "reports": { "os": "windows 11", "hdd": "500GB" }
      }
    ]
    
    1. Para obtener solo el atributo os para cada documento incrustado sin otros atributos o un documento vacío:
    db.collection.aggregate([
      { $unwind: "$reports" }, // desenrollar el array
      { $project: { _id: 0, computer: 1, os: "$reports.os" } }, // proyectar solo los campos de computadora y os
      { $match: { os: { $exists: true }, $expr: { $gt: [{ $size: { $objectToArray: "$os" } }, 0] } } } // solo incluir documentos con el campo 'os' y sin otros campos
    ])
    

    Esto devolverá:

    [
      {
        "computer": "comp01",
        "os": "windows 10"
      },
      {
        "computer": "comp01",
        "os": "windows 11"
      },
      {
        "computer": "comp02",
        "os": "windows 8"
      },
      {
        "computer": "comp02",
        "os": "Manjaro"
      }
    ]
    
    1. Para obtener solo el último valor del atributo os para cada documento:
    db.collection.aggregate([
      { $unwind: "$reports" }, // desenrollar el array
      { $match: { "reports.os": { $exists: true } } }, // solo incluir documentos con el campo 'os'
      { $sort: { "reports._id": -1 } }, // ordenar por _id descendente para obtener el último informe
      { $group: { _id: "$computer", os: { $first: "$reports.os" } } } // agrupar por equipo y obtener el primer (último) valor de os
    ])
    

    Esto devolverá:

    [
      {
        "_id": "comp02",
        "os": "Manjaro"
      },
      {
        "_id": "comp01",
        "os": "windows 11"
      }
    ]
    

    Estas consultas utilizan operadores de agregación como $unwind, $match, $sort y $group para manipular los datos antes de devolverlos. El operador $project se utiliza para elegir qué campos incluir en la salida. Puede modificar estas consultas para adaptarlas a su caso de uso específico.

Comments are closed.