Pergunta

Eu tenho uma mini coleção de documentos, onde alguns documentos têm um parent: ObjectId campo, que aponta outro documento da mesma coleção, por exemplo:

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

Agora eu gostaria de recuperar todos os pais, onde metadata.text = "I'm a parent" além disso, ele é filho de elementos.Mas eu quero que os dados em um aninhados formato, então eu simplesmente posso processá-lo depois, sem ter um olhar para metadata.parent.A saída deve se parecer:

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

(children também poderia ser parte do pais metadata objeto se que é mais fácil)

Por que não consigo salvar os documentos em uma estrutura aninhada? Eu não quiser armazenar os dados em um aninhada em formato de banco de dados, porque os documentos são parte de GridFS.

O principal problema é: Como posso saber o MongoDB para o ninho de um documento inteiro?Ou eu tenho que usar Mongo da agregação de quadro para essa tarefa?

Foi útil?

Solução

Para o tipo de "projeção" você está pedindo, em seguida, a agregação quadro é a ferramenta correta como este tipo de "documento re-shaping" só é realmente compatível com ela.

O outro caso é o "pai/filho", onde você precisa novamente ser "criativo" quando agrupamento utilizando a agregação quadro.O total de operações de mostrar o que é essencialmente:

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" }
    }}
])

A questão principal aqui é a $ifNull operador usado no agrupamento _id.Isto irá escolher $group sobre o "pai" do campo, onde presente, caso contrário, utilizar o documento geral _id.

Semelhantes coisas são feitas com a $cond operador, mais tarde, quando a avaliação é feita de que os dados para adicionar à matriz ou "set".No seguinte $project o false os valores são filtrados pelo uso do $setDifference o operador.

Se o final $sort e $group parece confuso, em seguida, o real motivo é porque o operador utilizado é um "conjunto" operador resultante "set" é considerado onu-ordenou.Então, realmente a parte que está lá apenas para se certificar de que o conteúdo da matriz aparecem na ordem de sua própria _id de campo.

Sem o adicional operadores do MongoDB 2.6 isso ainda pode ser feito, mas um pouco 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" }
    }}
])

Essencialmente, a mesma coisa, mas sem o mais recente operadores introduzido no MongoDB 2.6, para que este seria o trabalho em versões anteriores também.

Isso tudo vai estar bem, contanto que seus relacionamentos são um único nível de pai e filho.Para níveis aninhados, você precisa invocar um mapReduce do processo.

Outras dicas

Eu queria um resultado semelhante para Neil Lunn da resposta, exceto eu queria buscar todos os pais, independentemente de terem ou não filhos.Eu também queria generalizar-lo para trabalhar em qualquer coleção que tinha um único nível de aninhados filhos.

Aqui está a minha consulta com base na Neil Lunn resposta

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"
  }
])

Isso resulta em todos os pais que está sendo retornado para onde o pai campo contém todo o documento pai e os filhos de campo retornar uma matriz vazia se o pai não tem filhos ou uma matriz de criança documentos.

{
  _id: PARENT_ID
  parent: PARENT_OBJECT
  children: [CHILD_OBJECTS]
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top