Mostrando entradas con la etiqueta documentos. Mostrar todas las entradas
Mostrando entradas con la etiqueta documentos. Mostrar todas las entradas

sábado, 13 de abril de 2013

Variables como claves en la shell de MongoDB

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];



miércoles, 10 de abril de 2013

Intentando crear un esquema de datos para MongoDB

Esta vez trataré de crear un esquema más 'a lo NoSQL' para ver como puedo abordar la tarea de pasar KomanderTPV (corriendo el típico XAMPP con Apache, PHP, MySQL...) a NODE.JS + MONGODB.

Aviso de que este post no es un tutorial. Son los pasos (o palos) que voy dando para adentrarme en el mundo de node y mongo. Si lo sigues paso a paso irás fallando como yo lo he hecho... pero al menos habrás probado.

La situación que me planteo es la siguiente:

Un restaurante dividido en zonas, cada zona con sus mesas.Necesito un historial de cada mesa con los artículos que vayan pidiendo los clientes, los cuales pueden elegir entre un menú de carne o pescado.

Más o menos estas son las colecciones que necesito:

articulos: id, nombre, familia
mesas: id, nombre, zona
historial: id, fecha, mesa, descrip ( linea, articulo, precio)

Al lío:


> use km
switched to db km

> db.articulos
km.articulos
> a = db.articulos
km.articulos
> a.find();
> a.insert({_id:1, nombre: 'Churrasco', familia: 'Carnes'});


> a.insert({_id:2, nombre: 'Kobe', familia: 'Carnes'});
> a.insert({_id:3, nombre: 'Solomillo de Ternera', familia: 'Carnes'});
> a.insert({_id:4, nombre: 'Dorada', familia: 'Pescado'});
> a.insert({_id:5, nombre: 'Urta', familia: 'Pescado'});


Ya tengo mi colección 'articulos' con varios documentos.

Ahora las mesas:


> var m = db.mesas
> m.insert({_id:1, nombre:'1', zona:'Salon'});
> m.insert({_id:2, nombre:'2', zona:'Salon'});
> m.insert({_id:3, nombre:'3', zona:'Salon'});
> m.insert({_id:4, nombre:'4', zona:'Terraza'});
> m.insert({_id:5, nombre:'5', zona:'Terraza'});
> m.insert({_id:6, nombre:'6', zona:'Terraza'});

Veamos lo que he introducido hasta ahora:


> a.find();
{ "_id" : 1, "nombre" : "Churrasco", "familia" : "Carnes" }
{ "_id" : 2, "nombre" : "Kobe", "familia" : "Carnes" }
{ "_id" : 3, "nombre" : "Solomillo de Ternera", "familia" : "Carnes" }
{ "_id" : 4, "nombre" : "Dorada", "familia" : "Pescado" }
{ "_id" : 5, "nombre" : "Urta", "familia" : "Pescado" }
> 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" }

El turno de la colección 'historial', donde se guardaran los pedidos realizados y sus mesas correspondientes. Pero esta colección se creará sobre la marcha. Así que solo la asignaremos a una variable para que sea más cómodo acceder a ella:

> var h = db.historial;

Reconstruyamos lo que pasaría paso por paso.

- El camarero lleva a sus clientes a la mesa 5. Lo primero que tiene que hacer es visualizar las mesas que hay en la zona 'Terraza' para poder visualizar la mesa que necesita:

> m.find({zona: 'Terraza'});
{ "_id" : 4, "nombre" : "4", "zona" : "Terraza" }
{ "_id" : 5, "nombre" : "5", "zona" : "Terraza" }
{ "_id" : 6, "nombre" : "6", "zona" : "Terraza" }


- El camarero pincha en la mesa 5 y se abre una nueva cuenta:

> h.insert({_id:1, fecha:new Date(2013,4,10), mesa:'5'});


- El camarero, muy amablemente, le canta el menú a sus clientes. Uno de ellos quiere un Churrasco. Al pulsar sobre churrasco, tendría que añadirse a la lista de pedidos:

Al definir la colección 'articulos' se me pasó añadir una clave 'precio'. Necesito crear el campo 'precio' en 'articulos`' (los pondré todos al mismo precio):

>a.update({},{'$set':{precio:8.60}}, false, true);

Ahora intentaré recuperar el valor 'precio' del articulo 'Churrasco' y meterlo en la nueva linea de descripción:

> var pvp=a.find({nombre:'Churrasco'}, {precio:1});
> h.update({_id:1},{'$set': {descrip:[{linea: 1, articulo: 'Churrasco', precio: pvp}]}});

Algo de lo que acabo de hacer está mal... si hago un find() en 'historial':


> h.find();
{ "_id" : 1, "descrip" : [      {       "linea" : 1,    "articulo" : "Churrasco"
,       "precio" : {    "_mongo" : {    "slaveOk" : false,      "host" : "127.0.
0.1" },         "_db" : {       "_mongo" : {    "slaveOk" : false,      "host" :
 "127.0.0.1" },         "_name" : "km" },       "_collection" : {       "_mongo"
 : {    "slaveOk" : false,      "host" : "127.0.0.1" },         "_db" : {
"_mongo" : {    "slaveOk" : false,      "host" : "127.0.0.1" },         "_name"
: "km" },       "_shortName" : "articulos",     "_fullName" : "km.articulos",
"_lastID" : 5 },        "_ns" : "km.articulos",         "_query" : {    "nombre"
 : "Churrasco" },       "_fields" : {   "precio" : 1 },         "_limit" : 0,
"_skip" : 0,    "_batchSize" : 0,       "_options" : 0,         "_cursor" : null
,       "_numReturned" : 0,     "_special" : false } } ], "fecha" : ISODate("201
3-05-09T22:00:00Z"), "mesa" : "5" }


Supongo que ha sido al coger el pvp... lo compruebo paso a paso:

Borro los documentos de la coleccion 'historial':

>db.historial.remove();
> h.find();

Inserto un historial nuevo:


> h.insert({_id:1, fecha:new Date(2013,4,5,17,25), mesa:'5'});
> h.find();
{ "_id" : 1, "fecha" : ISODate("2013-05-05T15:25:00Z"), "mesa" : "5" }

La fecha es lo que hay que mirar, pero eso luego...

> h.update({_id:1},{'$set':{descrip:[{linea:1, articulo:'Churrasco', precio: 8.60}]}});



Si ahora hago un find():


> h.find();
{ "_id" : 1, "descrip" : [ { "linea" : 1, "articulo" : "Churrasco", "precio" : 8
.6 } ], "fecha" : ISODate("2013-05-05T15:25:00Z"), "mesa" : "5" }
>

Sí, lo muestra bien... No habré pasado la variable correctamente... Puede que no lo haya pasado de la forma correcta... Añadiré una segunda línea con el operador $push:



> var pvp=a.find({nombre:'Churrasco'}, {precio:1});
> h.update({_id:1},{'$push':{descrip:[{linea:2, articulo:'Churrasco', precio: pvp.precio}]}});

Nada. El valor de precio lo pone a null...



{ "_id" : 1, "descrip" :
 [{"linea" : 1,"articulo" : "Churrasco","precio" : 8.6 }
,[{"linea" : 2,"articulo" : "Churrasco","precio" : null } ] ]
, "fecha" : ISODate("2013-05-05T15:25:00Z"), "mesa" : "5" }

Veamos que es lo que vale 'pvp':


> pvp
{ "_id" : 1, "precio" : 8.6 }


El _id no lo necesito para nada, ya que lo que usaré será el precio nada más:

> pvp=a.find({nombre:'Churrasco'}, {precio:1, _id:0});
{ "precio" : 8.6 }


pvp no contiene el valor de precio, sino un cursor... si usamos .hasNext() podemos ver si tiene elementos:


> var pvp=db.articulos.find({nombre:'Churrasco'}, {precio:1, _id:0});
> pvp.hasNext();
true


Si lo mostramos:


> print(pvp);
DBQuery: km.articulos -> { "nombre" : "Churrasco" }




> print(pvp.next().precio);
8.6

Ahora sí. Con .next() avanzamos una posición en el resultado devuelto por find(). Para recorrer una serie de documentos, es decir, el resultado de un find, usaremos:

> while(variable.hasNext()){printjson(variable.next().nombreclave)};


> var arts = db.articulos.find({familia:'Carnes'});
> while(arts.hasNext()){printjson(arts.next().nombre)};
"Churrasco"
"Kobe"
"Solomillo de Ternera"


o bien:

> variable.forEach(printjson)


> var arts = db.articulos.find({familia:'Carnes'});
> arts.forEach(printjson);
{ "_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
}





Con todo esto, si quisiera agregar una nueva linea al pedido, pero esta vez usando el precio del articulo en una variable, hasta donde llego lo haría así:


> var pvp=a.find({nombre:'Churrasco'}, {precio:1});
> h.update({_id:1},{'$push':{descrip:[{linea:2, articulo:'Churrasco', precio: pv
p.next().precio}]}});

Lo compruebo:

> h.find().pretty();
{
        "_id" : 1,
        "descrip" : [
                {
                        "linea" : 1,
                        "articulo" : "Churrasco",
                        "precio" : null
                },
                [
                        {
                                "linea" : 2,
                                "articulo" : "Churrasco",
                                "precio" : null
                        }
                ],
                [
                        {
                                "linea" : 2,
                                "articulo" : "Churrasco",
                                "precio" : 8.6
                        }
                ]
        ],
        "fecha" : ISODate("2013-05-05T15:25:00Z"),
        "mesa" : "5"
}




Ok. Me ha insertado el precio del churrasco pero:

1.Me he equivocado y he añadido una nueva descrip con el mismo numero de linea.
2.Veo muchos corchetes en el resultado... suponía que sólo debo verlos una vez dentro de 'descrip'.


Primero revisaré la estructura. Para comprobar si estoy en lo correcto: Crearé un nuevo documento en 'historial', insertando dos lineas 'descrip' para ver si todo queda dentro de los corchetes:

> h.insert({_id:2, fecha: new Date(2013/04/10), mesa:'4', descrip:[{linea:1, art
iculo:'Churrasco', precio: 8.6},{linea:2, articulo:'Kobe', precio:8.6}]});



> h.find({_id:2}).pretty();
{
        "_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
                }
        ]


Este resultado me cuadra más (la fecha evidentemente no, pero he probado a introducirla de otra forma y ha salido esa). Seguramente me habré equivocado al hacer el $set o el $push... así que a probarlo de nuevo, a ver si al actualizar sigue todo dentro del mismo array 'descrip':


> h.insert({_id:3, fecha: new Date(), mesa:'4'});
> h.update({_id:3},{'$push':{descrip:{linea:1, articulo:'Churrasco', precio:8.6}}});


> h.update({_id:3},{'$push':{descrip:{linea:2, articulo:'Kobe', precio:8.6}}});

Lo compruebo:

> h.find({_id:3}).pretty();
{
        "_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"
}


Ahora sí me cuadra... Me cuadra hasta la fecha. Las líneas de 'descrip' están entre llaves, NO entre corchetes como aparecían al principio.

Lo único que no cuadra es el primer documento, ya que aparecen las lineas de descripción entre corchetes.
Así que lo borro y listo. Podía haber tratado de modificarlo, pero por hoy ya he tenido suficiente. Así que mañana será otro día y trataré de avanzar un poco más sobre el tema.







martes, 9 de abril de 2013

¡A enmongarse toca!

Después de un primer contacto con NODE+MONGODB+EXPRESS y antes de meterle mano a mongoJS, debería trastear con mongoDB directamente y hacerme una idea de cómo tendría que crear la estructura de registros (documentos creo que se llaman) necesaria para KomanderTPV.

Ver cómo se selecciona por criterios, hacer consultas complejas, alterar tablas y ese tipo de cosas que uno esta acostumbrado a hacer en una base de datos SQL (MySQL en mi caso).

Siento desilusionar a todos aquellos que hayan pensado que vamos a ponernos púos de monguis, tal y como el título del post pueda sugerir. Espero que al menos la banda sonora no te defraude.

MANOS A LA OBRA

Por lo pronto me voy a http://www.mongodb.org para hacer el pequeño tutorial que ofrecen online. Recuerdo que lo hice una vez porque me llamo la atención.. Pero no recuerdo mucho como iba y no tenía un mongo en mi máquina para practicar.

En el punto número 2 del tutorial aclaran que MongoDB guarda los datos en forma de documentos, los cuales son lo mismo que un objeto en JavaScript.

2. Documents
MongoDB is a document database. This means that we store data as documents,
which are similar to JavaScript objects. Here below are a few sample JS objects:
  var a = {age: 25}; 
  var n = {name: 'Ed', languages: ['c', 'ruby', 'js']}; 
  var student = {name: 'Jim', scores: [75, 99, 87.2]}; 

Así por encima, se me ocurre:

var plato = { nombre: 'Churrasco', mods: ['Patatas', 'Ensalada']};

Supongo que disponer de un array dentro de un registro, es una ventaja si lo comparo con SQL, ya que para hacerlo en SLQ tendría que tener dos tablas y relacionarlas: Platos y Modificadores. Pero eso ya se verá... no quiero precipitarme, ya que aún no se nada.

Este es el resultado de ejecutar la sentencia anterior:

{
 "nombre" : "Churrasco Argentino",
 "mods" : [   "Patatas",   "Ensalada" ]
 }

Un objeto 'plato'. Churrasco Argentino el cual podrá ir acompañado de Patatas o con Ensalada (ufffff, no comment)

El tercer paso muestra como guardar un documento (registro) dentro de una colección (tabla). 


Guarda un documento en la colección 'scores':
db.scores.save({a:99});

Muestra los documentos de la colección 'scores';
db.scores.find();

(Estoy tratando de que me funcione el save(), pero parece que no se guardan los documentos... acabaré de leer  el minitutorial por encima y al lio en mi máquina)

Se acabo el minitutorial... ahora a practicar.


Desde una shell con mongo, creo la db  donde guardaée las colecciones para KomanderTPV:

> use km
switched to db km

El comando use cambia a la db seleccionada y encaso de que no exista, la crea.

Para crear una colección simplemente inserto un documento en la colección con el nombre deseado:


> db.articulos.insert({nombre: 'Churrasco'});

Ahora si hago un find():

> db.articulos.find();
{ "_id" : ObjectId("516329924680b862f0ced8c9"), "nombre" : "Churrasco" }
>


Ya tengo mi db, mi primera colección y mi primer documento.
Añadiré un par de ellos más...

> db.articulos.insert({nombre: 'Kobe'});



> var mas_articulos = [{nombre: 'Solomillo Ternera'},{ nombre: 'Chuleton de Buey'}];
> db.articulos.insert(mas_articulos);


Ya tengo varios artículos añadidos a mi db. Todos platos de carne buena y jugosa. Hace falta algo de pescado. Tanta carne...


> var algunos_pescados = [{nombre: 'Dorada'},{ nombre: 'Urta'}];
> db.articulos.insert(algunos_pescados);


Consultando la colección 'articulos' veo que tengo 6 distintos:


> db.articulos.find();
{ "_id" : ObjectId("516329924680b862f0ced8c9"), "nombre" : "Churrasco" }
{ "_id" : ObjectId("51632a714680b862f0ced8ca"), "nombre" : "Kobe" }
{ "_id" : ObjectId("51632b5b4680b862f0ced8cb"), "nombre" : "Solomillo Ternera" }

{ "_id" : ObjectId("51632b5b4680b862f0ced8cc"), "nombre" : "Chuleton de Buey" }
{ "_id" : ObjectId("51632cfe4680b862f0ced8cd"), "nombre" : "Dorada" }
{ "_id" : ObjectId("51632cfe4680b862f0ced8ce"), "nombre" : "Urta" }


Pero cuatro de ellos son platos de carne y los otros dos son pescados. Debería agregar un campo al documento que me permita diferenciar la carne del pescado. Lo llamaré 'familia'.

Pero que hago? Inserto familia directamente artículo por artículo? Altero el documento para que tenga un campo familia vacio? Cual es la mejor forma?

Utilizando $set me ha añadido la familia 'carne' al articulo con nombre Churrasco:



> db.articulos.update({nombre: 'Churrasco'},{'$set': {'familia':'Carne'}});
> db.articulos.find();
{ "_id" : ObjectId("51632a714680b862f0ced8ca"), "nombre" : "Kobe" }
{ "_id" : ObjectId("51632b5b4680b862f0ced8cb"), "nombre" : "Solomillo Ternera" }

{ "_id" : ObjectId("51632b5b4680b862f0ced8cc"), "nombre" : "Chuleton de Buey" }
{ "_id" : ObjectId("51632cfe4680b862f0ced8cd"), "nombre" : "Dorada" }
{ "_id" : ObjectId("51632cfe4680b862f0ced8ce"), "nombre" : "Urta" }
{ "_id" : ObjectId("516329924680b862f0ced8c9"), "familia" : "Carne", "nombre" :
"Churrasco" }

 se debe cambiar de uno en uno? No lo sé. Lo comprobaré...

Para modificar la clave de en todos los documentos el selector debe estar vacío. Además, el último parámetro de la función update (multi) debe estar a true:
> db.articulos.update({},{'$set':{'familia': ""}},false,true);

> db.articulos.find();
{ "_id" : ObjectId("516329924680b862f0ced8c9"), "familia" : "", "nombre" : "Churrasco" }
{ "_id" : ObjectId("51632a714680b862f0ced8ca"), "familia" : "", "nombre" : "Kobe" }
{ "_id" : ObjectId("51632b5b4680b862f0ced8cb"), "familia" : "", "nombre" : "Solomillo Ternera" }
{ "_id" : ObjectId("51632b5b4680b862f0ced8cc"), "familia" : "", "nombre" : "Chuleton de Buey" }
{ "_id" : ObjectId("51632cfe4680b862f0ced8cd"), "familia" : "", "nombre" : "Dorada" }
{ "_id" : ObjectId("51632cfe4680b862f0ced8ce"), "familia" : "", "nombre" : "Urta" }

En este caso, al realizar el anterior update, ha machacado la familia que estaba en el artículo Churrasco.

Mi gran duda ahora es si debería crear una colección 'familias' la cual estará relacionada con 'articulos' mediante la id. No se cúal es la metodología en este caso. Así que a buscar algo de info.

EN la pagina de mongo, leo sobre el modelado de datos...
http://docs.mongodb.org/manual/tutorial/model-embedded-one-to-one-relationships-between-documents/
http://docs.mongodb.org/manual/tutorial/model-embedded-one-to-many-relationships-between-documents/
http://docs.mongodb.org/manual/tutorial/model-referenced-one-to-many-relationships-between-documents/
http://docs.mongodb.org/manual/tutorial/model-data-for-atomic-operations/

http://www.10gen.com/presentations/mongodb-melbourne-2012/schema-design-example

Después de leer sobre el modelado de datos en mongoDB realmente no tengo clara la idea. Todo depende de como se vayan a escribir y leer los datos en tu aplicación. La forma en que se usarán los datos.

SI que se recomienda embeber todo en un documento, ya que obtiene todo en una sola consulta... pero es más difícil de manipular.. No sé realmente. Leeré un poco más y lo practicaré.

...

Finalmente comenzaré creando colecciones distintas para cada cosa... Quizás lo haga así por mi mentalidad SQL, pero de todas formas, no me gusta la repetición de datos, la cual supongo es una mala práctica.

Probaré, aunque ya ni me acuerdo del nombre que le puse a la base de datos. Cerré las ventanas de mongo y con tanta información ya no recuerdo por donde iba...

Bueno, abro mi mongo, y reviso cuales son las dbs existentes:

> show dbs
blog    0.0625GB
km      0.0625GB
local   0.03125GB

Ahora ya me acuerdo, mi db es 'km'

> use km
switched to db km

Y, cual era la coleccion??

> show collections
articulos
system.indexes

AH sí... articulos... asi con para ver las dbs y las colecciones que tenemos, podemos usar el comando 'show' seguido de lo que queremos ver:

> show dbs
> show collections

Bueno.. a lo que vamos. Voy a crear una colección llamada 'familias', cuyo indice se insertará como valor en la clave 'familia' de los documentos 'articulos'. Pero esta vez pondré yo la id del documento:

> db.familias.insert({_id:1, nombre:'Carnes'});
> db.familias.find();
{ "_id" : 1, "nombre" : "Carnes" }

Por lo pronto ya no tengo una id tan larga... Lo que sí es largo, es tener que estar escribiendo 'db.coleccion'...

> var f = db.familias;
> f.find();
{ "_id" : 1, "nombre" : "Carnes" }

Vale. Es más fácil escribir 'f' (de familia) que tener que poner 'db.familias'. Vamos progresando...

> f.insert({_id: 2, nombre:'Pescado'});

Ya tenemos articulos y familias. Vamos a modificar los documentos de la colección articulos para que cada uno apunte a su tipo de familia:

> var a = db.articulos;
> a.update({nombre:'Churrasco'},{'$set':{familia:1}});


> a.update({nombre:'Kobe'},{'$set':{familia:1}});
> a.update({nombre:'Solomillo Ternera'},{'$set':{familia:1}});
> a.update({nombre:'Chuleton de Buey'},{'$set':{familia:1}});
> a.update({nombre:'Dorada'},{'$set':{familia:2}});
> a.update({nombre:'Urta'},{'$set':{familia:2}});
> a.find();
{ "_id" : ObjectId("516329924680b862f0ced8c9"), "familia" : 1, "nombre" : "Churrasco" }
{ "_id" : ObjectId("51632a714680b862f0ced8ca"), "familia" : 1, "nombre" : "Kobe" }
{ "_id" : ObjectId("51632b5b4680b862f0ced8cb"), "familia" : 1, "nombre" : "Solomillo Ternera" }
{ "_id" : ObjectId("51632b5b4680b862f0ced8cc"), "familia" : 1, "nombre" : "Chuleton de Buey" }
{ "_id" : ObjectId("51632cfe4680b862f0ced8ce"), "familia" : 2, "nombre" : "Urta" }
{ "_id" : ObjectId("51632cfe4680b862f0ced8cd"), "familia" : 2, "nombre" : "Dorada" }



OK, ahora viene lo bueno porque para ver el nombre de la familia del articulo Churrasco yo pensaría:

SELECT familias.nombre FROM familias INNER JOIN articulos ON familias._id = articulos.familia WHERE articulos.nombre = 'Churrasco'

A ver como cambio mi mentalidad para esto...

Buenooo... tras leer en varios post sobre el tema, todo desemboca en la misma respuesta:

OPEN YOUR MIND!!

No simplificar en las colecciones, es decir, no debería crear una colección para cada tabla y que esta se relacione con la otra y tal y cual... No debería pensar relacionalmente... Me cuesta pero debo hacerlo así... Lo mejor es que haga lo contrario a lo que creo que es lo mejor. Dividir un documento en distintas colecciones ralentizará el sistema.

Así que borrare mi db 'km' y comenzaré de nuevo con otra mentalidad.

> use km
switched to db km
> db.dropDatabase();
{ "dropped" : "km", "ok" : 1 }

CONCLUSIÓN

Hasta este punto y con lo visto, creo que he recorrido algunos pasos. De sobra sé, en el contexto que me toca, que por muchos pasos que dé, nunca llegaré al final. Pero seguro que estaré más lejos que al principio.

Por lo pronto tengo claro como crear, borrar, insertar, actualizar, moverme por la db... cosas muy simples pero que desconocía.

Lo que sí es seguro, es que hay que cambiar la forma de ver las cosas.

Para no hacer el post muy largo, continuaré en el siguiente,  creando una db de nuevo pero esta vez pensando en otro tipo de esquema para mis datos.

Nos vemos en el siguiente mongui!!:

Intentando crear un esquema para MongoDB