2011-05-25 19 views
5

Soy bastante nuevo en couchDB e incluso después de leer "how to store hierarchical data" todavía no hace clic todavía.Recuperando datos jerárquicos/anidados de CouchDB

En lugar de utilizar el patrón de ruta completa como se describe en la wiki, espero seguir a los niños como una matriz de UUID y la principal como un único UUID. Me inclino por este patrón para poder mantener el orden de los niños según sus posiciones en el conjunto de niños.

Aquí hay algunos documentos de muestra en el sofá, los depósitos pueden contener cubos y artículos, los artículos solo pueden contener otros artículos. (UUID abreviados para mayor claridad):

{_id: 3944 
name: "top level bucket with two items" 
type: "bucket", 
parent: null 
children: [8989, 4839] 
} 
{_id: 8989 
name: "second level item with no sub items" 
type: "item" 
parent: 3944 
} 
{ 
_id: 4839 
name: "second level bucket with one item" 
type: "bucket", 
parent: 3944 
children: [5694] 
} 
{ 
_id: 5694 
name: "third level item (has one sub item)" 
type: "item", 
parent: 4839, 
children: [5390] 
} 
{ 
_id: 5390 
name: "fourth level item" 
type: "item" 
parent: 5694 
} 

¿Es posible buscar un documento por un identificador de documento incrustado dentro de una función de mapa?

function(doc) { 
    if(doc.type == "bucket" || doc.type == "item") 
     emit(doc, null); // still working on my key value output structure 
     if(doc.children) { 
      for(var i in doc.children) { 
       // can i look up a document here using ids from the children array? 
       doc.children[i]; // psuedo code 
       emit(); // the retrieved document would be emitted here 
      } 
     } 
    } 
} 

En un mundo ideal, la salida JSON sería algo así como.

{"_id":3944, 
"name":"top level bucket with two items", 
"type":"bucket", 
"parent":"", 
"children":[ 
    {"_id":8989, "name":"second level item with no sub items", "type":"item", "parent":3944}, 
    {"_id": 4839, "name":"second level bucket with one item", "type":"bucket", "parent":3944, "children":[ 
     {"_id":5694", "name":"third level item (has one sub item)", "type":"item", "parent": 4839, "children":[ 
      {"_id":5390, "name":"fourth level item", "type":"item", "parent":5694} 
     ]} 
    ]} 
] 
} 

Respuesta

6

Puede encontrar un debate general on the CouchDB wiki.

no tengo tiempo para probarlo en este momento, sin embargo, su función de mapa debe ser algo como:

function(doc) { 
    if (doc.type === "bucket" || doc.type === "item") 
     emit([ doc._id, -1 ], 1); 
     if (doc.children) { 
      for (var i = 0, child_id; child_id = doc.children[i]; ++i) { 
       emit([ doc._id, i ], { _id: child_id }); 
      } 
     } 
    } 
} 

Usted debe consultar con include_docs=true para obtener los documentos, tal como se explica en el CouchDB documentation: si su mapa La función emite un valor de objeto que tiene {'_id': XXX} y consulta la vista con el parámetro include_docs=true, luego CouchDB buscará el documento con id XXX en lugar del documento que se procesó para emitir el par de clave/valor.

Agregue startkey=["3944"]&endkey["3944",{}] para obtener solo el documento con la identificación "3944" con sus hijos.

EDIT: echar un vistazo a this question para más detalles.

+0

Gracias por ayudar a Marcello. Cuando ejecuto la función de mapa, la salida no está anidada como esperaba, todo es plano. ¿Algunas ideas? – berg

+0

Mi respuesta es [aquí] (http://stackoverflow.com/questions/6084741/how-to-merge-view-collation-into-useful-output-in-couchdb/6094540#6094540). Sin embargo, no lo recomiendo. ¿Cuál es la ventaja de una lista anidada? La lista plana está ordenada para que cada "elemento" o "cubo" sea seguido inmediatamente por sus hijos en el orden solicitado. Es muy fácil y eficiente atravesar esta lista. ¿Por qué necesita una lista anidada? Puede ser que pueda darte una mejor solución. –

+0

Tenía la esperanza de utilizar los resultados directamente en mi código de JavaScript del lado del cliente, que está esperando que los datos vuelvan anidados. ¡Pero después de leer la pregunta que vinculó parece que esto va contra el grano de CouchDB así que planeo hacer este lado del cliente! ¡Gracias de nuevo, marcaré esto como la respuesta! – berg

6

¿Se puede generar una estructura de árbol desde una vista? No. Las consultas de vista de CouchDB devuelven una lista de valores, no hay forma de que muestren algo más que una lista. Entonces, debe tratar su mapa devolviendo la lista de todos los descendientes de un cubo dado.

Sin embargo, puede conectar una función de posprocesamiento _list después de la vista, para convertir esa lista nuevamente en una estructura anidada. Esto es posible si sus valores conocen el _id de su padre — el algoritmo es bastante sencillo, simplemente haga otra pregunta si le da problemas.

¿Se puede tomar un documento por su id en la función de mapa? No. No hay forma de tomar un documento por su identificador desde CouchDB. La solicitud debe provenir de la aplicación, ya sea en forma de un estándar GET en el identificador del documento, o agregando include_docs=true a una solicitud de vista.

El motivo técnico de esto es bastante simple: CouchDB solo ejecuta la función de mapa cuando el documento cambia. Si se permitió que el documento A obtuviera el documento B, los datos emitidos se volverían inválidos cuando B cambiara.

Puede usted salida de todos los descendientes, sin memorización de la lista de los padres de todos los nodos? No. Las funciones de mapa de CouchDB emiten un conjunto de pares clave-valor-id para cada documento en la base de datos, por lo que la correspondencia entre la clave y el ID debe determinarse basándose en un solo documento.

Si usted tiene una estructura de árbol de cuatro niveles A -> B -> C -> D pero sólo deja un nodo sabe acerca de su padre y los hijos, entonces ninguno de los nodos por encima de saber que D es un descendiente de A, por lo que no será capaz de emitir la id de D con una clave basada en A y por lo tanto no será visible en la salida.

Por lo tanto, usted tiene tres opciones:

  • agarre sólo tres niveles (esto es posible porque B sabe que C es un descendiente de A), y agarrar niveles adicionales mediante la ejecución de la consulta.
  • almacenar alguna manera la lista de los descendientes de cada nodo dentro del nodo (esto es costoso).
  • Almacenar la lista de padres de cada nodo dentro del nodo.