2009-10-07 18 views
108

tengo una pregunta que me he estado tratando de responder desde hace algún tiempo, pero no puedo averiguar:Principios para Documentos Modelado CouchDB

¿Cómo se diseña, o dividir, documentos CouchDB?

Tome una publicación de blog por ejemplo.

La forma semi "relacional" de hacerlo sería crear unos objetos:

  • Pon
  • usuario
  • comentario
  • Tag
  • fragmentos

Esto tiene mucho sentido. Pero estoy tratando de usar couchdb (por todas las razones por las que es genial) para modelar lo mismo y ha sido extremadamente difícil.

La mayoría de las entradas de blog que hay a continuación le dan un ejemplo fácil de cómo hacerlo. Básicamente lo dividen de la misma manera, pero dicen que puede agregar propiedades 'arbitrarias' a cada documento, lo que es definitivamente bueno. Por lo que tendría algo como esto en CouchDB:

  • Post (con etiquetas y fragmentos de los modelos de "pseudo" en el doc)
  • Comentario
  • usuario

Algunas personas incluso dicen que podría lanzar el comentario y usuario de allí, por lo que tendría esto:


post { id: 123412804910820 title: "My Post" body: "Lots of Content" html: "<p>Lots of Content</p>" author: { name: "Lance" age: "23" } tags: ["sample", "post"] comments { comment { id: 93930414809 body: "Interesting Post" } comment { id: 19018301989 body: "I agree" } } } 

Eso se ve muy bien y es fácil de entender. También entiendo cómo puede escribir vistas que extraen solo los comentarios de todos sus documentos de publicación, para incluirlos en los modelos de comentarios, lo mismo con los usuarios y las etiquetas.

Pero luego pienso, "¿por qué no acaba de poner toda mi sitio en un solo documento?":


site { domain: "www.blog.com" owner: "me" pages { page { title: "Blog" posts { post { id: 123412804910820 title: "My Post" body: "Lots of Content" html: "<p>Lots of Content</p>" author: { name: "Lance" age: "23" } tags: ["sample", "post"] comments { comment { id: 93930414809 body: "Interesting Post" } comment { id: 19018301989 body: "I agree" } } } post { id: 18091890192984 title: "Second Post" ... } } } } } 

Desde aquí se puede tomar puntos de vista para encontrar lo que quería con eso.

Entonces la pregunta que tengo es, ¿cómo se determina cuándo dividir el documento en documentos más pequeños, o cuándo hacer "RELACIONES" entre los documentos?

creo que sería mucho más "orientado a objetos", y más fácil de asignar a valorar los objetos, si se divide así:


posts { post { id: 123412804910820 title: "My Post" body: "Lots of Content" html: "<p>Lots of Content</p>" author_id: "Lance1231" tags: ["sample", "post"] } } authors { author { id: "Lance1231" name: "Lance" age: "23" } } comments { comment { id: "comment1" body: "Interesting Post" post_id: 123412804910820 } comment { id: "comment2" body: "I agree" post_id: 123412804910820 } } 

... pero luego empieza a buscar más como una base de datos relacional. Y muchas veces heredo algo que se parece al "sitio completo en un documento", por lo que es más difícil modelarlo con relaciones.

He leído muchas cosas sobre cómo/cuándo usar Bases de datos relacionales vs. Bases de datos de documentos, por lo que ese no es el problema principal aquí.Me pregunto más, ¿cuál es una buena regla/principio para aplicar al modelar datos en CouchDB?

Otro ejemplo es con archivos/datos XML. Algunos datos XML tienen más de 10 niveles de anidamiento, y me gustaría visualizar que usando el mismo cliente (Ajax on Rails por ejemplo, o Flex) me gustaría renderizar JSON desde ActiveRecord, CouchRest o cualquier otro Object Relational Mapper. A veces obtengo enormes archivos XML que son la estructura completa del sitio, como la que se muestra a continuación, y necesito asignarlos a Value Objects para utilizarlos en mi aplicación Rails, así no tengo que escribir otra forma de serializar/deserializar los datos :


<pages> <page> <subPages> <subPage> <images> <image> <url/> </image> </images> </subPage> </subPages> </page> </pages> 

Así que las preguntas generales CouchDB son:

  1. qué reglas/principios que utiliza para dividir sus documentos (relaciones, etc)?
  2. ¿Está bien colocar todo el sitio en un solo documento?
  3. En caso afirmativo, ¿cómo maneja la serialización/deserialización de documentos con niveles de profundidad arbitrarios (como el ejemplo grande json anterior, o el ejemplo xml)?
  4. ¿O no los convierte en VO, simplemente decide "estos están demasiado anidados en el Mapa Relacional de Objetos, así que solo accederé a ellos utilizando métodos XML/JSON sin formato"?

Muchas gracias por su ayuda, la cuestión de cómo dividir sus datos con CouchDB ha sido difícil para mí decir "así es como debería hacerlo a partir de ahora". Espero llegar pronto

He estudiado los siguientes sitios/proyectos.

  1. Hierarchical Data in CouchDB
  2. CouchDB Wiki
  3. Sofa - CouchDB App
  4. CouchDB The Definitive Guide
  5. PeepCode CouchDB Screencast
  6. CouchRest
  7. CouchDB README

... pero todavía no han respondido esta pregunta.

+2

wow has escrito un ensayo completo aquí ... :-) – Eero

+7

hey, esa es una buena pregunta – elmarco

Respuesta

15

El book dice, si mal no recuerdo, para desnormalizar hasta que "duela", teniendo en cuenta la frecuencia con la que sus documentos pueden ser actualizados.

  1. qué reglas/principios que utiliza para dividir sus documentos (relaciones, etc)?

Como regla general, que incluyen todos los datos que se necesitan para mostrar una página en relación con el artículo en cuestión. En otras palabras, todo lo que imprimirías en una hoja de papel del mundo real que le entregarías a alguien. P.ej. un documento de cotización de acciones incluiría el nombre de la compañía, el intercambio, la moneda, además de los números; un documento de contrato incluiría los nombres y direcciones de las contrapartes, toda la información sobre fechas y signatarios. Pero las cotizaciones de acciones de distintas fechas formarían documentos separados, los contratos separados formarían documentos separados.

  1. ¿Está bien colocar todo el sitio en un solo documento?

No, eso sería tonto, porque:

  • tendría que leer y escribir todo el sitio (el documento) en cada actualización, y que es muy ineficiente;
  • no se beneficiaría de ninguna vista de almacenamiento en caché.
+3

Gracias por participar un poco conmigo. Me da la idea de "incluir todos los datos que se necesitan para mostrar una página con respecto al elemento en cuestión", pero eso todavía es muy difícil de implementar. Una "página" podría ser una página de Comentarios, una página de Usuarios, una página de Publicaciones, o una página de Comentarios y Publicaciones, etc. ¿Cómo los dividiría entonces, principalmente? También podría hacer que su contrato se muestre con los usuarios. Obtengo los documentos de "forma similar", eso tiene sentido para mantenerlos separados. –

15

Sé que esta es una vieja pregunta, pero me encontré tratando de descubrir el mejor enfoque para este mismo problema. Christopher Lenz escribió una buena publicación en el blog sobre methods of modeling "joins" in CouchDB. Una de mis conclusiones fue: "La única manera de permitir la adición no conflictiva de datos relacionados es mediante la colocación de los datos relacionados en documentos separados". Entonces, por simplicidad, querrás inclinarte hacia la "desnormalización". Pero llegará a una barrera natural debido a escrituras contradictorias en ciertas circunstancias.

En su ejemplo de Publicaciones y Comentarios, si una sola publicación y todos sus comentarios vivían en un documento, entonces dos personas que intentaran publicar un comentario al mismo tiempo (es decir, en contra de la misma revisión del documento) causarían un conflicto. Esto empeoraría aún más en su escenario de "todo un sitio en un solo documento".

Así que creo que la regla general sería "desnormalizar hasta que duela", pero el punto donde "dolerá" es donde tienes una alta probabilidad de que se publiquen múltiples ediciones en la misma revisión de un documento.

+0

Interesante respuesta. Con esto en mente, uno debería preguntarse si un sitio de tráfico razonablemente alto incluso tendría todos los comentarios para una sola publicación de blog en un solo documento. Si leo este derecho, significa que cada vez que tenga personas agregando comentarios en rápida sucesión, es posible que tenga que resolver conflictos. Por supuesto, no sé cuán rápido en sucesión tendrían que ser para desencadenar esto. – pc1oad1etter

+1

En el caso en que los comentarios sean parte del documento en Couch, las publicaciones de comentarios simultáneos podrían generar un conflicto debido a que su ámbito de versión es la "publicación" con todos sus comentarios. En el caso en que cada uno de sus objetos sean colecciones de documentos, estos simplemente se convertirán en dos nuevos documentos de 'comentarios' con enlaces a la publicación y sin preocupaciones de colisión. También me gustaría señalar que construir vistas en el diseño de documentos "orientados a objetos" es sencillo: por ejemplo, se ingresa la clave de una publicación y luego se emiten todos los comentarios, ordenados por algún método, para esa publicación. –

5

Creo que la respuesta de Jake es clave en uno de los aspectos más importantes de trabajar con CouchDB que puede ayudarlo a tomar la decisión de determinar el alcance: conflictos.

En el caso en que tenga comentarios como una matriz de propiedad de la publicación en sí, y solo tiene una base de datos 'post' con un montón de enormes documentos 'post', como Jake y otros señalaron correctamente que podría Imagine un escenario en una publicación de blog realmente popular en la que dos usuarios envían modificaciones al documento de publicación simultáneamente, lo que provoca una colisión y un conflicto de versión para ese documento.

LADO: Como this article points out, también tienen en cuenta que cada vez que se solicita/actualización de dicha doc usted tiene que obtener/establecer el documento en su totalidad, por lo que pasa alrededor de una masiva documentos que, o bien representan la totalidad del sitio o un post con muchos comentarios al respecto pueden convertirse en un problema que desearía evitar.

En el caso en que las publicaciones se modelan por separado de los comentarios y dos personas envían un comentario de una historia, simplemente se convierten en dos documentos de "comentario" en esa base de datos, sin conflicto; solo dos operaciones PUT para agregar dos nuevos comentarios al db "comment".

Luego, para escribir las vistas que le devuelven los comentarios de una publicación, debe pasar el ID de publicación y luego emitir todos los comentarios que hacen referencia a esa ID de la publicación principal, ordenada de forma lógica. Tal vez incluso pases algo como [PostID, por nombre de usuario] como la clave de la vista de "comentarios" para indicar la publicación principal y cómo quieres que se ordenen los resultados o algo así.

MongoDB maneja los documentos de una manera diferente, permitiendo que los índices se construyan en subelementos de un documento, por lo que puede ver la misma pregunta en la lista de correo de MongoDB y alguien diciendo "simplemente haga que los comentarios sean propiedad de la publicación principal ".

Debido al bloqueo de escritura y la naturaleza de maestro único de Mongo, la cuestión conflictiva de revisión de dos personas que agregan comentarios no surgiría allí y la capacidad de consulta del contenido, como se mencionó, no se efectúa demasiado mal debido a los subíndices.

Dicho esto, si sus sub-elementos en ya sea DB van a ser enormes (por ejemplo 10s de miles de comentarios) Creo que es la recomendación de ambos bandos para que dichos elementos separados; Ciertamente, he visto que ese es el caso con Mongo, ya que hay algunos límites límite superiores sobre cuán grande puede ser un documento y sus subelementos.

+0

Muy útil. Gracias –

23

Ya ha habido algunas excelentes respuestas a esto, pero quería agregar algunas características más recientes de CouchDB a la combinación de opciones para trabajar con la situación original descrita por viatropos.

El punto clave en el que dividir documentos es donde podría haber conflictos (como se mencionó anteriormente). Nunca debe mantener documentos "enmarañados" masivamente en un solo documento, ya que obtendrá una ruta de revisión única para actualizaciones completamente independientes (por ejemplo, adición de comentarios que agrega una revisión a todo el documento del sitio). Administrar las relaciones o conexiones entre varios documentos más pequeños puede ser confuso al principio, pero CouchDB proporciona varias opciones para combinar piezas dispares en respuestas únicas.

La primera gran es la intercalación de vistas. Cuando se emiten pares de clave/valor en los resultados de una consulta de mapa/reducción, las claves se ordenan según la intercalación UTF-8 ("a" viene antes de "b"). También puede generar claves complejas desde su mapa/reducirlas como matrices JSON: ["a", "b", "c"]. Hacer eso le permitiría incluir un "árbol" de tipo construido a partir de claves de matriz. Usando su ejemplo anterior, podemos generar el post_id, luego el tipo de cosa que estamos refiriendo, luego su ID (si es necesario). Si tenemos entonces la salida el id del documento de referencia en un objeto en el valor que se devuelve podemos utilizar el parámetro de consulta 'include_docs' para incluir esos documentos en el mapa/reducir la producción:

{"rows":[ 
    {"key":["123412804910820", "post"], "value":null}, 
    {"key":["123412804910820", "author", "Lance1231"], "value":{"_id":"Lance1231"}}, 
    {"key":["123412804910820", "comment", "comment1"], "value":{"_id":"comment1"}}, 
    {"key":["123412804910820", "comment", "comment2"], "value":{"_id":"comment2"}} 
]} 

Solicitando ese mismo punto de vista con '? include_docs = true' agregará una clave 'doc' que usará el '_id' al que se hace referencia en el objeto 'value' o si eso no está presente en el objeto 'value', usará el '_id' de el documento desde el que se emitió la fila (en este caso, el documento 'post'). Tenga en cuenta que estos resultados incluirían un campo 'id' que hace referencia al documento fuente desde el que se realizó la emisión. Lo dejé por espacio y legibilidad.

entonces podemos utilizar el 'start_key' y los parámetros 'end_key' para filtrar los resultados a los datos de un único mensaje:

?start_key=["123412804910820"]&end_key=["123412804910820", {}, {}]
O incluso específicamente extraer la lista para un determinado tipo:
?start_key=["123412804910820", "comment"]&end_key=["123412804910820", "comment", {}]
Estas combinaciones consulta param son posibles gracias a un vacío objeto (" {}") siempre está en la parte inferior de la intercalación y nulo o "" siempre están en la parte superior.

La segunda adición útil de CouchDB en estas situaciones es la función _list. Esto le permitiría ejecutar los resultados anteriores a través de un sistema de plantillas de algún tipo (si desea HTML, XML, CSV o lo que sea), o generar una estructura JSON unificada si desea poder solicitar el contenido de una publicación completa (incluidos autor y datos de comentarios) con una única solicitud y se devuelve como un único documento JSON que coincide con lo que necesita su código de cliente/IU. Hacer eso le permitiría solicitar el documento de salida unificado de la publicación de esta manera:

/db/_design/app/_list/posts/unified??start_key=["123412804910820"]&end_key=["123412804910820", {}, {}]&include_docs=true
Su función _list (en este caso llamada "unificada") tomaría los resultados de la vista map/reduce (en este caso llamado "posts") y los ejecutaría a través de una función de JavaScript que devolvería la respuesta HTTP en el tipo de contenido que necesita (JSON, HTML, etc.).

Combinando estas cosas, puede dividir sus documentos en cualquier nivel que encuentre útiles y "seguros" para las actualizaciones, conflictos y replicaciones, y luego volver a armarlos cuando sea necesario cuando se soliciten.

Espero que ayude.

+2

No estoy seguro si esto ayudó a Lance, pero sé una cosa; ¡Definitivamente me ayudó mucho! ¡Esto es asombroso! – Mark