Una introducción de nivel principiante a MongoDB con Node.js

MongoDB es un sistema de gestión de bases de datos (DBMS) multiplataforma y orientado a documentos. Al ser una base de datos NoSQL similar a JSON, funciona muy bien con JavaScript.

Introducción

En este artículo, vamos a hablar sobre cómo usar la base de datos MongoDB con Node.js. Hay un par de formas de hacer esto, incluido el enfoque popular: usar una Biblioteca de modelado de objetos. Mangosta es un ejemplo de una biblioteca de este tipo en Node.js; sin embargo, usaremos el controlador oficial de MongoDB para Node.js.

En este artículo, nos conectaremos a un servidor MongoDB, crearemos documentos, los recuperaremos de la base de datos y finalmente eliminaremos algunos.

Esto se hará a través de algunos scripts, aunque normalmente los integraría con un servidor/aplicación web en lugar de tenerlos en scripts independientes.

¿Qué es MongoDB?

MongoDB es un sistema de gestión de base de datos (DBMS) multiplataforma (se ejecuta en varios sistemas operativos). MongoDB también es una base de datos NoSQL, lo que significa que no utiliza SQL para realizar operaciones en una base de datos.

MongoDB usa documentos que están en formato similar a JSON, conocido como BSON, que es la codificación binaria de JSON.

Está desarrollado como un proyecto de código abierto por MongoDB Inc. bajo la Licencia pública del lado del servidor.

Node y MongoDB funcionan muy bien juntos, en parte porque Mongo usa un motor de JavaScript integrado en la base de datos, ya que JavaScript es bueno para manejar objetos JSON.

En comparación con otras bases de datos, como MySQL, MongoDB es rápido para almacenar ciertos tipos de datos y se puede escalar automáticamente. Es muy simple de implementar y poner en marcha.

Dado que Mongo es una base de datos NoSQL, tiene su propia forma de almacenar datos. Estas son algunas de las construcciones que componen la estructura de la base de datos:

  1. Base de datos: el contenedor que contiene un conjunto de colecciones.
  2. Colección: Conjunto de documentos. Esto es similar a una tabla en una base de datos SQL. Sin embargo, a diferencia de una base de datos SQL, una colección no tiene una estructura establecida ni tipos de datos preconfigurados.
  3. Documentos: un objeto similar a JSON. Esto es similar a una fila en una base de datos SQL. Un objeto JSON también puede contener objetos secundarios, una matriz, etc.
  4. _id: Campo único obligatorio en cada documento. Separa un documento de otro, por lo que podemos identificar cada documento de forma independiente. Si no se proporciona este valor, MongoDB asigna automáticamente un valor aleatorio para el campo.

Configuración del Proyecto

Comencemos con el proyecto y omitamos las indicaciones npm:

1
$ npm init -y

Luego, instalemos el controlador oficial de MongoDB:

1
$ npm install --save mongodb

Para conectarse realmente a la base de datos, deberá asegurarse de que su servicio MongoDB se esté ejecutando en segundo plano o en su máquina de desarrollo. Ejecute el comando mongo en su símbolo del sistema para ingresar al shell de Mongo:

shell mongodb

Ejecutar el comando show dbs; presentará una lista de las bases de datos actuales:

mostrar mongodb

Puede salir del shell ejecutando el comando exit.

A diferencia de las bases de datos SQL, que requieren que se cree una base de datos antes de su uso, no es necesario crear una base de datos o una colección de antemano. Se crearán automáticamente cuando sea necesario.

Implementación de operaciones CRUD

Con nuestro proyecto inicializado y MongoDB instalado, podemos comenzar a escribir algo de lógica CRUD.

Conexión a la base de datos {#conexiónalabasede datos}

Por supuesto, para usar MongoDB en el código, necesitamos importar el módulo:

1
const mongodb = require('mongodb');

Entonces, instanciamos un cliente:

1
const MongoClient = mongodb.MongoClient;

El cliente necesita saber dónde conectarse, por lo que le proporcionaremos una url y un dbName:

1
2
3
4
5
// Connection URL
const url = 'mongodb://localhost:27017';

// Database Name
const dbName = 'userdb';

Finalmente, intentemos conectarnos a la base de datos:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Use the connect method to create a connection w/ the database
MongoClient.connect(url, (err, client) => {
    if (err) {
        throw err;
        return;
    }

    console.log('Database connection successful');

    // This objects holds the refrence to the db
    const db = client.db(dbName);

    client.close();
});

Si se ha conectado a la base de datos con éxito, debería ver el resultado:

1
Database connection successful

De lo contrario, recibirá un mensaje de error. Compruebe si el servidor está funcionando y si el nombre de usuario y la contraseña son correctos en ese caso.

Como puedes ver en el ejemplo, el método MongoClient.connect toma dos parámetros, la URL de la base de datos y la función de devolución de llamada.

La función de devolución de llamada tiene dos parámetros: err y cliente.

El primer parámetro contendría un error si hay algún problema de red o cualquier otro problema con la conexión a la base de datos. Si no hay problemas, el error será nulo.

El segundo parámetro es el objeto del cliente, que usamos para interactuar con la base de datos.

La propiedad db del cliente contiene una referencia a la base de datos. Para realizar cualquier acción en esa base de datos, usamos esta referencia.

Crear un documento

Para realizar cualquier acción sobre la base de datos, tienes que estar conectado a ella, obviamente. Con Mongo, hay dos formas de insertar documentos en la base de datos. La primera forma es agregar un solo documento a la vez. Podemos usar el método insertOne() para lograr esto:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
const collection = db.collection('userdb');

// Insert one document
collection.insertOne({
    firstName: 'john',
    lastName: 'doe',
    age: 21,
    hobbies: [
        'Reading books',
        'Collecting stamps'
    ]
}, (err, result) => {
    if (err) {
        console.log(err);
        return;
    }
    
    console.log(result.result);
});

El parámetro de resultado de la devolución de llamada contiene información sobre la consulta. Tiene un campo llamado resultado que se parece a:

1
result: { n: 1, ok: 1 }

n es el número de documentos insertados. ok es el estado he del comando.

No tienes que crear explicitly una base de datos llamada userdb, o una colección llamada users antes de insertar el documento. La base de datos y la colección se crearán automáticamente.

El segundo método le permite agregar múltiples documentos a la vez. Podemos usar el método insertMany() para lograr esto:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Insert multiple documents
collection.insertMany([
    {
        firstName: 'john',
        lastName: 'doe',
        age: 21,
        hobbies: [
            'Reading books',
            'Collecting stamps'
        ]
    }, {
        firstName: 'anna',
        lastName: 'dias',
        age: 20,
        hobbies: []
    }
], (err, result) => {
    if (err) {
        console.log(err);
        return;
    }
    
    console.log(result.ops);
});

Ejecutar este fragmento de código producirá:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[ { _id: 1,
    firstName: 'john',
    lastName: 'doe',
    age: 21,
    hobbies: [ 'Reading books', 'Collecting stamps' ] },
  { _id: 2,
    firstName: 'anna',
    lastName: 'dias',
    age: 20,
    hobbies: [] } ]

Dado que no hemos definido un _id para ninguno de estos documentos, podemos obtener el _id asignado del objeto result['ops'] si alguna vez necesitamos acceder al _id generado.

Además de eso, puedes definir el _id tú mismo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// Insert one document
collection.insertOne({
    _id: 'someuniqueid',    // Our specified ID
    firstName: 'john',
    lastName: 'doe',
    age: 21,
    hobbies: [
        'Reading books',
        'Collecting stamps'
    ]
}, (err, result) => {
    if (err) {
        console.log(err);
        return;
    }
    
    console.log(result.result);
});

Recuperando documentos {#recuperando documentos}

Recuperar todos los documentos

Primero, veamos cómo obtener todos los documentos de una colección:

1
2
3
4
5
6
7
// Find all documents
collection.find({}).toArray((err, docs) => {
    if (err) {
        throw err;
    }
    console.log(docs)
});

Ejecutar este fragmento de código nos dará:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[{ _id: 1,
    firstName: 'john',
    lastName: 'doe',
    age: 21,
    hobbies: [ 'Reading books', 'Collecting stamps' ] },
  { _id: 2,
    firstName: 'anna',
    lastName: 'dias',
    age: 20,
    hobbies: [] } ]

Como puede ver en el ejemplo, hemos pasado un objeto vacío ({}) como consulta.

Según la documentación, el método toArray() devuelve una matriz que contiene todos los documentos de un cursor. El método itera el cursor por completo, carga todos los documentos en la RAM y agota el cursor.

Los documentos obtenidos por la colección se asignarán al parámetro docs en la función de devolución de llamada.

Buscar documentos con un filtro de consulta

El siguiente método para encontrar un documento es utilizar un filtro de consulta. Por ejemplo, la siguiente consulta selecciona a los usuarios con el primer nombre john:

1
2
3
{
    'firstName': 'john'
}

Y para hacer esto en código:

1
2
3
4
5
6
7
8
collection.find({
    firstName: 'john'
}).toArray((err, docs) => {
    if (err) {
        throw err;
    }
    console.log(docs)
});

Este código dará como resultado:

1
2
3
4
5
[{ _id: 1,
    firstName: 'john',
    lastName: 'doe',
    age: 21,
    hobbies: [ 'Reading books', 'Collecting stamps' ] } ]

Evidentemente, se devuelven todos los registros con firstName john.

Actualización de un documento

La siguiente operación de la que vamos a hablar es la actualización de un documento. Para actualizar un solo documento, similar a recuperar un documento, podemos usar el método updateOne():

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
collection.updateOne(
    // The query filter
    {
        firstName: 'john'
    },
    // The update values
    {
        $set: {
            lastName: 'well',
            edited: true
        }
    },
    (err, result) => {
        if (err) {
            throw err;
        }
        console.log(result.result);
    }
);

Este código da como resultado:

1
{ n: 1, nModified: 1, ok: 1 }

Como puedes ver en el ejemplo, el método updateOne() acepta tres parámetros. El primero es el filtro de consulta. El segundo son los valores de actualización. La tercera es la función de devolución de llamada, que acepta el error y los resultados como parámetros.

Nuevamente, los resultados aquí nos notifican el estado (ok), la cantidad de documentos seleccionados para la actualización (n) y la cantidad de documentos actualizados (nModified).

n puede ser mayor que nModified, si un campo se actualiza con el valor que ya tenía.

Usando esta consulta, hemos seleccionado un documento donde el campo firstName es john y hemos cambiado el lastName de ese documento a well. Además, hemos agregado un campo llamado editado y lo configuramos como verdadero. Fíjate como no hemos necesitado especificar o seguir ningún esquema durante todo este proceso. Mongo solo acepta cualquier dato que le envíes.

Si está utilizando el método updateOne(), la consulta seleccionará el primer documento con el campo coincidente. Si hay varios documentos con un campo del mismo valor, usar el método updateMany() los actualizará a todos, lo que en algunos casos podría no ser lo que queremos hacer.

Nota: si está utilizando el método updateOne(), lo ideal sería que la consulta solo seleccionara un único documento. De lo contrario, no podemos predecir el documento que podría actualizarse. Así que tenga esto en cuenta y tenga cuidado al usar un filtro de consulta que podría coincidir con varios documentos.

También podemos editar todos los documentos que cumplan la condición de que el campo firstName sea john:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
collection.updateMany(
    // The query filter
    {
        firstName: 'john'
    },
    // The update values
    {
        $set: {
            lastName: 'well',
            edited: true
        }
    },
    (err, result) => {
        if (err) {
            throw err;
        }
        console.log(result.result);
    }
);

El método updateMany() es similar al método updateOne(), excepto que actualiza todos los documentos que coinciden con el filtro de consulta.

Eliminación de un documento

Podemos usar los métodos deleteOne() o deleteMany() para eliminar un documento de una colección:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
collection.deleteOne(
    // The query filter
    {
        firstName: 'john'
    },
    (err, result) => {
        if (err) {
            throw err;
        }
        console.log(result.result);
    }
);

Este código da como resultado:

1
{ n: 1, ok: 1 }

Nuevamente, de manera similar a los ejemplos anteriores: el primer parámetro aceptado es la consulta de filtro y el segundo parámetro es la función de devolución de llamada. La función de devolución de llamada devuelve un error o un resultado.

Ejecutar este fragmento de código eliminará un documento que coincida con la consulta; en este caso, un documento en el que el campo firstName es john. Nuevamente, esto solo eliminará el primer documento que coincida con la consulta.

También puede usar el método deleteMany para eliminar todos los documentos que están seleccionados:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
collection.deleteMany(
    // The query filter
    {
        firstName: 'john'
    },
    (err, result) => {
        if (err) {
            throw err;
        }
        console.log(result.result);
    }
);

Conclusión

MongoDB es una base de datos liviana NoSQL popular que es realmente fácil de implementar y usar con Node. Escribimos una aplicación Node muy simple que interactúa con un MongoDB para crear, recuperar y eliminar colecciones.

Como siempre, el código fuente está disponible en GitHub. s-mongo).