tutorial - Principios para modelar documentos de CouchDB
couchdb vs mongodb (4)
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 una decisión de alcance: los conflictos.
En el caso de que tenga comentarios como una propiedad de matriz 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 podría imaginar un escenario en una publicación de blog muy 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.
ASESOR: Como este artículo lo señala , también considere que cada vez que solicita / actualiza ese documento debe obtener / configurar el documento en su totalidad, por lo que debe pasar una gran cantidad de documentos que representan el sitio completo o una publicación con mucho de comentarios sobre esto puede convertirse en un problema que desearía evitar.
En el caso en que los mensajes se modelan por separado de los comentarios y dos personas envían un comentario sobre una historia, esos simplemente se convierten en dos documentos de "comentario" en ese DB, sin ningún problema de conflicto; solo dos operaciones PUT para agregar dos nuevos comentarios al db "comment".
Luego, para escribir los puntos de vista que le devuelven los comentarios de una publicación, debe ingresar el ID de publicación y luego emitir todos los comentarios que hacen referencia a la 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 documentos de forma un poco diferente, permitiendo que los índices se construyan en subelementos de un documento, por lo que es posible que vea la misma pregunta en la lista de correo de MongoDB y alguien diga "simplemente haga que los comentarios sean propiedad de la publicación principal".
Debido a la naturaleza de bloqueo de escritura y de maestro único de Mongo, la cuestión conflictiva de revisión de dos personas que agregaban comentarios no surgiría allí y la capacidad de consulta del contenido, como se mencionó, no se efectúa demasiado mal debido a índices
Dicho esto, si sus subelementos en cualquiera de los dos DB van a ser enormes (digamos 10s de miles de comentarios) creo que es la recomendación de ambos campos para hacer esos 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.
Tengo una pregunta que he tratado de responder desde hace un tiempo, pero no puedo entender:
¿Cómo se diseñan o dividen los documentos de CouchDB?
Tome una publicación de blog, por ejemplo.
La forma semi relacional de hacerlo sería crear algunos objetos:
- Enviar
- Usuario
- Comentario
- Etiqueta
- Retazo
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 publicaciones de blog que están disponibles le dan un ejemplo fácil de cómo hacer esto. Básicamente lo dividen de la misma manera, pero dicen que puede agregar propiedades ''arbitrarias'' a cada documento, lo que es definitivamente bueno. Entonces tendrías algo como esto en CouchDB:
- Publique (con etiquetas y fragmentos de "pseudo" modelos en el documento)
- Comentario
- Usuario
Algunas personas incluso dirían que podrías lanzar el comentario y el usuario allí, por lo que tendrías 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 simplemente poner todo 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"
...
}
}
}
}
}
Podrías hacer vistas fácilmente para encontrar lo que querías 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 objetos de valor, 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 comienza a parecerse más a 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, así 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>
Entonces las preguntas generales de CouchDB son:
- ¿Qué reglas / principios usas para dividir tus documentos (relaciones, etc.)?
- ¿Está bien colocar todo el sitio en un solo documento?
- Si es así, ¿cómo gestiona la serialización / deserialización de documentos con niveles de profundidades arbitrarias (como el ejemplo grande json anterior, o el ejemplo xml)?
- ¿O no los conviertes en VO, simplemente decides "estos están demasiado anidados al 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
Estudié los siguientes sitios / proyectos.
- Datos jerárquicos en CouchDB
- CouchDB Wiki
- Sofá - Aplicación CouchDB
- CouchDB La guía definitiva
- PeepCode CouchDB Screencast
- CouchRest
- CouchDB README
... pero todavía no han respondido esta pregunta.
El libro dice, si no recuerdo mal, desnormalizar hasta que "duela", teniendo en cuenta la frecuencia con la que sus documentos pueden actualizarse.
- ¿Qué reglas / principios usas para dividir tus documentos (relaciones, etc.)?
Como regla general, incluyo todos los datos que se necesitan para mostrar una página con respecto al 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. Por ejemplo, un documento de cotización de acciones incluiría el nombre de la empresa, 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.
- ¿Está bien colocar todo el sitio en un solo documento?
No, eso sería una tontería, porque:
- tendrías que leer y escribir todo el sitio (el documento) en cada actualización, y eso es muy ineficiente;
- no se beneficiaría de ningún almacenamiento en caché de vista.
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 métodos de modelado de "uniones" en 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 vivieron en un documento, entonces dos personas que intentan publicar un comentario al mismo tiempo (es decir, en contra de la misma revisión del documento) generarí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 empírica sería "desnormalizar hasta que duela", pero el punto donde "dolerá" es cuando tienes una alta probabilidad de que se publiquen varias ediciones en la misma revisión de un documento.
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 para 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.
El primero grande es ver la intercalación. 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 luego enviamos la identificación del documento referenciado a un objeto en el valor que se devuelve, podemos usar el parámetro de consulta ''include_docs'' para incluir esos documentos en el mapa / reducir el resultado:
{"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"}}
]}
Solicitar la misma 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'' del 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.
Luego podemos usar los parámetros ''start_key'' y ''end_key'' para filtrar los resultados a los datos de una sola publicación:
?start_key=["123412804910820"]&end_key=["123412804910820", {}, {}] O incluso extraer específicamente la lista para un cierto tipo:
?start_key=["123412804910820", "comment"]&end_key=["123412804910820", "comment", {}] Estas combinaciones de parámetros de consulta son posibles porque un objeto vacío (" {}
") está siempre 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 JavaScript que devolvería la respuesta HTTP en el tipo de contenido que Necesito (JSON, HTML, etc.)
Al combinar estas cosas, puede dividir sus documentos en cualquier nivel que considere útil y "seguro" para las actualizaciones, los conflictos y la replicación, y luego volver a armarlos cuando sea necesario cuando se soliciten.
Espero que ayude.