mongodb - ejemplos - ¿Relaciones en la base de datos orientada a documentos?
base de datos no relacional mongodb (6)
El diseño del esquema en las bases de datos orientadas a documentos puede parecer difícil al principio, pero al crear mi inicio con Symfony2 y MongoDB, descubrí que el 80% del tiempo es igual que con una base de datos relacional.
Al principio, piense como una db normal:
Para comenzar, simplemente cree su esquema como lo haría con un Db relacional:
Cada Entity
debe tener su propia Collection
, especialmente si necesita paginar los documentos en ella .
(En Mongo, puedes unir páginas de arreglos de documentos anidados, pero las capacidades son limitadas)
Luego simplemente elimine la normalización demasiado complicada
- ¿Necesito una tabla de categorías separada? (simplemente escriba la categoría en una columna / propiedad como una cadena o documento incrustado)
- ¿Puedo almacenar comentarios que cuenten directamente como un Int en la colección de autores? (luego actualice el conteo con un evento, por ejemplo en Doctrine ODM)
Documentos incrustados:
Utilice documentos incrustados solo para:
- claridad (documentos anidados como:
addressInfo
,billingInfo
en la colección de usuarios) - para almacenar etiquetas / categorías (por ejemplo:
[ name: "Sport", parent: "Hobby", page: "/sport" ]
) - para almacenar múltiples valores simples (por ejemplo, en
User
colección deUser
: lista de especialidades, lista de sitios web personales)
No los uses cuando:
- el documento padre crecerá demasiado grande
- cuando necesitas paginarlos
- Cuando sientas que la entidad es lo suficientemente importante como para merecer su propia colección.
Duplicar valores en recuentos de recopilación y precalculado:
Duplique algunos valores de columnas / atributos de una Colección a otra si necesita hacer una consulta con cada valor en las condiciones de donde. (recuerda que no hay join
)
Por ejemplo: en la colección de tickets ponga también el nombre del autor (no solo el ID
)
Además, si necesita un contador (número de tickets abiertos por usuario, por categoría, ecc), precáltelos.
Insertar referencias:
Cuando tenga una referencia de uno a muchos o de muchos a muchos, use una matriz incrustada con la lista de los identificadores de documentos a los que se hace referencia (consulte la referencia de la base de datos de MongoDB ).
Deberá usar un evento nuevamente para eliminar una identificación si se elimina el documento al que se hace referencia. (Hay una extensión para Doctrine ODM si la usa: Integridad de referencia )
Doctrine ODM gestiona directamente este tipo de referencias: muchas de referencia
Es fácil arreglar errores:
Si encuentra tarde que ha cometido un error en el diseño del esquema, es muy simple solucionarlo con algunas líneas de Javascript para ejecutar directamente en la consola Mongo.
(Los procedimientos almacenados son fáciles: no se necesitan scripts de migración complejos)
Waring: no use Doctrine ODM Migrations , lo lamentará más adelante.
Me interesan las bases de datos orientadas a documentos, y me gustaría jugar con MongoDB. Así que empecé un proyecto bastante simple (un rastreador de problemas), pero estoy teniendo dificultades para pensar de una manera no relacional.
Mis problemas:
Tengo dos objetos relacionados entre sí (p. Ej.,
issue = {code:"asdf-11", title:"asdf", reporter:{username:"qwer", role:"manager"}}
- aquí tengo un usuario relacionado con el tema). ¿Debo crear otro documento ''usuario'' y hacer referencia a él en el documento ''problema'' por su id (como en las bases de datos relacionales), o debo dejar todos los datos del usuario en el subdocumento?Si tengo objetos (subdocumentos) en un documento, ¿puedo actualizarlos todos en una sola consulta?
En mi opinión esto es en realidad bastante simple. Solo se puede acceder a los documentos incrustados a través de su documento maestro. Si puede imaginar la necesidad de consultar un objeto fuera del contexto del documento maestro, no lo incruste. Utilice una referencia.
Por tu ejemplo
issue = {code:"asdf-11", title:"asdf", reporter:{username:"qwer", role:"manager"}}
Haría la publicación y el informe de cada uno de sus propios documentos, y haría referencia al informador en el tema. También puede hacer referencia a una lista de problemas en el reportero. De esta manera, no se duplicarán los reporteros en los problemas, puede consultarlos cada uno por separado, puede consultar al reportero por problema y puede consultar los problemas por reportero. Si incrusta el reportero en cuestión, solo puede consultar de una manera, reportero por problema.
Si incrusta documentos, puede actualizarlos todos en una sola consulta, pero debe repetir la actualización en cada documento maestro. Esta es otra buena razón para usar documentos de referencia.
La belleza de mongodb y otro producto "NoSQL" es que no hay ningún esquema para diseñar. Utilizo MongoDB y me encanta, ¡no tengo que escribir consultas de SQL y consultas horribles de ÚNETE! Así que para responder a sus dos preguntas.
1 - Si crea varios documentos, necesitará hacer dos llamadas a la base de datos. No digo que sea algo malo, pero si puede incluir todo en un solo documento, ¿por qué no? Recuerdo que cuando solía usar MySQL, creaba una tabla de "blog" y una tabla de "comentarios". Ahora, agrego los comentarios al registro en la misma colección (también conocida como tabla) y sigo construyendo sobre él.
2 - si ...
Me gusta MongoDB, pero debo decir que lo usaré mucho más sobrio en mi próximo proyecto.
Específicamente, no he tenido tanta suerte con la instalación de documentos incrustados como la gente promete.
El documento incrustado parece ser útil para la composición (consulte Composición UML), pero no para la agregación. Los nodos de la hoja son excelentes, cualquier cosa en el centro de su gráfico de objetos no debe ser un documento incrustado. Hará que la búsqueda y la validación de sus datos sean más una lucha de la que desearía.
Una cosa que es absolutamente mejor en MongoDB son tus relaciones de muchos a X. Puede hacer de muchos a muchos con solo dos tablas, y es posible representar una relación de muchos a uno en cada tabla. Es decir, puede poner 1 tecla en N filas, o N teclas en 1 fila, o ambas. En particular, las consultas para realizar operaciones de conjunto (intersección, unión, conjunto disjunto, etc.) son realmente comprensibles por sus compañeros de trabajo. Nunca he estado satisfecho con estas consultas en SQL. A menudo tengo que conformarme con que "otras dos personas entenderán esto".
Si alguna vez sus datos se han vuelto muy grandes, sabrá que las inserciones y actualizaciones pueden verse limitadas por el costo de los índices. Necesita menos índices en MongoDB; se puede utilizar un índice en ABC para consultar A, A y B, o A & B y C (pero no B, C, B y C o A y C). Además, la capacidad de invertir una relación le permite mover algunos índices a tablas secundarias. Mis datos no se han vuelto lo suficientemente grandes como para intentarlo, pero espero que eso ayude.
Soy totalmente nuevo en las bases de datos orientadas a documentos, y en este momento estoy tratando de desarrollar una especie de CMS utilizando node.js y mongodb, por lo que me enfrento a los mismos problemas que usted.
Por prueba y error encontré esta regla de oro: hago una recopilación para cada entidad que pueda ser un "tema" para mis consultas, mientras incrusté el resto dentro de otros objetos.
Por ejemplo, los comentarios en una entrada de blog se pueden insertar, porque generalmente están vinculados a la entrada en sí y no puedo pensar en una consulta útil realizada globalmente en todos los comentarios. Por otro lado, las etiquetas adjuntas a una publicación pueden merecer su propia colección, ya que incluso si están vinculadas a la publicación, es posible que desee razonar globalmente sobre todas las etiquetas (por ejemplo, hacer una lista de temas de tendencias).
Vuelva a hacer esta respuesta ya que la respuesta original tomó la relación al revés debido a la lectura incorrecta.
problema = {código: "asdf-11", título: "asdf", reportero: {nombre de usuario: "qwer", rol: "administrador"}}
En cuanto a si la incorporación de cierta información importante sobre el usuario (creador) del ticket es una decisión acertada o no, depende de las características específicas del sistema.
¿Le está dando a estos usuarios la capacidad de iniciar sesión y reportar problemas que encuentren? Si es así, es probable que desees factorizar esa relación con una colección de usuarios.
Por otro lado, si ese no es el caso, entonces podría salirse fácilmente con este esquema. El único problema que veo aquí es si desea ponerse en contacto con el reportero y su función laboral ha cambiado, eso es un poco incómodo; Sin embargo, ese es un dilema del mundo real, no uno para la base de datos.
Ya que el subdocumento representa una relación de uno a uno con un periodista, tampoco debería sufrir los problemas de fragmentación mencionados en mi respuesta original.
Hay un problema evidente con este esquema y es la duplicación de los datos de repetición cambiantes (materia de formulario normalizado).
Tomemos un ejemplo. Imagínate que te topas con el dilema del mundo real del que hablé antes y un usuario llamado Nigel
quiere que su rol refleje su nuevo puesto de trabajo de ahora en adelante. Esto significa que tiene que actualizar todas las filas donde Nigel
es el reportero y cambiar su role
a esa nueva posición. Esta puede ser una consulta larga y que consume muchos recursos para MongoDB.
Para contradecirme otra vez, si tuviera solo 100 tickets (por ejemplo, algo manejable) por usuario, entonces la operación de actualización probablemente no sería tan mala y, de hecho, sería manejable para la base de datos con bastante facilidad; Además, debido a la falta de movimiento (con suerte) de los documentos, esta sería una actualización completamente implementada.
Entonces, si esto debería ser incorporado o no depende en gran medida de sus consultas y documentos, etc., sin embargo, diría que este esquema no es una buena idea; específicamente debido a la duplicación de datos cambiantes en muchos documentos raíz. Técnicamente, sí, podría salirse con la suya, pero no lo intentaría.
En su lugar, me gustaría dividir los dos.
Si tengo objetos (subdocumentos) en un documento, ¿puedo actualizarlos todos en una sola consulta?
Al igual que el estilo de relación en mi respuesta original, sí y fácilmente.
Por ejemplo, actualicemos el rol de Nigel
a MD
(como se indicó anteriormente) y cambiemos el estado del ticket a completado:
db.tickets.update({''reporter.username'':''Nigel''},{$set:{''reporter.role'':''MD'', status: ''completed''}})
Por lo tanto, un solo esquema de documento facilita CRUD en este caso.
Una cosa a tener en cuenta, que proviene de su inglés, no puede usar el operador posicional para actualizar todos los subdocumentos en un documento raíz. En su lugar se actualizará solo el primero encontrado
De nuevo, espero que tenga sentido y no he dejado de lado nada. HTH
Respuesta original
Aquí tengo un usuario relacionado con el tema). ¿Debo crear otro documento ''usuario'' y hacer referencia a él en el documento ''problema'' por su id (como en las bases de datos relacionales), o debo dejar todos los datos del usuario en el subdocumento?
Esta es una pregunta considerable y requiere algunos conocimientos previos antes de continuar.
Lo primero a considerar es el tamaño de un problema:
issue = {code:"asdf-11", title:"asdf", reporter:{username:"qwer", role:"manager"}}
No es muy grande y, como ya no necesita la información del reporter
(que estaría en el documento raíz), podría ser menor, sin embargo, los problemas nunca son tan simples. Si echa un vistazo a la JIRA de MongoDB, por ejemplo: https://jira.mongodb.org/browse/SERVER-9548 (como una página aleatoria que demuestra mi punto), el contenido de un "ticket" puede ser bastante considerable.
La única forma en que obtendría un verdadero beneficio al incrustar los boletos sería si pudiera almacenar TODA la información del usuario en un solo bloque de 16 MB de almacenamiento contigioso, que es el tamaño máximo de un documento BSON (según lo impone el mongod
actualmente).
No creo que puedas almacenar todos los boletos bajo un solo usuario.
Incluso si tuviera que reducir el boleto a, tal vez, un código, título y una descripción, todavía podría sufrir el problema del "queso suizo" causado por las actualizaciones periódicas y los cambios en los documentos en MongoDB, como siempre: http://www.10gen.com/presentations/storage-engine-internals es una buena referencia para lo que quiero decir.
Por lo general, sería testigo de este problema ya que los usuarios agregan varios tickets a su documento de usuario raíz. Los boletos en sí también cambiarán, pero tal vez no de manera drástica o frecuente.
Por supuesto, puede remediar un poco este problema usando una asignación de potencia de 2 tamaños: http://docs.mongodb.org/manual/reference/command/collMod/#usePowerOf2Sizes que hará exactamente lo que dice en la lata.
De acuerdo, hipotéticamente, si solo tuviera un code
y un title
entonces sí, podría almacenar los tickets como subdocumentos en el usuario raíz sin demasiados problemas, sin embargo, esto es algo que se reduce a detalles que el cesionario de la recompensa no ha mencionado.
Si tengo objetos (subdocumentos) en un documento, ¿puedo actualizarlos todos en una sola consulta?
Sí, con bastante facilidad. Esto es una cosa que se vuelve más fácil con la incrustación. Podrías usar una consulta como:
db.users.update({user_id:uid,''tickets.code'':''asdf-1''}, {$set:{''tickets.$.title'':''Oh NOES''}})
Sin embargo, para tener en cuenta, solo puede actualizar UN subdocumento a la vez utilizando el operador posicional. Como tal, esto significa que no puede, en una sola operación atómica, actualizar todas las fechas de los boletos en un solo usuario a 5 días en el futuro.
En cuanto a agregar un nuevo ticket, eso es bastante simple:
db.users.update({user_id:uid},{$push:{tickets:{code:asdf-1,title:"Whoop"}}})
Entonces, sí, puede, simplemente, dependiendo de sus consultas, actualizar los datos completos de los usuarios en una sola llamada.
Esa fue una respuesta bastante larga, así que espero que no me haya perdido nada, espero que ayude.