Este post es una continuación (Intentando crear un esquema de datos para MongoDB)
Uno de los contras de crear el esquema con documentos embebidos es hacer las consultas. En este post he ido realizando distintas pruebas con toda la información que he ido encontrando. Practicando y equivocándome es como mejor aprendo.
Lo que más me ha costado ha sido recoger un valor de un subdocumento pasándole una variable como nombre de la clave. Pero finalmente lo he conseguido y ahí va...
Por lo pronto abro mi shell de mongo y voy a mirar como quedaron las colecciones:
> var a = db.articulos;
> var m = db.mesas;
> var h = db.historial;
> a.find();
{ "_id" : 1, "familia" : "Carnes", "nombre" : "Churrasco", "precio" : 8.6 }
{ "_id" : 2, "familia" : "Carnes", "nombre" : "Kobe", "precio" : 8.6 }
{ "_id" : 3, "familia" : "Carnes", "nombre" : "Solomillo de Ternera", "precio" :
8.6 }
{ "_id" : 4, "familia" : "Pescado", "nombre" : "Dorada", "precio" : 8.6 }
{ "_id" : 5, "familia" : "Pescado", "nombre" : "Urta", "precio" : 8.6 }
> m.find();
{ "_id" : 1, "nombre" : "1", "zona" : "Salon" }
{ "_id" : 2, "nombre" : "2", "zona" : "Salon" }
{ "_id" : 3, "nombre" : "3", "zona" : "Salon" }
{ "_id" : 4, "nombre" : "4", "zona" : "Terraza" }
{ "_id" : 5, "nombre" : "5", "zona" : "Terraza" }
{ "_id" : 6, "nombre" : "6", "zona" : "Terraza" }
> h.find();
{ "_id" : 2, "fecha" : ISODate("1970-01-01T00:00:00.050Z"), "mesa" : "4", "descrip" : [ { "linea" : 1, "articulo" : "Churrasco", "precio" : 8.6 }, { "linea" : 2, "articulo" : "Kobe", "precio" : 8.6 }
] }
{ "_id" : 3, "descrip" : [ { "linea" : 1, "articulo" : "Churrasco", "precio" : 8.6 }, { "linea" : 2, "articulo" : "Kobe","precio" : 8.6 } ], "fecha" : ISODate("2013-04-10T18:27:15.796Z"), "mesa" : "4"
}
Intentando ampliar el esquema voy a insertar una clave 'visores' dentro de 'historial'. La clave 'visores' servirá para indicar a la la línea de descripción por el visor que debe mostrarse (en cocina o barra por ejemplo).
Para el ejemplo y para comprobar que la sintaxis es la correcta, voy a crear en todas las lineas de pedido la clave 'visores' con los visores cocina y parrilla.
> h.update({},{'$set':{descrip:[{visores:['cocina', 'parrilla']}]}},false, true);
A ver cómo ha quedado:
> h.findOne();
{
"_id" : 2,
"descrip" : [
{
"visores" : [
"cocina",
"parrilla"
]
}
],
"fecha" : ISODate("1970-01-01T00:00:00.050Z"),
"mesa" : "4"
}
Vaya cagada... He machacado el resto de las claves dentro de la clave 'descrip'. Se me han borrado al hacer '$set'??
Voy a añadírselas de nuevo, pero lo haré con '$push':
> h.update({},{'$push':{descrip:[{linea:"", articulo:"", precio: 0}]}},false,true);
> h.findOne();
{
"_id" : 2,
"descrip" : [
{
"visores" : [
"cocina",
"parrilla"
]
},
[
{
"linea" : "",
"articulo" : "",
"precio" : 0
}
]
],
"fecha" : ISODate("1970-01-01T00:00:00.050Z"),
"mesa" : "4"
De nuevo la estoy liando... 'visores' debe apareer en el mismo array que linea, articulo y precio. Buena oportunidad para revisar como se borran las claves.
Al igual que con $set inserto, lo intentaré sacar de ahí con $unset:
> h.update({},{$unset:{descrip:{visores:1}}},false,true);
> h.find();
{ "_id" : 2, "fecha" : ISODate("1970-01-01T00:00:00.050Z"), "mesa" : "4" }
{ "_id" : 3, "fecha" : ISODate("2013-04-10T18:27:15.796Z"), "mesa" : "4" }
Bueno, la he vuelto a liar... me he cargado toda la clave 'descrip' :(
Pues a crearla de nuevo, para eso estoy aqui:
> h.update({},{$set:{descrip:{linea:"", articulo:"", precio:0}}},false,true);
> h.findOne();
{
"_id" : 2,
"descrip" : {
"linea" : "",
"articulo" : "",
"precio" : 0
},
"fecha" : ISODate("1970-01-01T00:00:00.050Z"),
"mesa" : "4"
}
A ver si ahora soy capaz de hacerlo en condiciones (no se que me pasa con los corchetes, me gustan :)
> h.update({},{$set:{descrip:{visores:['cocina','parrilla']}}},false,true);
> h.findOne();
{
"_id" : 2,
"descrip" : {
"visores" : [
"cocina",
"parrilla"
]
},
"fecha" : ISODate("1970-01-01T00:00:00.050Z"),
"mesa" : "4"
}
Pues no, la vuelvo a liar... Pero de nuevo tengo la oportunidad de borrar sólo los visores de la descrip:
> h.remove({},{descrip:{visores:1}},false,true);
> h.findOne();
null
Ea! Ya la lié del todo... he mandado los documentos de 'historial' a tomar fanta...
Allá vamos de nuevo, podría crear el documento de nuevo con todo lo que necesito, pero prefiero probar con las operaciones de quita y pon clave aquí y allí para tenerlo todo muy claro y no toparme con las sorpresas que voy encontrándome (sobre todo por mi ignorancia sobre el tema)
> h.insert({_id:1, fecha:new Date(), mesa:'5'});
Ahora, las lineas de descripcion:
>h.update({},{$set:{descrip:{linea:"1", articulo:'Churrasco', precio:8.60}}},false,true);
El turno de actualizar 'descrip':
> h.update({},{$set:{descrip:{visores:['cocina', 'parrilla']}}},true,true);
Nada.... me vuelve a borrar el resto de claves en descrip... Estoy buscando pero no encuentro nada... o no se buscarlo... Casi todo apunta a que debo crearlo desde el principio, pero no creo que sea así... Buscaré más o mejor.
Por fín lo encuentro... revisando documentación he visto que acceden a las claves de un documento embebido de esta forma:
clave.subclave
Separando por un punto puedo acceder a mi subclave 'descrip' para insertar sin borrar lo que había anteriormente.
Lo haré todo de nuevo:
> h.insert({_id:1, fecha: new Date(), mesa: '5'});
> h.update({},{$set:{descrip:{linea:1, articulo:'Churrasco',precio:8.60}}},false,true);
> h.update({},{$set:{"descrip.visores":['cocina', 'parrilla']}},false,true);
> h.find().pretty();
{
"_id" : 1,
"descrip" : {
"articulo" : "Churrasco",
"linea" : 1,
"precio" : 8.6,
"visores" : [
"cocina",
"parrilla"
]
},
"fecha" : ISODate("2013-04-12T17:45:53.718Z"),
"mesa" : "5"
}
Eso sí es lo correcto... Ahora si sé como añadir una clave dentro de otra clave sin eliminar el resto del documento embebido.
Para salir de la duda... como la eliminaría?
> h.update({},{$unset:{"descrip.visores":""}},false,true);
> h.find().pretty();
{
"_id" : 1,
"descrip" : {
"articulo" : "Churrasco",
"linea" : 1,
"precio" : 8.6
},
"fecha" : ISODate("2013-04-12T17:45:53.718Z"),
"mesa" : "5"
}
Perfecto... agregaré de nuevo la clave 'visores' a 'descrip' y ya tengo mi estructura preparada:
{
"_id" : 1,
"descrip" : {
"articulo" : "Churrasco",
"linea" : 1,
"precio" : 8.6,
"visores" : [
"cocina",
"parrilla"
]
},
"fecha" : ISODate("2013-04-12T17:45:53.718Z"),
"mesa" : "5"
}
Ya puedo continuar con la razón principal de este post.
En principio, he creado un documento en el historial introduciendo los datos directamente, añadiendo la clave 'visores' con unos valores. Pero esos valores deben ser introducidos dependiendo del tipo de articulo y la mesa donde se realice el pedido.
Por lo pronto, necesito una clave en la colección 'articulos' que me diga qué tipo de artículo es. Si es comida, bebida, o cualquier otro tipo de artículo. Dependiendo del 'tipo' de 'articulo', se comprobará en la colección mesas por donde deben aparecer dichos articulos (si en cocina, en barra...)
Como todos los artículos que tengo por ahora son del tipo comida, añado a todos el mismo tipo:
> a.update({},{$set:{tipo:'comida'}},false,true);
> a.findOne();
{
"_id" : 1,
"familia" : "Carnes",
"nombre" : "Churrasco",
"precio" : 8.6,
"tipo" : "comida"
}
Como sólo tenemos una barra y una cocina (por ahora)
> m.update({},{$set:{visores:{comida:['cocina', 'parrilla'], bebida:['barra']}}},false,true);
> m.findOne();
{
"_id" : 1,
"nombre" : "1",
"visores" : {
"comida" : [
"cocina",
"parrilla"
],
"bebida" : [
"barra"
]
},
"zona" : "Salon"
}
Ahora que tengo mis datos como necesito, voy a hacer una prueba para ver las operaciones que tengo que realizar para poblar la colección 'historial' en función de los artículos que se vayan pulsando y la mesa en cuestión:
- La mesa se abre en el momento que el camarero inserta un pedido en ella (estamos en la mesa 4 y hemos pedido Kobe):
Del artículo me interesan los valores en la clave 'precio' y 'tipo':
> var art = a.find({nombre:'Kobe'},{precio:1, tipo: 1});
> var myart = art.hasNext() ? art.next() : null;
> print(myart.precio);
8.6
> print(myart.tipo);
comida
De la mesa sólo me interesan los valores de la clave 'visores' dependiendo de si el articulo es comida o bebida. Hago unas pruebas para comprobar que puedo obtener los resultados:
> m.find({nombre:'4'},{"visores.comida":1});
{ "_id" : 4, "visores" : { "comida" : [ "cocina", "parrilla" ] } }
> m.find({nombre:'4'},{"visores.bebida":1});
{ "_id" : 4, "visores" : { "bebida" : [ "barra" ] } }
Otra prueba para ver si todo aparece bien si el 'tipo' fuera 'bebida':
> var prueba = m.find({nombre:'4'},{"visores.bebida":1});
> print(prueba.next().visores.bebida)
barra
var query = {};
query["visores." + myart.tipo]=1;
> m.find({nombre:'4'},query);
{ "_id" : 4, "visores" : { "comida" : [ "cocina", "parrilla" ] } }
Probando la salida:
> var prueba = m.find({nombre:'4'},query);
> print(prueba.next().visores[myart.tipo]);
cocina,parrilla
Meto el valor de los visores en una variable:
> var prueba = m.find({nombre:'4'},query);
> var _visores = prueba.next().visores[myart.tipo];
> print(_visores);
cocina,parrilla
Ya puedo insertar en 'historial' con los valores que necesito:
> h.insert({_id:2, fecha:new Date(), mesa:'4', descrip:{linea:1, articulo:'Kobe', precio: myart.precio, visores:_visores}});
Comprobemos el nuevo historial que se ha creado:
> h.find({_id:2}).pretty();
{
"_id" : 2,
"fecha" : ISODate("2013-04-13T17:59:36.477Z"),
"mesa" : "4",
"descrip" : {
"linea" : 1,
"articulo" : "Kobe",
"precio" : 8.6,
"visores" : [
"cocina",
"parrilla"
]
}
}
Perfecto. Se ha insertado el historial con sus precio y sus visores en la linea 1:
Añadamos otra línea a la descripción resumiendo los pasos. Supongamos que seleccionamos 'Churrasco':
> var art = a.find({nombre:'Churrasco'},{precio:1, tipo: 1});
> var myart = art.hasNext() ? art.next() : null;
> var query = {};
> query["visores." + myart.tipo]=1;
1
> var prueba = m.find({nombre:'4'},query);
> var _visores = prueba.next().visores[myart.tipo];
> h.update({_id:2},{$push:{descrip:{linea:2, articulo:'Churrasco', precio: myart.precio, visores:_visores}}}, true);
Cannot apply $push/$pushAll modifier to non-array
No puedo insertar la nueva línea porque 'descrip' no es un array... Crearé de nuevo la estructura, a ver si ahora me permite hacer push en la 'descrip':
> h.remove({});
> h.insert({_id:1, fecha:new Date(), mesa:'5'});
> h.update({_id:1},{$set:{descrip:[{linea:"1", articulo:'Kobe', precio:myart.precio, visores: _visores}]}});
> h.find({_id:1}).pretty();
{
"_id" : 1,
"descrip" : [
{
"linea" : "1",
"articulo" : "Kobe",
"precio" : 8.6,
"visores" : [
"cocina",
"parrilla"
]
}
],
"fecha" : ISODate("2013-04-13T18:35:33.957Z"),
"mesa" : "5"
}
Ahora si está entre corchetes,lo que supone que es un array. Trato de hacer push con la nueva línea:
> h.update({_id:1},{$push:{descrip:{linea:2, articulo:'Churrasco', precio: myart.precio, visores:_visores}}});
> h.find({_id:1}).pretty();
{
"_id" : 1,
"descrip" : [
{
"linea" : "1",
"articulo" : "Kobe",
"precio" : 8.6,
"visores" : [
"cocina",
"parrilla"
]
},
{
"linea" : 2,
"articulo" : "Churrasco",
"precio" : 8.6,
"visores" : [
"cocina",
"parrilla"
]
}
],
"fecha" : ISODate("2013-04-13T18:35:33.957Z"),
"mesa" : "5"
}
Parece que por fin va tomando forma la comanda.
Supongo que ha quedado claro cómo pasar una variable para sustituir el nombre de la clave:
> var art = a.find({nombre:'Churrasco'},{precio:1, tipo: 1});
> var myart = art.hasNext() ? art.next() : null;
> var query = {};
> query["visores." + myart.tipo]=1;
1
> var prueba = m.find({nombre:'4'},query);
> var _visores = prueba.next().visores[myart.tipo];