2011-07-07 10 views
5

versión corta:consulta MongoDB utilizando el documento incrustado como clave

Si tengo un índice {"category": 1}, y un documento {"category": {type: "memory", class: "DDR400"}, ¿cómo puedo hacer una consulta como {"category.type": "memory"} que utiliza mi índice?

Versión larga:

Con MongoDB, quiero usar un documento incrustado como una clave para un índice.

Por ejemplo, podría haber algunos documentos como este (para una hipotética base de datos del producto):

{"category": {"type": "hard-drive", "form_factor": "2.5in", "size": "500GB"}, ...} 
{"category": {"type": "hard-drive", "form_factor": "3.5in", ...}, ...} 
{"category": {"type": "memory", "class": "DDR400", ...}, ...} 

Para los ejemplos anteriores, puede ser que desee hacer preguntas tales como:

{"category.type": "hard-drive"} 
{"category.type": "hard-drive", "category.form_factor": "2.5in"} 
{"category.type": "memory"} 
{"category.type": "memory", "category.class": "DDR400"} 

Mis problemas es crear un índice. El documento en http://www.mongodb.org/display/DOCS/Indexes#Indexes-DocumentsasKeys describe dos opciones:

La primera opción es crear un índice compuesto, por ejemplo { "category.type": 1, "category.class": 1 }. Esto no funciona bien para mi caso, ya que podría tener muchos tipos diferentes de subcategorías.

La segunda opción es utilizar el documento como la clave: { "category": 1 }. Ahora una consulta como {"category": {"type": "memory", "class": "DDR400"}} usaría el índice, pero {"category": {"type": "memory"}} no devolvería nada, y {"category.type": "memory"} no usaría el índice. ¿Hay alguna manera de hacer una consulta usando este índice que daría los mismos resultados que {"category.type": "memory"}?

Sospecho que una consulta usando algo como {"category" {"$gt": ..., "$lt": ...} debería funcionar, pero ¿qué debería poner en los espacios en blanco?

Respuesta

3

Crear un índice separado para category.type (probablemente además de category) parece ser la mejor opción.

Puede usar una consulta de rango con $gt y $lt. Esos trabajarían en la representación binaria del objeto incrustado, que solo funciona para el primer campo (en orden de almacenamiento), y solo si ese primer campo es el mismo en todos los documentos, por lo que no es muy flexible y fácil de romper.

{"category" : {"$gt": {"type": "memory"}, "$lt": {"type": "memoryX" } } } 

"memoryX" aquí sirve como punto de corte: Todo con "memoria" clasificará antes de eso.

Tenga en cuenta que esto requiere que el campo "tipo" sea el primero en la representación binaria para todos los documentos que lo tienen. También SÓLO funciona para el campo "tipo" (no hay forma de consultar en otros campos en la primera posición, tiene que elegir uno por adelantado), por lo que no le da prácticamente ninguna ventaja sobre un índice dedicado "categoría-tipo" (solo espacio ahorros).

Estaba experimentando con esta idea antes, vea this thread on the mailing list. Funciona, pero debe tener cuidado con lo que está haciendo:

Es compatible y estable. Muchas de las partes internas de fragmentación/replicación usan _id values ​​que son documentos incrustados.

Lo único a tener en cuenta aquí es el orden de las claves en el elemento incrustado . Se ordenan por su representación binaria, por lo que {x: 1, y: 1} es diferente de {y: 1, x: 1} y se ordena de forma diferente. No solo se clasifican de manera diferente, son valores diferentes. Algunos idiomas siempre ordenan las teclas en un diccionario/hash/mapa de forma predeterminada.

De nuevo, considere crear índices adicionales en los campos que necesita.

En mi caso, solo tendré que consultar en 'a', 'a, b' o 'a, b, c', o en 'a, x, y', donde los documentos que contengan x nunca contiene 'b' o 'c'

Eso probablemente funcione entonces. Todavía haría dos índices compuestos a,b y a,x, sin embargo. O tal vez solo b y x. Dado que un documento contiene b o x, probablemente ya haya filtrado efectivamente los documentos irrelevantes con respecto a a (form_factor = 2.5in ya le dice que es un disco duro, class = DDR400 ya lo hace memoria). Y después de filtrar por a,b, es posible que no necesite un índice para profundizar más en c.

Al usar esta complicada consulta en la representación binaria, se está haciendo dependiente de lo que podría llamarse un detalle de implementación. Usted puede ser golpeado por los conductores que les gusta volver a ordenar los campos, o algo así como this issue sobre Mongo sí mismo reorganizar las cosas a veces.

+0

Mi problema es que, posiblemente, podría tener muchas llaves diferentes dentro de la categoría, y no puedo crear un índice en cada uno de ellos. Siempre consultaba las teclas en el documento incrustado de izquierda a derecha (así que nunca solo las últimas claves). – Ralf

+0

Su solución con $ gt y $ lt parece funcionar, pero ¿en qué casos podría fallar? – Ralf

+0

Solo necesita crear índices en las teclas que va a consultar. Preguntar "en las teclas de izquierda a derecha" probablemente tampoco funcione demasiado bien si puede haber muchas combinaciones. Considere {a: 1, b: 1, c: 1} frente a {a: 1, c: 1} frente a {b: 1, c: 1}. No puede consultar todas las combinaciones de a, b, c con estos documentos. – Thilo

2

Si hay una propiedad básica que usted está buscando para cada "tipo", a continuación, simplemente añada también es como un campo separado, y crear un índice compuesto, por ejemplo:

{"category": {"type": "hard-drive", "form_factor": "2.5in", "searchfield: "2.5in", ...}, ...} 
{"category": {"type": "memory", "class": "DDR400", searchfield: "DDR400", ...}, ...} 

Si hay varios campos que se está buscando, pero los valores de estos campos son diferentes, se podría añadir los valores como etiquetas y, de nuevo, crear una clave compuesta:

{"category": {"type": "hard-drive", "form_factor": "2.5in", "size": "500GB", "tags": ["2.5in", "500GB"]}, ...} 
{"category": {"type": "memory", "class": "DDR400", "tags": ["DDR400"], ...}, ...} 
+0

Me gusta esta solución. Incluso podría colocar todo como etiquetas: {"tags": ["disco duro", "2.5in "," 500GB "]}, {" tags ": [" memory "," DDR400 "]}, o anteponga la etiqueta" type "si puede haber conflictos, por ejemplo" memory-DDR400 ". El operador $ all entonces funcionaré para mis consultas si necesito buscar varias etiquetas. – Ralf

+0

Acepté la respuesta de Thilo, ya que responde a mi pregunta original y la explica bien. Sin embargo, probablemente terminaré usando esta solución. – Ralf

+0

+1. Bueno, sobre todo porque puedes usar $ todo. Buen ejemplo de desnormalización de los datos para que sea fácil de consultar. – Thilo

Cuestiones relacionadas