RSS Feed

Mongoose + Nodejs + Modelos! Parte 1

17

febrero 13, 2013 by - @alejonext

La verdad es genial las NoSQL, pero siempre existe el problema. Como se modelar toda la información, tanto en NoSQL como en SQL. Como todos buenos arquitectos de la información, pero en este caso vamos a utilizar el FarmeWork MongooseJs que corre bastante bien en NodeJs.

Todo comenzó con los callbacks, en el mongodb-native de NodeJS. La verdad es bastante estresante generar muchísimos callbacks, y no se pueden generar relaciones entre una y otra colección. Ese realmente fue el problema que me enfrente. Y buscando encontré MongooseJs, que construye modelos de información, para que todo sea mas asequible, y mas fácil de encontrar. Vamos a echarle un vistazo!

var mongoose = require('mongoose');
var db = mongoose.createConnection( 'mongodb://localhost:27017/prueba' );

Hasta hora no es nada raro! Solo llamamos el modulo y le construimos una conexión a la base de datos. Ahora bien lo interesante! La construcción de la base de datos

var postSchema = mongoose.Schema({
    titulo: { type : String, trim : true, index : true },
    post : string,
    slug : string,
    autor : { type : Schema.Types.ObjectId, ref : 'autores' }  });
var userSchema = mongoose.Schema({
    name : { type : String, trim : true },
    nick : { type : String, trim : true, index : true },
    email: { type : String, trim : true },
});

Listo! voy a dar un ejemplo de blog! Como vemos, construimos Schema (Esquemas). Estos esquemas, es tan dados en Json (Bastante cercano a MongoDB por que utilizan BSON que es el mismo JSON pero en Binario ). Cada propiedad debe tener una definición, al igual se pueden colocar múltiples validaciones de esa propiedad. Y todas los valores van a ser validados, si existe algún error nos mostrara. Es decir a los titulo no le puedo poner un false, únicamente un String.

var Post = db.model('posts', postSchema);
var User = db.model('users', userSchema);

Y en lazaremos con la base de datos. El primer parámetro es el nombre de la Colección, y el segundo parámetro es el esquema. Ya con esto podemos subir información a la base de datos. Claro en el momento de subida va hacer validada.

var PrimerUsuario =  new User({
    name : 'Pepito Perez',
    nick : 'pepito',
    email: 'pepito@pepito.com'
});
PrimerUsuario.save(function(err, doc){
   if(err)
      console.log(err);
   var PrimerPost = new Post({
       titulo: 'Este es mi primer Post!',
       post  : 'Publicando mi primer post!! que felicidad!!',
       autor : doc._id,
   });
   PrimerPost.slug = slug( PrimerPost.titulo );
   PrimerPost.save( function(err, doc){
     console.log(err);
     console.log(doc);
   })
});

Como podemos ver, lo primero que llamamos es a la creación de un nuevo usuario, le montamos en las propiedades que queremos, al igual que un prototype. Y claro esta la función llamada save, que nos va a retornar el Error y el Documento. Generando un nuevo post, hacemos lo mismo es decir le pasamos los parámetros que queremos. Yo utilizo Slug para tener urls familiares, al igual es un Object. Y guardamos! Y veremos algo así en consola.

{
    _id : 50903550a04313310c000001,
    titulo : 'Este es mi primer Post!',
    post : 'Publicando mi primer post!! que felicidad!!',
    slug : 'este-es-mi-primer-post',
    autor : 2d2ac97cf59cee65f7a38e596c,
}

Claro yo se que los id no son los mismos, que salen en tu consola. Pero seamos un poco desconfiados, vamos a revisar la base de datos!

$ mongo
mongo> show dbs
prueba 0.203125GB
test 0.203125GB
mongo> use prueba
mongo> show collections
posts
users
mongo> db.users.find()
{ "_id" : ObjectId("2d2ac97cf59cee65f7a38e596c"), "name" : "Pepito Perez", "nick" : "pepito", "email" : "pepito@pepito.com" }
mongo> db.posts.find()
{ _id : ObjectId("50903550a04313310c000001"), "titulo" : "Este es mi primer Post!", "post" : "Publicando mi primer post!! que felicidad!!", "slug" : "este-es-mi-primer-post", "autor" : ObjectId("2d2ac97cf59cee65f7a38e596c" }

Genial!! Esta toda la información que subimos a la base de datos. Vamos a buscar a nuestro usuario!

User.findOne().where('nick', 'pepito').exec(function(err, doc){
   console.log(err);
   console.log(doc);
});
null
{
    _id : 2d2ac97cf59cee65f7a38e596c,
    name : "Pepito Perez",
    nick : "pepito",
    email : "pepito@pepito.com",
}

Tomamos el modelo, y le mandamos un findOne, find o findById, le podremos pasar querys de Mongodb. Pero todos estos parámetros en una función como aquí, y nos facilitan el trabajo. Nunca olvidemos hacerle exec. Si al igual podemos buscar los Post, pero también podemos saber de que usuario es! Miremos como se hace

Post.find().populate('autor').exec(function(err, doc){
   console.log(err);
   console.log(doc);
});

Bueno en este caso, buscamos todos los Post. Le montamos la función populate, lo que hace, es buscar según el id, en otra colección y lo anida en la propiedad autor. Miremos que nos muestra la consola!

null
[ {
    _id : 50903550a04313310c000001,
    titulo : 'Este es mi primer Post!',
    post : 'Publicando mi primer post!! que felicidad!!',
    slug : 'este-es-mi-primer-post',
    autor : {
        _id : 2d2ac97cf59cee65f7a38e596c,
        name : "Pepito Perez",
        nick : "pepito",
        email : "pepito@pepito.com",
    }
} ]

Como nos damos cuenta, es un Array o mejor una lista de Object, y dentro de cada Object existe el Object Usuario. Así se vuelve mucho mas fácil tanto la búsqueda como la subida de información. Claro teniendo esto podemos modificar el nick de nuestro usuario y con un .save, se sube a la base de datos.

Bueno esta es la primera parte de esta serie de posts. En el próximo les mostrare como guardar elementos anidados, hacer querys avanzados y validaciones.


  • pjnovas

    Muy bueno!!, me gusto la onda de enganchar entidades (es como que lo haces medio relacional, no?) … esta piola ;)

    • http://twitter.com/AlejoNext Alejandro Ñext

      Técnicamente enlazar documentos es de una DB relacional, pero en este caso la estructura es mucho mas compleja. En pocas palabras los documentos pueden ser diferentes…

  • opensas

    Muy buen articulo. Pero se me escaparon un par de cosas.

    No entiendo bien dónde relacionás la tabla posts con users. Cuando definis el esquema le pasas:

    autor : { type : Schema.Types.ObjectId, ref : ‘autores’ } });

    y despues al traerlo haces

    Post.find().populate(‘autor’).

    No veo donde le decis que ‘autor’lo tiene que traer de la tabla users. O lo hace directamente mongo mediante el ObjectId de posts.autor???

    • http://twitter.com/AlejoNext Alejandro Ñext

      Jejeje tienes razón!! se supone que debe tener el mismo nombre :),

  • Pepepepe

    Muy bueno! Para cuando el próximo comentado?

  • dertin

    Buen articulo, el problema que tengo es que mi Schema tiene un campo donde se deberian guardar un array de DBRef osea:

    Supongo que debe ser asi la definicion en mi Schema:
    idChats : [ { type: Schema.ObjectId, ref: ‘Chats’ } ]

    Pero como uso el populate ¿? Si quiero obtener los datos de todos los Chats referenciados al Usuario.

    • pjnovas

      dertin, el populate lo haces cuando obtenes el padre, por ejemplo:


      mongoose.model('Usuario')
      .find({ 'nombre': 'pepe' })
      .populate('idChats')
      .exec(function(err, usuarios){
      console.dir(usuarios);
      });

      Fijate que deberia ser “chats”, no idChats … ya que es una referencia a al schema “Chat”, tampoco “Chats” ya que es un solo chat tu Schema (que guarde los ids es algo interno, tus referencias son a un schema de entidad y una coleccion de ese schema):

      Usuario tendria:

      "chats": [{ type: ObjectId, ref: 'Chat'}]

      • dertin

        Gracias pjnovas, voy a probar.

        • dertin

          Estoy tratando de entender como seria para hacer un Update y asi agregar otro nuevo ObjectId a “chats”: [ { type: ObjectId, ref: ‘Chat’} ] hasta ahora no me doy cuenta, como hacerlo y como funcionaria.

          Me imagino tener una estructura asi:

          nombre : pepe

          chats {

          ObjectId : 123
          ObjectId : 222
          ObjectId : 333
          }

          Entonces tengo la relacion que pepe, utilizo el chat 123, 222 y 333
          y si mas adelante utiliza pepe otro chat se lo agregaro a chats [{ObjectId}] su referencia.

          Luego tendria que ver como se podria buscar los datos de todos o algunos de los chats del usuario pepe.

          • dertin

            Estoy diseñando diferente la base de datos para NO manejar en un documento multiples refs dentro de un array a otro documento.

  • cmarrero01

    Hola pjnovas, Siguiendo tu articulo trate de integrarlo modularisando mi codigo… pero no me funciona correctamente el populate.. te pego partes de mic odigo y mi resultado para ver si me podes guiar en la solucion.

    Mi esquema es el siguiente:

    Esto esta un archivo llamado userModel.js

    var mongoose = require(‘mongoose’),
    Schema = mongoose.Schema;
    //Listado de paises
    var countrySchema = new Schema({
    country:{ type: String }
    });
    //Usuarios del sistema
    var userSchema = new Schema({
    full_name:{ type: String },
    username: { type: String, required: true, index: { unique: true }},
    email: { type: String, required: true, index: { unique: true } },
    password: { type: String, required: true },
    birthday: { type: String },
    country: { type : Schema.ObjectId, ref : ‘Country’ },
    genre: { type: String, enum: [‘Female’, ‘Male’] }
    });
    //Dejamos el schema para que se acceda desde afuera del archivo
    exports.Country = mongoose.model(‘Country’, countrySchema);
    //Dejamos el schema para que se acceda desde afuera del archivo
    exports.User = mongoose.model(‘User’, userSchema);

    Y en una rchivo aparte tengo lo siguiente:

    //Traemos el modelo de base de datos
    var UserSchema = require(‘../models/userModel.js’);
    //Dejamos accesso desde afuera
    exports = module.exports = User;
    //Controller de usuarios
    function User(){
    var self = this;
    //Select 1 user
    this.getUsers = function(req, res) {
    UserSchema.User.find().populate(‘country’).exec(function(err, user){
    if(!err){
    res.json(user);
    } else {
    console.log(‘ERROR: ‘ + err);
    }
    });
    };

    }

    Y el resultado

    [{

    “full_name”: “Claudio A. Marrero”,
    “username”: “cmarrero01″,
    “email”: “cmarrero01@gmail.com”,
    “password”: “xxxxxxxx”,
    “birthday”: “08/02/1985″,
    “country”: null,
    “genre”: “Male”,
    “_id”: “524f36e34ca6e9c82a000001″,
    “__v”: 0
    }, {
    “full_name”: “Claudio A. Marrero”,
    “username”: “cmarrero02″,
    “email”: “cmarrero02@gmail.com”,
    “password”: “xxxxxx”,
    “birthday”: “08/02/1985″,
    “country”: null,
    “genre”: “Male”,
    “_id”: “524f3a6a0e8dbf0425000002″,
    “__v”: 0
    }]

    Como veras country es Null… que vez de raro en lo que he hecho…?????

    • cmarrero01

      Lo solucione poniendo la referencia del modelo..

      Bueno, quedo resulto, habia que definir el modelo… algo asi:

      var mongoose = require('mongoose'),
      Schema = mongoose.Schema;
      //Listado de paises
      var countrySchema = new Schema({
      name:{ type: String }
      });

      //Usuarios del sistema
      var userSchema = new Schema({
      full_name:{ type: String },
      username: { type: String, required: true, index: { unique: true }},
      email: { type: String, required: true, index: { unique: true } },
      password: { type: String, required: true },
      birthday: { type: String },
      country: { type : Schema.Types.ObjectId, ref : 'Country' },
      genre: { type: String, enum: ['Female', 'Male'] }
      });

      var Country = mongoose.model('Country', countrySchema, 'CountryModel');
      var User = mongoose.model('User', userSchema, 'UserModel');
      exports.Country = Country;
      exports.User = User;

      • Alejandro Ñext

        Disculpa por no haberte respondido. Próximamente se sacara un articulo dedicado a Documentos embeidos y populare.

        • jdbetin

          sera que ya sacaron el artico es que me interesa gracias

  • andres jaraba

    hola hace poco estoy trabajando con mongoose y tengo una duda con una consulta
    quiero saber si se puede hacer esto en mongoose
    select * from id1=1 and id2=2
    union
    select * from id1=2 and id2=1

  • Pingback: leslie

  • Pingback: Gerald