Mangosta poblar vs objeto anidación

¿Hay alguna diferencia de rendimiento (tiempo de proceso de consulta) entre el uso de la población Mongoose y la inclusión directa de objetos? ¿Cuándo se debe usar cada uno?

Ejemplo de población de mongooses:

var personSchema = Schema({ _id : Number, name : String, stories : [{ type: Schema.Types.ObjectId, ref: 'Story' }] }); var storySchema = Schema({ _creator : { type: Number, ref: 'Person' }, title : String, }); 

Ejemplo de anidación de objeto de mongoose:

 var personSchema = Schema({ _id : Number, name : String, stories : [storySchema] }); var storySchema = Schema({ _creator : personSchema, title : String, }); 

Lo primero que debe entender acerca de la población de la mongoose es que no es magia, sino solo un método de conveniencia que le permite recuperar información relacionada sin hacerlo todo usted mismo.

El concepto es esencialmente para usarlo cuando decide que va a necesitar colocar los datos en una colección separada en lugar de incorporar esos datos, y sus consideraciones principales deberían ser típicamente sobre el tamaño del documento o si esa información relacionada está sujeta a actualizaciones frecuentes que harían manteniendo los datos incrustados difíciles de manejar.

La parte “no mágica” es que, esencialmente, lo que ocurre debajo de las coberturas es que cuando “referencia” a otra fuente, la función populate hace una consulta / consultas adicionales a esa colección “relacionada” para “fusionar” los resultados del padre objeto que has recuperado. Puede hacerlo usted mismo, pero el método está allí para facilitar la tarea. La consideración obvia de “rendimiento” es que no hay un solo viaje de ida y vuelta a la base de datos (instancia de MongoDB) para recuperar toda la información. Siempre hay más de uno.

Como muestra, tome dos colecciones:

 { "_id": ObjectId("5392fea00ff066b7d533a765"), "customerName": "Bill", "items": [ ObjectId("5392fee10ff066b7d533a766"), ObjectId("5392fefe0ff066b7d533a767") ] } 

Y los artículos:

 { "_id": ObjectId("5392fee10ff066b7d533a766"), "prod": "ABC", "qty": 1 } { "_id": ObjectId("5392fefe0ff066b7d533a767"), "prod": "XYZ", "qty": 2 } 

Lo “mejor” que se puede hacer con un modelo “referenciado” o el uso de populate (debajo del capó) es este:

 var order = db.orders.findOne({ "_id": ObjectId("5392fea00ff066b7d533a765") }); order.items = db.items.find({ "_id": { "$in": order.items } ).toArray(); 

Entonces, claramente hay “al menos” dos consultas y operaciones para “unir” esos datos.

El concepto de incrustación es esencialmente la respuesta de MongoDB a cómo lidiar con el no apoyo de “uniones” 1 . Para que, en lugar de dividir los datos en colecciones normalizadas, intente incrustar los datos “relacionados” directamente en el documento que lo utiliza. Las ventajas aquí son que hay una sola operación de “lectura” para recuperar la información “relacionada”, y también un único punto de operaciones de “escritura” para actualizar las entradas “padre” e “hijo”, aunque a menudo no es posible escribir en “muchos” niños a la vez sin procesar “listas” en el cliente o aceptando de otro modo operaciones de escritura “múltiples”, y preferiblemente en procesamiento “por lotes”.

Entonces, los datos se parecen más a esto (en comparación con el ejemplo anterior):

 { "_id": ObjectId("5392fea00ff066b7d533a765"), "customerName": "Bill", "items": [ { "_id": ObjectId("5392fee10ff066b7d533a766"), "prod": "ABC", "qty": 1 }, { "_id": ObjectId("5392fefe0ff066b7d533a767"), "prod": "XYZ", "qty": 2 } ] } 

Por lo tanto, buscar los datos es solo una cuestión de:

 db.orders.findOne({ "_id": ObjectId("5392fea00ff066b7d533a765") }); 

Los pros y los contras de cualquiera siempre dependerán en gran medida del patrón de uso de su aplicación. Pero de un vistazo:

Incrustar

  • El tamaño total del documento con datos incrustados generalmente no excederá los 16 MB de almacenamiento (el límite BSON) o de lo contrario (como una guía) tienen arreglos que contienen 500 o más entradas.

  • Los datos que están integrados generalmente no requieren cambios frecuentes. Por lo tanto, podría vivir con la “duplicación” que proviene de la des-normalización y no la necesidad de actualizar esos “duplicados” con la misma información en muchos documentos principales solo para invocar un cambio.

  • Los datos relacionados se usan frecuentemente en asociación con el padre. Lo que significa que si sus casos de “lectura / escritura” casi siempre necesitan “leer / escribir” tanto para padres como para niños, entonces tiene sentido incorporar los datos a las operaciones atómicas.

Referenciando

  • Los datos relacionados siempre superarán el límite de BSM de 16 MB. Siempre se puede considerar un enfoque híbrido de “agrupamiento”, pero el límite duro general del documento principal no se puede violar. Los casos comunes son “publicaciones” y “comentarios” donde se espera que la actividad de “comentarios” sea muy grande.

  • Los datos relacionados necesitan una actualización periódica. O esencialmente el caso donde “normalizas” porque esos datos son “compartidos” entre muchos padres y los datos “relacionados” cambian con la frecuencia suficiente como para que no sea práctico actualizar los elementos incrustados en cada “padre” donde se produce ese elemento “hijo” . El caso más fácil es simplemente hacer referencia al “niño” y hacer el cambio una vez.

  • Hay una clara separación de lecturas y escrituras. En el caso en que tal vez no siempre va a requerir esa información “relacionada” al leer el “padre” o de lo contrario no necesita modificar siempre el “padre” cuando escribe al niño, podría haber una buena razón para separar el modelo como se hace referencia. Además, si hay un deseo general de actualizar muchos “subdocumentos” a la vez en los que esos “subdocumentos” son realmente referencias a otra colección, con frecuencia la implementación es más eficiente cuando los datos están separados. colección.

Así que en realidad hay una discusión mucho más amplia de los “pros / contras” para cualquier posición en la documentación de MongoDB sobre Modelado de Datos , que cubre varios casos de uso y formas de abordar, ya sea mediante el uso de incrustación o el modelo referenciado como es compatible con el método populate.

Afortunadamente los “puntos puntos” son útiles, pero la recomendación general es considerar los patrones de uso de datos de su aplicación y elegir lo mejor. Tener la “opción” de insertar “debería” ser la razón por la que ha elegido MongoDB, pero en realidad será la forma en que su aplicación “utiliza los datos” que toma la decisión de qué método se adecua a qué parte de su modelo de datos (ya que no “todo o nada”) lo mejor.

  1. Tenga en cuenta que, dado que esto fue escrito originalmente, MongoDB introdujo el operador de $lookup que efectivamente realiza “uniones” entre colecciones en el servidor. A los efectos de la discusión general aquí, whist “mejor” en la mayoría de las circunstancias que la sobrecarga de “consulta múltiple” incurrida por populate() y “consultas múltiples” en general, todavía hay una “sobrecarga significativa” incurrida con cualquier operación $lookup .

El principio de diseño central es “incrustado” significa “ya está allí” en lugar de “ir a buscar a otro lado”. Esencialmente, la diferencia entre “en su bolsillo” y “en el estante”, y en términos de E / S generalmente es más parecida a “en el estante en el centro de la biblioteca” , y notablemente más lejos para las solicitudes basadas en red.