2011-08-09 11 views
6

Este es el trato. Supongamos que tenemos el siguiente esquema de datos en MongoDB:¿Cómo lidiar con las relaciones de muchos a muchos en MongoDB cuando la incrustación no es la respuesta?

  • items: una colección de grandes documentos que contienen algunos datos (que es absolutamente irrelevante lo que realmente es).
  • item_groups: una colección con documentos que contienen una lista de items._id llamada item_groups.items más algunos datos adicionales.

Por lo tanto, estos dos están vinculados con una relación de Muchos a Muchos. Pero hay una cosa complicada: por alguna razón no puedo almacenar elementos dentro de los grupos de artículos, así que, como dice el título, la incrustación no es la respuesta.

La consulta que realmente me preocupa tiene como objetivo encontrar algunos grupos en particular que contienen algunos elementos en particular (es decir, tengo un conjunto de criterios para cada colección). De hecho, también tiene que decir cuánto elementos dentro de cada grupo encontrado se ajustan a los criterios (ningún elemento significa que no se encuentra el grupo).

La única solución viable que se me ocurrió hasta aquí es utilizar un mapa/Reducir enfoque con un maniquí reducir la función:

function map() { 
    // imagine that item_criteria came from the scope. 
    // it's a mongodb query object. 
    item_criteria._id = {$in: this.items}; 
    var group_size = db.items.count(item_criteria); 
    // this group holds no relevant items, skip it 
    if (group_size == 0) return; 

    var key = this._id.str; 
    var value = {size: group_size, ...}; 

    emit(key, value); 
} 

function reduce (key, values) { 
    // since the map function emits each group just once, 
    // values will always be a list with length=1 
    return values[0]; 
} 

db.runCommand({ 
    mapreduce: item_groups, 
    map: map, 
    reduce: reduce, 
    query: item_groups_criteria, 
    scope: {item_criteria: item_criteria}, 
}); 

La línea problema es:

item_criteria._id = {$in: this.items}; 

¿Y si esto .items.length == 5000 o incluso más? Mi fondo RDBMS llora en voz alta:

SELECT ... FROM ... WHERE whatever_id IN (over 9000 comma-separated IDs) 

definitivamente no es una buena manera de ir.

¡Muchísimas gracias por su tiempo, muchachos!

espero que la mejor respuesta será algo así como "eres estúpido, dejar de pensar en el estilo de RDBMS, utilice $ its_a_kind_of_magicSphere de la última versión de MongoDB" :)

+0

Cuando se habla de "incrustación", ¿está hablando de DBReferences -> http://www.mongodb.org/display/DOCS/ Base de datos + referencias # DatabaseReferences-Javascript% 28mongoshell% 29? – DrColossos

+0

Estoy hablando de la incrustación real, es decir, el almacenamiento de un documento dentro de otro. p.ej. comment = {user: 'DrColossos', texto: '¿de qué estás hablando?'}; pregunta = {x: 13, y: 42, comentarios: [comentario]}. –

+0

Si crea una relación Muchos a Muchos con una tabla intermedia y consulta eso? ¿No resolverá eso tu problema? – Qqbt

Respuesta

1

Por qué no usar lo contrario diseño ?

Está almacenando elementos y grupos de elementos. Si su primera idea para almacenar artículos en item_group entradas entonces tal vez lo contrario no es una mala idea :-)

Me explico:

en cada elemento que almacena los grupos a los que pertenece. (Usted está en NOSql, la duplicación de datos está bien!) por ejemplo, digamos que almacena en las entradas de artículos una lista llamada grupos y sus elementos se ven como: {_id: .... , nombre: .... , grupos: [oBJECTID (...), oBJECTID (...), oBJECTID (...)]}

Entonces la idea de reducir el mapa toma una gran cantidad de energía:

map = function() { 
    this.groups.forEach(function(groupKey) { 
     emit(groupKey, new Array(this)) 
    } 
} 


reduce = function(key,values) { 
    return Array.concat(values); 
} 


db.runCommand({ 
    mapreduce : items, 
    map : map, 
    reduce : reduce, 
    query : {_id : {$in : [...,....,.....] }}//put here you item ids 
}) 

Puede agregar algunos parámetros (finalizar, por ejemplo, para modificar la salida del mapa reducir) pero esto podría ayudarlo.

Por supuesto, necesita tener otra colección donde almacene los detalles de item_groups si lo necesita, pero en algún caso (si esta información sobre item_groups no existe, o no cambia, o no lo hace cuidado de que no tenga la versión más actualizada) ¡no los necesita!

¿Eso le da una pista sobre la solución a su problema?

4

Creo que está luchando con la separación de modelado de dominio/objeto del modelado de esquema de base de datos. Yo también luché con esto al probar MongoDb.

En aras de la claridad y la semántica, voy a sustituir Groups con la palabra Categories

Esencialmente su modelo teórico es una relación "muchos a muchos" en la que cada Item puede pertenecer Categories, y cada Category puede poseer muchos Items.

Esto se maneja mejor en su modelado de objetos de dominio, no en el esquema de base de datos, especialmente cuando se implementa una base de datos de documentos (NoSQL). En su esquema de MongoDb "simula" una relación de "muchos a muchos", mediante el uso de una combinación de modelos de documentos de nivel superior y la incrustación.

incrustación es difícil de tragar para las personas que vienen de SQL persistencia back-ends, pero es una parte esencial de la respuesta. El truco es decidir si es o no es superficial o profunda, unidireccional o bidireccional, etc.


de Primer Nivel de modelos de documentos

Debido a sus Category documentos contienen algunos datos de su propio y están ampliamente referenciados por un gran número de Items, estoy de acuerdo con usted que insertarlos por completo dentro de cada Item es imprudente.

En su lugar, trate los objetos Item y Category como documentos de nivel superior. Asegúrese de que su esquema MongoDb asigna una tabla para cada uno de manera que cada documento tenga su propio ObjectId.

El siguiente paso es decidir dónde y cuánto para incrustar ... no hay una respuesta correcta, ya que todo depende de cómo lo usa y cuáles son sus ambiciones de escala son ...

decisiones Incorporación de

1. los productos

Como mínimo, sus Item objetos deben tener una propiedad de colección para sus categorías. Por lo menos, esta colección debe contener ObjectId para cada Category.

Mi sugerencia sería que añadir a esta colección, los datos que se utiliza en la interacción con la mayoría de las veces Item ...

Por ejemplo, si quiero enumerar un montón de artículos en mi página web en una rejilla, y muestra los nombres de las categorías de las que forman parte.Es obvio que no necesito saber todo sobre el Category, pero si solo tengo el ObjectId incrustado, una segunda consulta sería necesaria para obtener algún detalle al respecto.

En cambio, lo que haría más sentido es integrar Name la propiedad de la Categoría en la colección junto con el ObjectId, de manera que tirando hacia atrás una Item ahora puede mostrar sus nombres de las categorías y sin otra consulta.

Lo más importante a recordar es que los objetos clave/valor incrustados en su Item que "representar" un Category no tienen que coincidir con el verdadero modelo de documento Category ... No es oop o modelado de base de datos relacional.

2. Categorías

a la inversa puede optar por dejar la incrustación de una sola vía, y no tener ninguna información en sus ItemCategory documentos ... o podría optar por añadir una colección de datos del ítem mucho como la de arriba (ObjectId, o ObjectId + Name) ...

En este sentido, me gustaría personalmente inclinarse hacia tener nada incrustado ... más que probable que si quiero Item información para mi categoría, quiero porciones de él, más que solo un nombre ... y una profunda incrustación de un docume de alto nivel nt (Artículo) no tiene sentido. Simplemente me resignaría a consultar la base de datos de una colección de Artículos donde cada uno poseía el ObjectId de mi Categoría en su colección de Categorías.

Phew ... confuso para seguro. El punto es que tendrá tiene alguna duplicación de datos y tendrá que ajustar sus modelos a su uso para un mejor rendimiento. La buena noticia es que eso es lo que MongoDb y otras bases de datos de documentos son buenas en ...

Cuestiones relacionadas