jueves, 18 de abril de 2013

Documentos para las pruebas

Finalmente, dejaré listas las colecciones de MongoDB que necesito para las pruebas con Node.js.

En el post anterior (Variables como claves en la shell de MongoDB) me quedó clara la forma en que debo coger un valor de un documento o subdocumento y usarlo para obtener los datos de otro documento, mezclando los nombres de las claves con los resultados.

A modo de resumen:

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


Ahora lo que me interesa es añadir un par de campos para las pruebas y realizar un par de operaciones básicas sobre los documentos. 

Una vez que sepa como mostrar los valores del documento 'historial' por cocina o barra (o dónde le toque) estaré preparado para meterle a mano a MongoJS desde Node.js y empezar a juntar la piezas de este entretenido puzzle.

Por lo pronto abro mi shell y antes de hacer nada, asignaré las colecciones a unas variables para no tener que escribir tanto, tal y como he venido haciendo hasta el momento:


> use km
switched to db km
> var a = db.articulos;
> var m = db.mesas;
> var h = db.historial;

Ahora añadiré algunos artículos del tipo 'bebida':

> a.insert({_id:6, familia: 'Refrescos', nombre: 'Fanta Naranja', precio: 1, tipo:'bebida'});
> a.insert({_id:7, familia: 'Refrescos', nombre: 'Fanta Limon', precio: 1, tipo:'bebida'});
> a.insert({_id:8, familia: 'Cervezas', nombre: 'Tanque', precio: 1, tipo:'bebida'});
> a.insert({_id:9, familia: 'Cervezas', nombre: 'Jarra Cerveza', precio: 3, tipo:'bebida'});

En la colección 'historial' debo añadir una clave 'uds' dentro de 'descrip' para anotar la cantidad del articulo pedido:


> h.update({},{$set:{"descrip.uds":1}},false,true);
can't append to array using string field name: uds



Según he estado leyendo, para añadir una clave a un subdocumento se debe utilizar el operador de posicionamiento $. Pero como sé cual es el índice (en este caso 0, el primer valor de un array) lo pongo directamente:


> h.update({},{$set:{"descrip.0.uds":1}},false,true);


{
        "_id" : 1,
        "descrip" : [
                {
                        "articulo" : "Kobe",
                        "linea" : "1",
                        "precio" : 8.6,
                        "uds" : 1,
                        "visores" : [
                                "cocina",
                                "parrilla"
                        ]
                }
        ],
        "fecha" : ISODate("2013-04-13T18:35:33.957Z"),
        "mesa" : "5"
}



Ya tengo preparadas las colecciones necesarias para las pruebas que quiero realizar desde node. De todas formas, probaré a insertar un par de líneas más para ver un par de cosas.

Revisando la estructura del documento, el valor de 'linea' está entre comillas y lo tratará como un string. Hay que modificarlo:

> h.update({_id:1, 'descrip.linea':'1'},{$set:{'descrip.$.linea':1}});

> h.findOne();
{
        "_id" : 1,
        "descrip" : [
                {
                        "articulo" : "Kobe",
                        "linea" : 1,
                        "precio" : 8.6,
                        "uds" : 1,
                        "visores" : [
                                "cocina",
                                "parrilla"
                        ]
                }
        ],
        "fecha" : ISODate("2013-04-15T17:59:03.152Z"),
        "mesa" : "5"
}                                                                                    



Nos vamos entendiendo... Voy a añadir otra línea al pedido de la mesa 5:

> h.update({_id:1},{$push:{descrip:{linea:2, articulo:'Churrasco', precio: 8.6, uds:1, visores:['cocina','parrilla'] }}});

Las dos lineas del pedido son del tipo comida. Añado un par de líneas, pero del tipo bebidas, para hacer la prueba de ver los pedidos según visor:

> h.update({_id:1},{$push:{descrip:{linea:3, articulo:'Fanta Limon', precio: 1, uds:1, visores:['barra'] }}});
> h.update({_id:1},{$push:{descrip:{linea:4, articulo:'Tanque', precio: 1, uds:1, visores:['barra'] }}});


Esta es la primera consulta que se me viene a la mente, suponiendo que abro el visor de la barra (llamado 'barra' evidentemente) para ver los pedidos que hay que preparar:

> h.find({'descrip.visores':{$in:['barra']}}).pretty();

Pero si la ejecuto me muestra todas las líneas en 'descrip', ya sean barra o cocina... Me devuelve el documento completo y yo sólo necesito las lineas en descrip que tengan el visor 'barra'.

find() retorna el documento (o documentos) completo, con todas sus claves. Supongo que la consulta anterior busca en la clave visores la palabra 'barra' y si la encuentra, devuelve el documento entero.

Para filtrar la salida y devolver sólo los subdocumetos que contengan 'barra' en su clave 'visores' tengo hacer uso de la función aggregate():

> h.aggregate([{$match: { }},{'$unwind': '$descrip'}, {$match:{'descrip.visores':{$in:['barra']}}}]);

{
        "result" : [
                {
                        "_id" : 1,
                        "descrip" : {
                                "linea" : 3,
                                "articulo" : "Fanta Limon",
                                "precio" : 1,
                                "uds" : 1,
                                "visores" : [
                                        "barra"
                                ]
                        },
                        "fecha" : ISODate("2013-04-15T17:59:03.152Z"),
                        "mesa" : "5"
                },
                {
                        "_id" : 1,
                        "descrip" : {
                                "linea" : 4,
                                "articulo" : "Tanque",
                                "precio" : 1,
                                "uds" : 1,
                                "visores" : [
                                        "barra"
                                ]
                        },
                        "fecha" : ISODate("2013-04-15T17:59:03.152Z"),
                        "mesa" : "5"
                }
        ],
        "ok" : 1
}


Eso está mejor. He obtenido sólo las líneas cuyas claves 'visores' contienen la palabra 'barra'.

Voy a añadir un segundo historial (otra mesa nueva) para comprobar que obtengo todos los resultados que necesito:

> h.insert({_id:2, fecha: new Date(), mesa:'4'});
> h.update({_id:2},{$set:{descrip:[{linea:"1", articulo:'Fanta Naranja', precio:1, visores: ['barra']}]}});


Comprobaré las bebidas que se han pedido en 'barra':


> h.aggregate([{$match: { }},{'$unwind': '$descrip'}, {$match:{'descrip.visores':{$in:['barra']}}}]);
{
        "result" : [
                {
                        "_id" : 1,
                        "descrip" : {
                                "linea" : 3,
                                "articulo" : "Fanta Limon",
                                "precio" : 1,
                                "uds" : 1,
                                "visores" : [
                                        "barra"
                                ]
                        },
                        "fecha" : ISODate("2013-04-15T17:59:03.152Z"),
                        "mesa" : "5"
                },
                {
                        "_id" : 1,
                        "descrip" : {
                                "linea" : 4,
                                "articulo" : "Tanque",
                                "precio" : 1,
                                "uds" : 1,
                                "visores" : [
                                        "barra"
                                ]
                        },
                        "fecha" : ISODate("2013-04-15T17:59:03.152Z"),
                        "mesa" : "5"
                },
                {
                        "_id" : 2,
                        "descrip" : {
                                "linea" : "1",
                                "articulo" : "Fanta Naranja",
                                "precio" : 1,
                                "visores" : [
                                        "barra"
                                ]
                        },
                        "fecha" : ISODate("2013-04-18T16:11:15.500Z"),
                        "mesa" : "4"
                }
        ],
        "ok" : 1
}

Perfecto. Me devuelve las líneas de pedidos realizados para la barra.

Ya tengo las colecciones que necesito para las pruebas con MongoJS y Node.

Para terminar con este post y antes de pasar a MongoJS, voy a indexar las claves _id de mis colecciones. Esto hace que Mongo pueda buscar más rápidamente entre los documentos (si la query se hace desde la clave indexada). Podemos indexar cualquier campo de nuestras colecciones (más info aquí).

> h.ensureIndex({_id:1});

> a.ensureIndex({_id:1});
> m.ensureIndex({_id:1});

Podemos ver los indices con la función getIndexes();


> h.getIndexes();
[
        {
                "v" : 1,
                "key" : {
                        "_id" : 1
                },
                "ns" : "km.historial",
                "name" : "_id_"
        }
]

Y se pueden ver todos los índices de la base de datos con db.system.indexes.find():

> db.system.indexes.find() { "v" : 1, "key" : { "_id" : 1 }, "ns" : "km.articulos", "name" : "_id_" } { "v" : 1, "key" : { "_id" : 1 }, "ns" : "km.mesas", "name" : "_id_" } { "v" : 1, "key" : { "_id" : 1 }, "ns" : "km.historial", "name" : "_id_" }



Creo que ahora esta todo preparado para realizar algunas  pruebas con mongojs y node. Al menos hasta ahora sé manejarme un poco y tengo algunas colecciones con las que jugar.

Quizás podría ser mejor o más fácil. Seguramente existirá otra fórmula más cómoda de realizar los esquemas para los documentos y jugar con sus claves/valores. Pero hasta aquí es donde llego con mi conocimiento y el poco tiempo que puedo dedicarle a este tema. Por lo poco que he leído (que no ha sido tan poco) recomiendan el tipo de esquema embebido, y así lo haré.

De todas formas, en estos tres últimos posts dedicados a MongoDB, he tenido un primer contacto con este tipo de bases de datos NoSQL.

Aún no puedo comparar con MySQL, porque apenas sé sobre este tipo de base de datos. Pero si que comparando las colecciones que uso en los ejemplos, en MySQL hubiera tenido que crear varias tablas de más, las cuales estarían relacionadas.

Ya veremos si merece la pena.

Continúa en Primeras pruebas: node + mongo + express


No hay comentarios:

Publicar un comentario