2009-12-05 21 views
7

¿Cuáles son las mejores prácticas para almacenar conjuntos anidados (como árboles de comentarios) en MongoDB?MongoDB conjuntos anidados

Quiero decir, cada comentario puede tener un comentario principal y comentarios de niños (respuestas).

almacenarlos como esto:

{ 
    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" }, 
    ] 
} 

no es fresco, porque no podemos, por ejemplo, pedir "todos los post que se ha comentado por Peter" sin map/reduce.

Respuesta

3

Creo que no hay una solución perfecta, depende de qué operaciones sean más importantes para su aplicación. Creo que Silicon Alley Insider almacena comentarios anidados con MongoDB, por ejemplo. Eso hace que la consulta que mencionas sea más difícil.

Una opción es almacenar en el nivel superior en la publicación una lista de todos los comentaristas en una matriz. Piense en eso como datos desnormalizados. Entonces uno puede encontrar fácilmente todas las publicaciones que involucran a un cierto comentarista. Luego, para profundizar, use map/reduce o db.eval() para obtener la información de publicación anidada dentro.

Otra nota: si se trata de un único documento, db.eval() probablemente sea más ligero que map/reduce. $ donde también hay una opción, pero puede ser lenta, así que me gusta la 'lista de comentaristas' adicionales mencionada anteriormente, no es fácil también indexar esa matriz (ver 'Multikey' en los documentos).

Consulte también: http://groups.google.com/group/mongodb-user/browse_thread/thread/df8250573c91f75a/e880d9c57e343b52?lnk=gst&q=trees#e880d9c57e343b52

2

En el enlace desde el puesto de dm Dwight Merriman menciones mediante una clave de ruta y haciendo expresión coincide con

{ 
    path : "a.b.c.d.e.f" 
} 

Otra forma de hacerlo sería con matrices

{ 
    path : ["a", "b", "c", "d", "e", "f"] 
} 

db.test.ensureIndex({path: 1}) 

que debería hacerlo bastante rápido.

si cada nodo sólo puede estar en un único camino, entonces no tendría que hacer la preocupación acerca de dónde se encuentra en la lista

db.test.find({path: "a"}) 

encontraría todos los niños de "a"

En lugar de nombres de ruta, probablemente usaría el _id de los nodos.

actualización

  • una cosa que tener cuidado de que es un índice sólo puede tener una matriz en ella.
  • Tenga cuidado al usar explique en sus consultas

    db.test.encontrar ({ruta: {$ en: [ "a", "b"]})

le da

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

pero

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

sólo se utiliza el primer elemento y luego escanea todos los resultados coincidentes para b.
Si a es su elemento raíz o está en la mayoría de sus registros, entonces realiza un escaneo casi completo de los registros en lugar de una consulta de índice eficiente.

Cuestiones relacionadas