Cómo relacionar tus modelos en MongoDB

18 enero, 2015

3 minutos de lectura

💻 Desarrollo

¿Ves alguna errata o quieres modificar algo? Haz una Pull Request

MongoDB es una base de datos no relacional, es decir no es como las típicas bases de datos SQL (MySQL, Oracle, PostgreSQL, etc...) donde existen relaciones entre una tabla y otra. Veamos un ejemplo clásico.

Imaginemos una base de datos de libros. Tendríamos una tabla con los títulos de los libros y otra con los datos de los autores. El campo autor en la tabla de libros, apuntaría a un ID o clave primaria de un autor de la tabla autores.

En MongoDB podemos hacer algo parecido, por medio de referencias y el método populate de MongoDB .

Sigamos con el ejemplo anterior. Un modelo autor en Node.js usando mongoose sería tal que así:

var mongoose = require("mongoose");
var Schema = mongoose.Schema;

var autorSchema = new Schema({
  nombre: String,
  biografia: String,
  fecha_de_nacimiento: Date,
  nacionalidad: String,
});

module.exports = mongoose.model("Autor", autorSchema);

y supongamos un modelo sencillo para libro de la siguiente manera:

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Autor = mongoose.model('Autor');

var libroSchema = new Schema({
	titulo: String
    paginas: Number,
    isbn: String,
    autor: { type: Schema.ObjectId, ref: "Autor" }
});

module.exports = mongoose.model("Libro", libroSchema);

Si nos fijamos, para el campo autor en el modelo libro hemos usado el tipo Schema.ObjectId y la referencia al modelo Autor. Esto nos permitirá establecer la relación entre un campo de una tabla y otra.

Pero si no tenemos consultas SQL, y pedimos una lista de todos los libros en la base de datos ¿Qué datos nos llegarán?

Lo primero que programaríamos sería esto (en Express/Node.js):

app.get("/libros", function (req, res) {
  Libro.find({}, function (err, libros) {
    res.status(200).send(libros);
  });
});

y si tenemos varios libros registrados en la base de datos, nos devolvería un JSON parecido a este:

[{
    "_id": "547db17cbe9956a000001",
    "__v": 0
    "titulo": "Juego de Tronos",
    "paginas": 150,
    "isbn": "0-553-57340-4",
    "autor": "547db17cbe9958b000001"
},
{
    "_id": "547db17cbe9956a000001",
    "__v": 0
    "titulo": "Choque de Reyes",
    "paginas": 340,
    "isbn": "0-553-57340-5",
    "autor": "547db17cbe9958b000001"
},
{
    "_id": "547db17cbe9956a000001",
    "__v": 0
    "titulo": "Tormenta de Espadas",
    "paginas": 620,
    "isbn": "0-553-57340-7",
    "autor": "547db17cbe9958b000001"
}]

En el campo autor obtenemos la referencia en formato ObjectID del autor, pero no su ficha completa. ¿Qué pasa si queremos mostrar en un sólo JSON toda la información para poder pintarla de una sola llamada en nuestra webapp? Para eso necesitamos hacer uso del método populate de MongoDB que también implementa la librería mongoose.

En nuestro controlador anterior, debemos ampliarlo con lo siguiente:

app.get("/libros", function (req, res) {
  Libro.find({}, function (err, libros) {
    Autor.populate(libros, { path: "autor" }, function (err, libros) {
      res.status(200).send(libros);
    });
  });
});

La línea Autor.populate(libros, {path: "autor"},...); toma el array de objectos libros y le indica que en la ruta autor lo "popule" con los datos del modelo Autor. Quedando una respuesta más completa como este ejemplo:

[{
    "_id": "547db17cbe9956a000001",
    "__v": 0
    "titulo": "Juego de Tronos",
    "paginas": 150,
    "isbn": "0-553-57340-4",
    "autor": {
    	"_id": "547db17cbe9958b000001",
        "__v": 0,
        "nombre": "George R. R. Martin",
        "biografia": "American novelist...",
        "fecha_de_nacimiento": "1948-09-20T00:00:00.000Z",
        "nacionalidad": "USA"
    }
},
{
    "_id": "547db17cbe9956a000001",
    "__v": 0
    "titulo": "Choque de Reyes",
    "paginas": 340,
    "isbn": "0-553-57340-5",
    "autor": {
    	"_id": "547db17cbe9958b000001",
        "__v": 0,
        "nombre": "George R. R. Martin",
        "biografia": "American novelist...",
        "fecha_de_nacimiento": "1948-09-20T00:00:00.000Z",
        "nacionalidad": "USA"
    }
},
{
    "_id": "547db17cbe9956a000001",
    "__v": 0
    "titulo": "Tormenta de Espadas",
    "paginas": 620,
    "isbn": "0-553-57340-7",
    "autor": {
    	"_id": "547db17cbe9958b000001",
        "__v": 0,
        "nombre": "George R. R. Martin",
        "biografia": "American novelist...",
        "fecha_de_nacimiento": "1948-09-20T00:00:00.000Z",
        "nacionalidad": "USA"
    }
}]

Podemos ver que el objeto _id del campo autor ha sido sustituido por el modelo autor completo al que hace referencia.

De esta manera conseguimos un comportamiento parecido al típico relacional en las bases de datos SQL en MongoDB.

© 2023 Carlos Azaustre | Made with 💻 in 🇪🇸