Pregunta

Tengo un piso de la colección de documentos, donde algunos documentos tienen un parent: ObjectId campo, que señala otro documento de la misma colección, es decir:

{id: 1, metadata: {text: "I'm a parent"}}
{id: 2, metadata: {text: "I'm child 1", parent: 1}}

Ahora, me gustaría recuperar todos los padres de familia donde metadata.text = "I'm a parent" además, es hijo de los elementos.Pero quiero que los datos anidadas formato, así que simplemente puedo proceso después sin tener una mirada en metadata.parent.El resultado debería parecerse a:

{
  id: 1,
  metadata: {text: "I'm a parent"},
  children: [
    {id: 2, metadata: {text: "I'm child 1", parent: 1}}
  ]
}

(children también podrían ser parte de los padres metadata objeto si que es más fácil)

¿Por qué no puedo guardar los documentos en una estructura anidada? No quiero almacenar los datos en un anidada formato en DB, debido a que los documentos son parte de GridFS.

El principal problema es: ¿Cómo puedo saber MongoDB para anidar todo el documento?O ¿tengo que usar el Mongo de agregación de marco para esa tarea?

¿Fue útil?

Solución

Para la especie de "proyección" que usted está pidiendo, a continuación, el marco de agregación es la herramienta correcta como este tipo de "documento" re-formar" sólo es soportado realmente allí.

El otro caso es el "padre/hijo" cosa", en la que de nuevo necesita ser "creativos" cuando el agrupamiento utilizando el marco de agregación.El total de las operaciones de mostrar lo que es esencialmente involucrado:

db.collection.aggregate([

    // Group parent and children together with conditionals
    { "$group": {
        "_id": { "$ifNull": [ "$metadata.parent", "$_id" ] },
        "metadata": {
            "$addToSet": {
                "$cond": [
                    { "$ifNull": [ "$metadata.parent", false ] },
                    false,
                    "$metadata"
                ]
            }
        },
        "children": {
            "$push": {
                "$cond": [
                    { "$ifNull": [ "$metadata.parent", false ] },
                    "$$ROOT",
                    false
                ]
            }
        }
    }},

    // Filter out "false" values
    { "$project": {
        "metadata": { "$setDifference": [ "$metadata", [false] ] },
        "children": { "$setDifference": [ "$children", [false] ] }
    }},

    // metadata is an array but should only have one item
    { "$unwind": "$metadata" },

    // This is essentially sorting the children as "sets" are un-ordered 
    { "$unwind": "$children" },
    { "$sort": { "_id": 1, "children._id": 1 } },
    { "$group": {
        "_id": "$_id",
        "metadata": { "$first": "$metadata" },
        "children": { "$push": "$children" }
    }}
])

Lo principal aquí es la $ifNull operador utilizado en la agrupación _id.Este va a elegir $group en el campo "padre" donde se presente, de lo contrario, utilizando el documento general _id.

Cosas similares se realizan con el $cond operador después de que la evaluación es el hecho de que los datos para agregar a la matriz o "set".En la siguiente $project el false los valores son filtrados por el uso de la $setDifference operador.

Si el final $sort y $group parece confuso, a continuación, la verdadera razón es debido a que el operador que se utiliza es un "conjunto" del operador, con la consiguiente "set" se considera onu-ordenó.Así que en realidad esa parte está ahí para asegurarse de que el contenido de la matriz aparecen en el orden de su propia _id campo.

Sin los operadores de MongoDB 2.6 esto todavía se puede hacer, pero sólo un poco diferente.

db.collection.aggregate([
    { "$group": {
        "_id": { "$ifNull": [ "$metadata.parent", "$_id" ] },
        "metadata": {
            "$addToSet": {
                "$cond": [
                    { "$ifNull": [ "$metadata.parent", false ] },
                    false,
                    "$metadata"
                ]
            }
        },
        "children": {
            "$push": {
                "$cond": [
                    { "$ifNull": [ "$metadata.parent", false ] },
                    { "_id": "$_id","metadata": "$metadata" },
                    false
                ]
            }
        }
    }},
    { "$unwind": "$metadata" },
    { "$match": { "metadata": { "$ne": false } } },
    { "$unwind": "$children" },
    { "$match": { "children": { "$ne": false } } },
    { "$sort": { "_id": 1, "children._id": 1 } },
    { "$group": {
        "_id": "$_id",
        "metadata": { "$first": "$metadata" },
        "children": { "$push": "$children" }
    }}
])

Esencialmente lo mismo, pero sin los nuevos operadores que se introdujeron en MongoDB 2.6, por lo que este funciona en versiones anteriores también.

Todo estará bien siempre y cuando sus relaciones son de un solo nivel de los padres y el niño.Para los niveles anidados que usted necesita para invocar un proceso mapReduce en su lugar.

Otros consejos

Yo quería un resultado similar a Neil Lunn la respuesta, excepto yo quería buscar todos los padres, independientemente de que tengan hijos o no.También quería generalizar a trabajar en cualquier colección que había un único nivel de anidado de los niños.

Aquí está mi consulta basada en Neil Lunn la respuesta

db.collection.aggregate([
  {
    $group: {
      _id: {
        $ifNull: ["$parent", "$_id"]
      },
      parent: {
        $addToSet: {
          $cond: [
            {
              $ifNull: ["$parent", false]
            }, false, "$$ROOT"
          ]
        }
      },
      children: {
        $push: {
          $cond: [
            {
              $ifNull: ["$parent", false]
            }, "$$ROOT", false
          ]
        }
      }
    }
  }, {
    $project: {
      parent: {
        $setDifference: ["$parent", [false]]
      },
      children: {
        $setDifference: ["$children", [false]]
      }
    }
  }, {
    $unwind: "$parent"
  }
])

Esto se traduce en que cada padre se volvió donde el padre de campo contiene todo el documento principal y el campo de niños devolviendo una matriz vacía si el padre no tiene hijos o una matriz de documentos secundarios.

{
  _id: PARENT_ID
  parent: PARENT_OBJECT
  children: [CHILD_OBJECTS]
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top