Pergunta
Quais são as melhores práticas para armazenar conjuntos aninhados (como árvores de comentários) em MongoDB?
Quero dizer, todos os comentários podem ter um comentário dos pais e comissões de filhos (respostas).
Armazenando -os assim:
{
title: "Hello",
body: "Please comment me!",
comments: [
{
author: "Peter",
text: "Hi there",
answers: [
{
author: "Peter",
text: "Hi there",
answers: [
{ author: "Ivan", text: "Hi there" },
{ author: "Nicholas", text: "Hi there" }
]
},
{ author: "Ivan", text: "Hi there" },
{ author: "Nicholas", text: "Hi there" },
]
},
{ author: "Ivan", text: "Hi there" },
{ author: "Nicholas", text: "Hi there" },
]
}
Não é legal, porque não podemos, por exemplo, pedir "todos os post que são comentados por Peter" sem mapa/redução.
Solução
Eu acho que não há solução perfeita - depende de quais operações são mais importantes para o seu aplicativo. Acredito que o Silicon Alley Insider armazena comentários aninhados com MongoDB, por exemplo. Isso torna a consulta que você menciona mais.
Uma opção é armazenar no nível superior na postagem de uma lista de todos os comentaristas em uma matriz. Pense nisso como dados desnormalizados. Então, pode -se encontrar facilmente todas as postagens que envolvem um determinado comentarista. Em seguida, para perfurar, você usa mapa/redução ou db.eval () para obter as informações postais aninhadas dentro.
Outra nota - se você estiver lidando com um único documento, o db.eval () provavelmente é mais leve que o mapa/redução. $ onde também é uma opção, mas pode ser lento, por isso gosto da 'lista de comentaristas' mencionada acima - não é fácil indexar essa matriz também (consulte 'Multikey' nos documentos).
Outras dicas
No link do post de DM, Dwight Merriman menciona usando uma chave de caminho e fazendo correspondências de regex
{
path : "a.b.c.d.e.f"
}
Outra maneira de fazer isso seria com as matrizes
{
path : ["a", "b", "c", "d", "e", "f"]
}
db.test.ensureIndex({path: 1})
Isso deve torná -lo muito rápido.
Se cada nó só puder estar em um único caminho, você não precisará se preocupar com onde está localizado na lista
db.test.find({path: "a"})
encontraria todos os filhos de "A"
Em vez de nomes de caminhos, eu provavelmente usaria a _id dos nós.
Atualizar
- Uma coisa a ter cuidado é que um índice só pode ter uma matriz.
Tenha cuidado para usar explicar em suas consultas
db.test.find ({path: {$ in: ["a", "b"]})
da-te
db.test.find({path: {$in: ["a", "b"]}}).explain()
{
"cursor" : "BtreeCursor path_1 multi",
"nscanned" : 2,
"nscannedObjects" : 2,
"n" : 1,
"millis" : 0,
"nYields" : 0,
"nChunkSkips" : 0,
"isMultiKey" : true,
"indexOnly" : false,
"indexBounds" : {
"path" : [
[
"a",
"a"
],
[
"b",
"b"
]
]
}
}
mas
db.test.find({path: {$all: ["a", "b"]}}).explain()
{
"cursor" : "BtreeCursor path_1",
"nscanned" : 1,
"nscannedObjects" : 1,
"n" : 1,
"millis" : 0,
"nYields" : 0,
"nChunkSkips" : 0,
"isMultiKey" : true,
"indexOnly" : false,
"indexBounds" : {
"path" : [
[
"a",
"a"
]
]
}
}
usa apenas o primeiro elemento e depois digitaliza todos os resultados correspondentes para b.
Se A é o seu elemento raiz ou estiver na maioria dos seus registros, você está fazendo uma varredura quase completa dos registros, em vez de uma consulta de índice eficiente.