Translate

jueves, 24 de octubre de 2013

Scripts para MongoDB con Javascript y JSON: ejemplo práctico

Title: Scripts for MongoDB with Javascript and JSON.

En esta entrada quiero ilustrar como se hace un script de actualización de datos para MongoDB utilizando el lenguaje de programación javascript.

Los datos en MongoDB están almacenados en JSON y vamos ver como poblar una base de datos con unos cuantos objectos JSON y también como realizar unas sencillas manipulaciones de los objectos creados.

Toda la información que aquí os pongo es todo lo que necesitáis para empezar a lanzar scripts a cualquier base de datos MongoDB situada en cualquier host o ip.

Se da por hecho que la instalación de MongoDB ya está hecha, sino puedes consultar aquí como instalarlo en Windows 7 de una manera muy sencilla.

Los pasos para crear y lanzar nuestros scripts son los siguientes:

1 - Arrancar motor de MongoDB
2 - Abrir una ventana de comandos y situarnos en el directorio bin de mongo.
3 - Creación del script de poblado de base de datos
4 - Lanzamiento del script.
5 - Comprobación  de datos
6 - Creación de script de actualización
7 - Lanzamiento del script.
8 - Comprobación  de datos

Nota: Siempre tenemos que tener un MongoDB instalado en nuestra máquina local donde realizaremos las operaciones de lanzamiento del script, pero esté script podrá ejecutar en cualquier instalación de MongoDB que tengas en remoto siempre que conozcas su host y puerto.


1 - Arrancar motor de MongoDB

Lo arrancamos yendo a la ruta de instalación de MongoDB y ejecutando: mongod --dbpath ..\data\db

[MONGODB-PATH]\bin\mongod --dbpath ..\data\db




2 - Abrir una ventana de comandos y situarnos en el directorio bin de mongo

En windows sería:

cmd
cd [MONGODB-PATH]\bin



3 - Creación del script de poblado de base de datos

Con nuestro editor favorito creamos un script en lenguaje javascript para ejecutarlo en la instancia de MongoDB que queramos.

Los aspectos importes que tenemos que tener en cuenta son los siguientes que se entenderán perfectamente al ver el código:

  • Tendremos que tener creada ya, una base de datos con su nombre. Si no la tenemos creada no pasa nada porque al utilizarla por primera vez e insertar en ella se creará.          
  •  En el propio script elegimos  el host o direccion ip del mongodb donde se ejecutará este script y la base de datos donde se aplicará. Es decir que es en el propio script donde indica el destino de todo.
  • Al ser MongoDb una base de datos NoSQL no es necesario crear una estructura de datos previa directamente iremos insertando nuestros datos con código javascript y estructura JSON.
  • El ID de los registros prefiero introducirlo yo manualmente porque si dejamos que el propio Mongodb nos lo autogenere no lo hace como un número entero normal y a la hora de buscarlo desde una aplicación puede resultar complicado. Así que recomiendo generar los ID de los registros en el servidor con un generador de ids único y añadirlo a tus registros, otra opción es generar el código con una función en javascript dentro del propio script.


El script de relleno de la base de datos es el siguiente:



--

/*
 * Script que se encarga de poblar la base de datos  
 * 
 */

print("STARTING SCRIPT");
//Host donde está nuestra base de datos, no tiene que ser nuestro equipo local, puede ser cualquier mongoDb.
conn = new Mongo("localhost");
//Nombre de la base de datos que vamos a utilizar
db = conn.getDB("antuandb");

/*Limpiamos la base de datos por si existia algo antes*/
db.dropDatabase();


/*coleciones de nuestro modelo de datos*/
db.createCollection("users");
db.createCollection("courses");

/* Usuarios */
print("***********creating users*********");


user1 = {
  
"_id" : "0",
"name" : "Antuan",
"surname" : "Martín",
"age": 35,
"deleted":false

}

user2 = {
  
"_id" : "1",
"name" : "Felime",
"surname" : "Rodriguez",
"age": 31,
"deleted":false

}


user3 = {
  
"_id" : "2",
"name" : "Jose",
"surname" : "Carrizo",
"age": 28,
"deleted":false

}


print("***********creating courses*********");

/* courses */


course1 = {
     
    "_id" : "0",
    "students" : [],
    "tutors" : [],
    "name":"Master en Animación y Modelado 3d con Maya en Madrid",
    "credits":3.5,
    "hours":35
  
};

course2 = {
   
    "_id" : "1",
    "students" : [],
    "tutors" : [],
    "name":"SEO: Posicionamiento Web en Buscadores",
    "credits":4.0,
    "hours":40
  
};

course3 = {
   
    "_id" : "2",
    "students" : [],
    "tutors" : [],
    "name":"Máster en Game design",
    "credits":2.8,
    "hours":28  
};


print("***********saving users*********");
db.users.save(user1);
db.users.save(user2);
db.users.save(user3);

print("***********saving courses*********");
db.courses.save(course1);
db.courses.save(course2);
db.courses.save(course3);


print("SCRIPT FINISHED");

--

4 - Lanzamiento del script.

Ya tenemos el script guardado en un archivo por ejemplo llamado initDB.js y estamos situados en el directorio [MONGODB-PATH]\bin como indicamos en el apartado 2.

Para larzar el script ejecutamos el siguiente comando.

mongo archivo1.js [archivon.js]

en nuestro caso

mongo initDB.js

Nota: Podemos poner tantos archivos .js como utilicemos, o sea que se pueden dividir los scripts en varios archivos.


Si tenemos algún error durante la ejecución podemos verlo en la salida de la ventana de comandos, tras la ejecución del script, donde nos dirá el error cometido y la línea en la que se ha producido, en este caso la línea 71 y nos se ha producido un SyntaxError.

--

MongoDB shell version: 2.4.1
connecting to: test
Mon Oct 21 18:12:09.559 JavaScript execution failed: SyntaxError: 
Unexpected string at C:\......\initDB.js:L71
failed to load: C:\..............\initDB.js

--


Una vez ejecutado el script correctamente obtengo la siguiente salida.

--
C:\mongodb-win32-2.4.1\bin>mongo initDB.js
MongoDB shell version: 2.4.1
connecting to: test
STARTING SCRIPT
***********creating users*********
***********creating courses*********
***********saving users*********
***********saving courses*********
SCRIPT FINISHED

--

No hay que hacer caso de lo del mensaje:

connecting to: test

porque es la base de datos de conexión por defecto.



5 - Comprobación  de datos

Yo recomiendo siempre que ejecutes un script comprobar los datos que se han introducido en la base de datos sean correctos tanto en número de registros como en los tipos de datos, porque podemos llevarnos sorpresas y quizás lo que era un Double es un String o lo que debia de ser un Array es un String y eso es un error grave.

Con el software MongoVUE que ya os he recomendado podemos ver esto sin problemas.

NOTA: No os fieis nunca del modo texto porque no deja de ser una interpretación del JSON, yo siempre me voy a la vista en arbol para comprobar los tipos de datos de cada campo (String, Double, Arry, etc..).



Si no te has descargado MongoVE, lo cual es un error muy grande, tienes la opción de ver los resultados por consola, para ello ejecutamos lo siguiente en una ventana de comandos:

mongo

y entramos en la shell de mongo, donde podremos operar con la base de datos realizar las siguiente operaciones, una para usar la base de datos que nos hace falta y la segunda para realizar la query de consulta de los datos.

> use antuandb
> db.users.find()

Y podremos ver el resultado en pantalla pero queda muy feo y no podemos ver bien los tipos de datos.






6 - Creación de script de actualización

Una vez que hemos creado un script de llenado de la base de datos ahora vamos a hacer un script de actualización de los datos que tenemos.

Vamos a hacer dos modificaciones sobre los datos para que podamos ver varios ejemplos de actualización.

  • Primero: vamos añadir un campo nuevo a todos los cursos que es deleted=false y sumaremos una hora al campo hours.
  • Segundo: vamos agregar a los usuarios menores de 30 a los cursos como alumnos y a los mayores de 30 como tutor.

Cosas importantes a tener en cuenta:

  • Al realizar una query los datos se almacenan una variable de tipo cursor que podemos recorrer y a su vez nos darán funciones interesantes como count() o forEach(funciton).
  • En la query donde buscamos los cursos debemos de utilizar la función snapshot porque si no la usamos al realizar modificaciones sobre los propios objetos que están en el cursos de resultados este se podría ver alterado. Usando snapshot garantizamos que los datos recuperados en ese cursos no se alterarán.
  •  La manera de recorrer los datos es usando la función forEach que incluirá una función para procesar cada unos de los datos del cursor.
  • lo la función addToSet introducimos un valor en un array del objeto de MongoDB de tal modo que si el objeto ya existe este no se inserta 2 veces. Esto lo hacemos para introducir el id del usuario en el array de alumnos o de tutores del curso.



--
/*
 * Script que se encarga de actualizar la base de datos  
 * 
 */

print("STARTING SCRIPT");
//Host donde está nuestra base de datos, no tiene que ser nuestro equipo local, puede ser cualquier mongoDb.
conn = new Mongo("localhost");
//Nombre de la base de datos que vamos a utilizar
db = conn.getDB("antuandb");

var i_int = 0;
var modificados_int = 0;
var ignorados_int = 0;
var erroneos_int = 0;

//Recuperamos todos los cursos
var courses_cur = db.courses.find().snapshot();
var totalSize_int = courses_cur.count();

print("******FIRST PART UPDATING COURSES************");

courses_cur.forEach(function(course){
 i_int++;
 print("*********************************");
    print("RECORD "+i_int +" of " + totalSize_int);   
 print("PROCESSING:"+ course.name + "("+course._id+")");
 
 //añado la nueva propiedad
 course.deleted = false;
 
 //Modifico propiedad existente
 course.hours = course.hours + 1;
   
 db.courses.save(course);
 
 print("UPDATED");
});

print("******END FIRST PART************");

print("******SECOND PART ADDING STUDENTS AND TUTORS************");

//Recorro todos los cursos
courses_cur = db.courses.find().snapshot();
courses_cur.forEach(function(course){
   print("*********************************");
   print("PROCESSING COURSE:"+ course.name + "("+course._id+")");
   var id_course = course._id;
   
   //Recupero los usario mayores de 30 años
   var users_cur = db.users.find({age:{$gt:30}}).snapshot();
         
   users_cur.forEach(function(user){
    
    print("      PROCESSING USER:"+ user.name + "("+user._id+")");
    var id_user = user._id;    
    db.courses.update({_id:id_course},{$addToSet:{"students":id_user}});
    
    print("      USER ADDED AS STUDENT");
 
   });
   
   //Recupero los usuarios menores de 30 años
   var users_cur = db.users.find({age:{$lte:30}}).snapshot();
   users_cur.forEach(function(user){
    
    
    print("      PROCESSING USER:"+ user.name + "("+user._id+")");
    var id_user = user._id;    
    db.courses.update({_id:id_course},{$addToSet:{"tutors":id_user}});
    
    print("      USER ADDED AS TUTOR");
    
   });      
 
});




--



7 - Lanzamiento del script.

Una vez guardado el script en el archivo updateDB.js lo ejecutamos igual que hicimos en el paso 4

mongo updateDB.js

La salida es la siguiente




--
C:\mongodb-win32-2.4.1\bin>mongo updateDB.js
MongoDB shell version: 2.4.1
connecting to: test
STARTING SCRIPT
******FIRST PART UPDATING COURSES************
*********************************
RECORD 1 of 3
PROCESSING:Master en Animación y Modelado 3d con Maya en Madrid(0)
UPDATED
*********************************
RECORD 2 of 3
PROCESSING:SEO: Posicionamiento Web en Buscadores(1)
UPDATED
*********************************
RECORD 3 of 3
PROCESSING:Máster en Game design(2)
UPDATED
******END FIRST PART************
******SECOND PART ADDING STUDENTS AND TUTORS************
*********************************
PROCESSING COURSE:Master en Animación y Modelado 3d con Maya en Madrid(0)
      PROCESSING USER:Antuan(0)
      USER ADDED AS STUDENT
      PROCESSING USER:Felipe(1)
      USER ADDED AS STUDENT
      PROCESSING USER:Jose(2)
      USER ADDED AS TUTOR
*********************************
PROCESSING COURSE:SEO: Posicionamiento Web en Buscadores(1)
      PROCESSING USER:Antuan(0)
      USER ADDED AS STUDENT
      PROCESSING USER:Felipe(1)
      USER ADDED AS STUDENT
      PROCESSING USER:Jose(2)
      USER ADDED AS TUTOR
*********************************
PROCESSING COURSE:Máster en Game design(2)
      PROCESSING USER:Antuan(0)
      USER ADDED AS STUDENT
      PROCESSING USER:Felipe(1)
      USER ADDED AS STUDENT
      PROCESSING USER:Jose(2)
      USER ADDED AS TUTOR
SCRIPT FINISHED

--


8 - Comprobación  de datos

Finalmente realizamos la comprobación de que que los datos se han usertado correctamente, recomiendo hacerlo porque MongoDB no da errores si la inserción no es correcta o te equivocas en una query, directamente seguirá ejecutando el código como nada hubiera pasado.

echando un ojo a los datos vemos que todo está correcto y ya está todo terminado.



--

{
  "_id" : "0",
  "credits" : 3.5,
  "deleted" : false,
  "hours" : 41.0,
  "name" : "Master en Animación y Modelado 3d con Maya en Madrid",
  "students" : ["0", "1"],
  "tutors" : ["2"]
}

{
  "_id" : "0",
  "name" : "Antuan",
  "surname" : "Martín",
  "age" : 35.0,
  "deleted" : false
}

--


Espero que os resulte muy útil y si es así se agradece un comentario. También se aceptan críticas y mejoras de mismo, así como dudas.

Referencias:

Write Scripts for the mongoDb Shell