Este tutorial te introduce en la base de datos MongoDB. Descubrirás cómo instalar el software, manipular los datos y aplicar las técnicas de diseño de datos a tus propias aplicaciones.

Todos los ejemplos se han desarrollado con MongoDB 5, pero la mayoría funcionarán con versiones anteriores o posteriores. El código puede introducirse directamente en una aplicación cliente o en el shell de MongoDB (mongo o mongosh) para consultar y actualizar la base de datos.

¿Qué es MongoDB?

MongoDB es una base de datos NoSQL de código abierto. NoSQL significa que la base de datos no utiliza tablas relacionales como una base de datos SQL tradicional.

Hay una serie de tipos de bases de datos NoSQL, pero MongoDB almacena los datos en objetos similares a JavaScript, conocidos como documents, cuyo contenido tiene este aspecto:

{
  _id: "123",
  name: "Craig"
}


Aunque MongoDB se ha convertido en sinónimo del framework basado en JavaScript Node.js, hay controladores oficiales de la base de datos MongoDB para la mayoría de los frameworks, lenguajes y tiempos de ejecución, incluidos Node.js, PHP y Python. También puedes optar por bibliotecas como Mongoose, que ofrecen un mayor nivel de abstracción o funciones de mapeo relacional de objetos (ORM).

A diferencia de las tablas SQL, no hay límites estructurales sobre lo que puedes almacenar en MongoDB. Los esquemas de datos no se imponen: Puedes almacenar lo que quieras donde quieras. Esto hace que MongoDB sea ideal para estructuras de datos más orgánicas —o desordenadas—.

Piensa en una agenda de contactos. Los individuos pueden tener a menudo varios números de teléfono. Podrías definir tres campos de teléfono en una tabla SQL, pero eso sería demasiado para algunos contactos y demasiado poco para otros. En última instancia, necesitarás una tabla de teléfonos independiente, lo que añade más complejidad.

En MongoDB, esos números de teléfono podrían definirse como una matriz ilimitada de objetos en el mismo documento:

{
  _id: "123",
  name: "Craig",
  telephone: [
    { home: "0123456789" },
    { work: "9876543210" },
    { cell: "3141592654" }
  ]
}

Ten en cuenta que MongoDB utiliza una notación de objetos similar a la de JavaScript para las actualizaciones y consultas de datos, lo que puede plantear algunos problemas si estás acostumbrado a SQL.

Elementos de MongoDB

Antes de seguir adelante, echemos un vistazo a lo que hace que MongoDB funcione. Utilizaremos este vocabulario a lo largo de este artículo.

  • Documento: Un único objeto individual en un almacén de datos, análogo a un registro o fila en una tabla de una base de datos SQL.
  • Campo: Un único dato dentro de un documento, como un nombre o un número de teléfono, análogo a un campo o columna de una tabla SQL.
  • Colección: Un conjunto de documentos similares, análogo a una tabla SQL. Aunque podrías poner todos tus documentos en una sola colección, suele ser más práctico agruparlos en tipos específicos. En una agenda de contactos podrías tener una colección para personas y otra para empresas.
  • Base de datos: Una colección de datos relacionados, con un significado idéntico al de una base de datos SQL.
  • Esquema: Un esquema define las estructuras de datos. En las bases de datos SQL debes definir definiciones de tablas con campos y tipos asociados antes de poder almacenar datos. Esto no es necesario en MongoDB, aunque es posible crear un esquema que valide los documentos antes de añadirlos a una colección.
  • Índice: Una estructura de datos utilizada para mejorar el rendimiento de las consultas, con un significado idéntico al de los índices de SQL.
  • Clave primaria: Un identificador único para cada documento. MongoDB añade automáticamente un campo _id único e indexado a cada documento de una colección.
  • Desnormalización: En las bases de datos SQL, la «normalización» es una técnica utilizada para organizar los datos y eliminar la duplicación. En MongoDB, se fomenta la «desnormalización». Se repiten activamente los datos y un solo documento podría contener toda la información necesaria.
  • Uniones: SQL proporciona un operador JOIN para poder recuperar datos de varias tablas normalizadas en una sola consulta. Las uniones no eran posibles en MongoDB hasta la versión 3.6 y las limitaciones siguen existiendo. Esta es otra razón por la que los datos deben desnormalizarse en documentos autocontenidos.
  • Transacciones: Cuando una actualización cambia dos o más valores en un mismo documento, MongoDB se asegura de que todas tengan éxito o fallen. Las actualizaciones en dos o más documentos deben estar envueltas en una transacción. MongoDB admite las transacciones desde la versión 4.0, pero se requiere un conjunto de réplicas multiservidor o un clúster fragmentado. Las instalaciones de ejemplo que se muestran a continuación utilizan un único servidor, por lo que las transacciones no son posibles.

Cómo Instalar MongoDB

Tienes tres opciones para usar MongoDB en tu máquina local. Te guiaremos por cada una de ellas.

1. Utilizar Docker (Recomendado)

Docker es una herramienta de gestión de software que puede instalar, configurar y ejecutar MongoDB o cualquier otra aplicación en minutos.

Instala Docker y Docker Compose y luego crea una carpeta de proyecto con un único archivo llamado docker-compose.yml que contenga el siguiente contenido (ten en cuenta que las sangrías son esenciales):

version: '3'

services:

  mongodb:
    image: mongo:5
    environment:
      - MONGO_INITDB_ROOT_USERNAME=root
      - MONGO_INITDB_ROOT_PASSWORD=pass
      - MONGO_INITDB_DATABASE=mongodemo
    container_name: mongodb
    volumes:
      - dbdata:/data/db
    ports:
      - "27017:27017"

  adminer:
    image: dehy/adminer
    container_name: adminer
    depends_on:
      - mongodb
    ports:
      - "8080:80"

volumes:
  dbdata:

Accede a la carpeta desde la línea de comandos y ejecuta

docker-compose up

Se descargará y lanzará la última versión de MongoDB 5. La primera vez que se ejecute tardará unos minutos, pero las siguientes serán mucho más rápidas.

Ten en cuenta que:

  • Se define una cuenta de administrador de MongoDB con el ID «root» y la contraseña «pass».
  • Los datos se guardan entre reinicios en un volumen Docker llamado dbdata.
  • También se proporciona el cliente de base de datos Adminer.

Puedes utilizar cualquier cliente de base de datos MongoDB para conectarte a localhost:27017 utilizando el ID «root» y la contraseña «pass». Alternativamente, puedes acceder a Adminer en http://localhost:8080/ e iniciar sesión con las siguientes credenciales:

  • Sistema: MongoDB (alfa)
  • Servidor: host.docker.internal
  • Nombre de usuario: root
  • Contraseña: pass
Inicio de sesión de Adminer
Inicio de sesión de Adminer

Adminer te permite inspeccionar las colecciones y los documentos. Sin embargo, ten en cuenta que las colecciones se denominan «tablas»:

Vista de la colección de Adminer
Vista de la colección de Adminer

Para ejecutar comandos, puedes utilizar la Shell de MongoDB (mongosh) o el entorno de línea de comandos REPL (Read Eval Print Loop) heredado de mongo.

Accede al shell bash del contenedor Docker MongoDB:

docker exec -it mongodb bash

A continuación, lanza el shell de MongoDB con el ID y la contraseña:

mongosh -u root -p pass

(Si lo prefieres, puedes utilizar el comando heredado mongo.)

A continuación, puedes emitir comandos de MongoDB como los siguientes:

  • show dbs; – Mostrar todas las bases de datos
  • use mongodemo; – Utilizar una base de datos específica
  • show collections; – Listar las colecciones de una base de datos
  • db.person.find(); – Listar todos los documentos de una colección
  • exit; – Salir/cerrar el shell

Cierra MongoDB ejecutando el siguiente comando desde el directorio del proyecto:

docker-compose down

2. Utilizar un Proveedor en la Nube (Sin Instalación)

Puedes utilizar una instancia de MongoDB alojada, por lo que no es necesario instalar nada localmente. Es imprescindible una conexión a Internet y la velocidad de respuesta dependerá del proveedor de alojamiento y de tu ancho de banda. La mayoría de los servicios cobrarán una cuota mensual y/o por uso de megabytes.

El hosting normalmente proporcionará detalles para que puedas administrar la base de datos de forma remota utilizando el software cliente de MongoDB.

3. Instalar MongoDB Localmente

MongoDB se puede instalar y configurar en Linux, Windows o Mac OS. Hay dos ediciones disponibles:

  1. Una Edición Empresarial Comercial
  2. Una Edición Comunitaria de código abierto (utilizada en este tutorial)

La página de instalación de MongoDB proporciona instrucciones para varios sistemas operativos. En general:

¡Asegúrate de seguir las instrucciones cuidadosamente para que tu instalación tenga éxito!

Cómo Acceder a Tu Base de Datos MongoDB

Ahora que tu base de datos MongoDB está instalada, es el momento de aprender a gestionarla. Vamos a hablar de lo que tendrás que hacer para acceder y trabajar con tu base de datos.

1. Instalar un Cliente MongoDB

Para administrar las bases de datos se necesita una aplicación cliente MongoDB. Si utilizas una instalación en la nube o local, te recomendamos que instales la línea de comandos mongosh MongoDB Shell.

Adminer es un cliente de base de datos basado en la web que soporta MongoDB, aunque actualmente se limita a inspeccionar colecciones. Adminer se puede descargar como un único script PHP, pero ya está configurado si utilizas la instalación Docker o tienes instalado DevKinsta.

Una aplicación cliente con interfaz gráfica de usuario proporciona una interfaz mejor para actualizar e inspeccionar los datos. Hay varias opciones disponibles, incluida la gratuita y multiplataforma MongoDB Compass:

MongoDB Compass
MongoDB Compass

Studio 3T, otro contendiente con interfaz gráfico, ofrece una aplicación comercial que otorga una funcionalidad limitada de forma gratuita:

Studio 3T
Studio 3T

Puedes acceder a tu base de datos MongoDB con una de estas herramientas utilizando cualquiera de las siguientes:

  1. El nombre de red de la máquina, la URL o la dirección IP (localhost para una instalación local).
  2. El puerto de MongoDB (27017 por defecto).
  3. Un ID de usuario y una contraseña. Normalmente se define un usuario root durante la instalación.

2. Establecer y Guardar las Credenciales de Acceso a la Base de Datos

El administrador root tiene acceso sin restricciones a todas las bases de datos. En general, debes utilizar un usuario personalizado con privilegios específicos para limitar el acceso y aumentar la seguridad.

Por ejemplo, el siguiente comando crea un usuario llamado myuser con la contraseña mypass que tiene acceso de lectura y escritura a la base de datos mydb:

use mydb;

db.createUser({
  user: "myuser",
  pwd: "mypass",
  roles: [
    { role: "readWrite", db: "mydb" }
  ]
});

Cómo Insertar Nuevos Documentos en MongoDB

No es necesario definir una base de datos o una colección antes de insertar tu primer documento. Utilizando cualquier cliente de MongoDB, simplemente pasa a una base de datos llamada mongodemo:

use mongodemo;

A continuación, inserta un único documento en una nueva colección de personas:

db.person.insertOne(
  {
    name: 'Abdul',
    company: 'Alpha Inc',
    telephone: [
      { home: '0123456789' },
      { work: '9876543210' }
    ]
  }
);

Visualiza el documento ejecutando una consulta que devuelva todos los resultados de la colección de personas:

db.person.find({});

El resultado será algo así:

{
  "_id" : ObjectId("62442429854636a03f6b8534"),
  name: 'Abdul',
  company: 'Alpha Inc',
  telephone: [
    { home: '0123456789' },
    { work: '9876543210' }
  ]
}

Cómo Insertar Múltiples Documentos

Puedes insertar múltiples documentos en una colección pasando un array a insertMany(). El siguiente código crea documentos adicionales de persona y una nueva colección de empresa:

db.person.insertMany([
  {
    name: 'Brian',
    company: 'Beta Inc'
  },
  {
    name: 'Claire',
    company: 'Gamma Inc',
    telephone: [
      { cell: '3141592654' }
    ]
  },
  {
    name: 'Dawn',
    company: 'Alpha Inc'
  },
  {
    name: 'Esther',
    company: 'Beta Inc',
    telephone: [
      { home: '001122334455' }
    ]
  },
  {
    name: 'George',
    company: 'Gamma Inc'
  },
  {
    name: 'Henry',
    company: 'Alpha Inc',
    telephone: [
      { work: '012301230123' },
      { cell: '161803398875' }
    ]
  },
]);

db.company.insertMany([
  {
    name: 'Alpha Inc',
    base: 'US'
  },
  {
    name: 'Beta Inc',
    base: 'US'
  },
  {
    name: 'Gamma Inc',
    base: 'GB'
  },
]);

¿De Dónde Viene el _id?

MongoDB asigna automáticamente un _id a cada documento de una colección. Se trata de un ObjectID, un valor BSON (Binary Javascript Object Notation) que contiene:

  • La época de Unix en segundos en el momento de la creación (4 bytes)
  • Un ID de máquina/proceso de 5 bytes
  • Un contador de 3 bytes que comienza con un valor aleatorio

Es la clave primaria del documento. Se garantiza que el valor hexadecimal de 24 caracteres es único en todos los documentos de la base de datos, y no puede cambiarse una vez insertado.

MongoDB también proporciona una función getTimeStamp() para que puedas obtener la fecha/hora de creación del documento sin tener que establecer explícitamente un valor. Como alternativa, puedes definir tu propio valor _id único cuando se crea un documento.

Desnormalización de los Datos

Los registros insertados anteriormente establecen la empresa de cada usuario en una cadena como «Alpha Inc». Esto no es recomendable en las bases de datos SQL normalizadas:

  • Es fácil cometer un error: un usuario está asignado a «Alpha Inc» mientras que otro es «Alpha Inc.» (punto adicional al final). Se tratan como empresas diferentes.
  • Actualizar el nombre de una empresa puede suponer actualizar muchos registros.

La solución de SQL es crear una tabla de empresas y asociar una empresa a una persona utilizando su clave primaria (probablemente un número entero). La clave seguiría siendo la misma independientemente de los cambios de nombre de la empresa y la base de datos puede aplicar reglas para garantizar la integridad de los datos.

En MongoDB se fomenta la desnormalización. Deberías repetir activamente los datos y un solo documento podría contener toda la información necesaria. Esto tiene varias ventajas:

  • Los documentos son autocontenidos y más fáciles de leer — no hay necesidad de hacer referencia a otras colecciones.
  • El rendimiento de la escritura puede ser más rápido que el de una base de datos SQL porque se aplican menos reglas de integridad de los datos.
  • La fragmentación — o distribución de los datos en varias máquinas — es más fácil porque no es necesario hacer referencia a los datos de otras colecciones.

Consultas Simples en MongoDB

Puedes listar todos los documentos de una colección, como el de persona, utilizando un find() vacío:

db.person.find({})

El método count() devuelve el número de documentos (en nuestro caso, ese número será 7):

db.person.find({}).count();

El método sort() devuelve los documentos en el orden que prefieras, por ejemplo, por  orden alfabético inverso del nombre:

db.person.find({}).sort({ name: -1 });

También puedes limitar el número de documentos devueltos, por ejemplo, encontrar los tres primeros nombres:

db.person.find({}).sort({ name: 1 }).limit(2);

Puedes buscar registros específicos definiendo una consulta con uno o varios campos, por ejemplo, localizar todos los documentos de personas cuyo nombre sea «Claire»:

db.person.find({ name: 'Claire' });

También se admiten operadores lógicos como $and, $or, $not, $gt (mayor que), $lt (menor que) y $ne (no igual), por ejemplo, localizar todos los documentos de persona en los que la empresa sea «Alpha Inc» o «Beta Inc»:

db.person.find({
  $or: [
    { company: 'Alpha Inc' },
    { company: 'Beta Inc' }
  ]
});

En la base de datos del ejemplo, el mismo resultado podría conseguirse con $nin (no en) para extraer todos los documentos en los que la empresa no sea «Gamma Inc»:

db.person.find({
  company: { $nin: ['Gamma Inc'] }
});

Un segundo objeto valor en el método find() establece una proyección que define los campos devueltos. En este ejemplo, sólo se devuelve el nombre (ten en cuenta que siempre se devuelve el _id del documento, a menos que se desactive explícitamente):

db.person.find(
  { name:'Claire' },
  { _id:0, name:1 }
);

El resultado:

{
  "name" : "Claire"
}

La consulta $elemMatch te permite encontrar elementos en un array, como por ejemplo todos los documentos en los que el array de teléfonos tiene un elemento de trabajo. El mismo $elemMatch puede utilizarse en la proyección para mostrar sólo el número de trabajo:

db.person.find(
  {
    telephone: { $elemMatch: { work: { $exists: true }} }
  },
  {
    _id: 0,
    name:1,
    telephone: { $elemMatch: { work: { $exists: true }}}
  }
);

El resultado:

{
  "name" : "Abdul",
  "telephone" : [
    { "work" : "9876543210" }
  ]
},
{
  "name" : "Henry",
  "telephone" : [
    { "work" : "012301230123" }
  ]
}

Uso de Cursores en MongoDB

La mayoría de los controladores de bases de datos permiten devolver los resultados de una consulta como un conjunto o una estructura de datos similar. Sin embargo, si ese conjunto contiene miles de documentos, esto podría provocar problemas de memoria.

Como la mayoría de las bases de datos SQL, MongoDB admite el concepto de cursores. Los cursores permiten a una aplicación leer los resultados de la consulta de uno en uno antes de pasar al siguiente elemento o abandonar la búsqueda.

Los cursores también pueden utilizarse desde un shell de MongoDB:

let myCursor = db.person.find( {} );

while ( myCursor.hasNext() ) {
  print(tojson( myCursor.next() ));
}

Cómo Crear Índices en MongoDB

La colección de personas contiene actualmente siete documentos, por lo que cualquier consulta no será computacionalmente cara. Sin embargo, imagina que tienes un millón de contactos con un nombre y una dirección de correo electrónico. Los contactos pueden estar ordenados por nombre, pero las direcciones de correo electrónico estarán en un orden aparentemente aleatorio.

Si necesitas buscar un contacto por su correo electrónico, la base de datos tendría que buscar hasta un millón de elementos antes de encontrar una coincidencia. Al añadir un índice en el campo de correo electrónico se crea una «tabla» de búsqueda en la que los correos electrónicos se almacenan por orden alfabético. Ahora la base de datos puede utilizar algoritmos de búsqueda más eficaces para localizar a la persona correcta.

Los índices se vuelven esenciales a medida que aumenta el número de documentos. En general, debes aplicar un índice a cualquier campo al que se pueda hacer referencia en una consulta. Podrías aplicar índices a todos los campos, pero ten en cuenta que esto ralentizaría la actualización de los datos y aumentaría el espacio de disco necesario, ya que sería necesario reindexar.

MongoDB ofrece una serie de tipos de índices.

Índices de Un Solo Campo

La mayoría de los índices se aplicarán a campos únicos, por ejemplo, indexar el campo nombre en orden alfabético ascendente:

db.person.createIndex({ name: 1 });

El uso de -1 invierte el orden. Esto sería poco beneficioso en nuestro ejemplo, pero podría ser práctico si tienes un campo de fecha en el que tienen prioridad los eventos más recientes.

Otros tres índices son útiles en la base de datos de ejemplo de mongodemo:

db.person.createIndex( { company: 1 } );
db.company.createIndex( { name: 1 } );
db.company.createIndex( { base: 1 } );

Índices Compuestos sobre Múltiples Campos

Se pueden especificar dos o más campos en un índice, por ejemplo:

db.person.createIndex( { name: 1, company: 1 } );

Esto puede ser útil cuando un campo se utiliza habitualmente junto con otro en las consultas de búsqueda.

Índices Multiclave sobre un Array o Elementos de Objetos

Los documentos pueden ser complejos y a menudo es necesario indexar campos más profundos en la estructura, como el número de teléfono del trabajo:

db.products.createIndex( { 'telephone.work': 1 } );

Índices Wildcard

Un wildcard puede indexar todos los campos de un documento. Esto suele ser práctico en documentos más pequeños y sencillos que pueden consultarse de diversas maneras:

db.company.createIndex( { '$**': 1 } );

Índices de Texto Completo

Un índice de texto te permite crear consultas similares a las de un motor de búsqueda que pueden examinar el texto en todos los campos de la cadena y ordenarlo por relevancia. Puedes limitar el índice de texto a campos específicos:

db.person.createIndex( { name: "text", company: "text" } );

…o crear un índice de texto en todos los campos de cadena:

db.person.createIndex( { "$**": "text" } );

El operador $texto te permite buscar en este índice, como por ejemplo encontrar todos los documentos en los que se hace referencia a «Gamma»:

db.person.find({ $text: { $search: 'Gamma' } });

Ten en cuenta que las búsquedas de texto completo suelen requerir cinco o más caracteres para obtener resultados útiles.

Otros Tipos de Índices

MongoDB proporciona otros tipos de índices especializados:

Cómo Gestionar los Índices de MongoDB

Los índices definidos en una colección se pueden examinar con:

db.person.getIndexes();

Esto devuelve un array de resultados como:

[
  {
    "v" : 2.0,
    "key" : { "_id" : 1.0 },
    "name" : "_id_"
  },
  {
    "v" : 2.0,
    "key" : { "company" : 1.0 },
    "name" : "company_1"
  },
  {
    "v" : 2.0,
    "key" : { "name" : 1.0 },
    "name" : "name_1"
  }
]

La «clave» define el campo y el orden, mientras que «nombre» es un identificador único para ese índice, como «company_1» para el índice del campo company.

La eficacia de un índice puede examinarse añadiendo un método .explain() a cualquier consulta, por ejemplo:

db.person.find({ name:'Claire' }).explain();

Esto devuelve un gran conjunto de datos, pero el objeto «winningPlan» muestra el «indexName» utilizado en la consulta:

"winningPlan" : {
  "stage" : "FETCH",
  "inputStage" : {
    "stage" : "IXSCAN",
    "keyPattern" : { "name" : 1.0 },
    "indexName" : "name_1",
  }
}

Si es necesario, puedes eliminar un índice haciendo referencia a su nombre:

db.person.dropIndex( 'name_1' );

o utilizando el documento de especificación del índice:

db.person.dropIndex({ name: 1 });

El método dropIndexes() te permite eliminar más de un índice en un solo comando.

Uso de los Esquemas de Validación de Datos de MongoDB

A diferencia de SQL, los esquemas de definición de datos no son necesarios en MongoDB. Puedes enviar cualquier dato a cualquier documento de cualquier colección en cualquier momento.

Esto proporciona una libertad considerable. Sin embargo, puede haber ocasiones en las que quieras insistir en que se sigan las reglas. Por ejemplo, no debería ser posible insertar un documento en la colección de personas si no contiene un nombre.

Las reglas de validación pueden especificarse mediante un objeto $jsonSchema que define un array de elementos requeridos y las propiedades de cada campo validado. La colección de personas ya ha sido creada, pero aún puedes definir un esquema que especifique que se requiere una cadena de nombre:

db.runCommand({
  collMod: 'person',
  validator: {
    $jsonSchema: {
      required: [ 'name' ],
      properties: {
        name: {
          bsonType: 'string',
          description: 'name string required'
        }
      }
    }
  }
});

Intenta insertar un documento de persona sin nombre:

db.person.insertOne({ company: 'Alpha Inc' });

…y el comando fallará:

{
  "index" : 0.0,
  "code" : 121.0,
  "errmsg" : "Document failed validation",
  "op" : {
      "_id" : ObjectId("624591771658cd08f8290401"),
      "company" : "Alpha Inc"
  }
}

Los esquemas también pueden definirse si creas una colección antes de utilizarla. El siguiente comando implementa las mismas reglas que el anterior:

db.createCollection('person', {
  validator: {
    $jsonSchema: {
        required: [ 'name' ],
        properties: {
          name: {
          bsonType: 'string',
          description: 'name string required'
          }
      }
    }
  }
});

Este ejemplo más complejo crea una colección de usuarios que valida que se proporcione un nombre, una dirección de correo electrónico y al menos un número de teléfono:

db.createCollection('users', {
  validator: {
    $jsonSchema: {
      required: [ 'name', 'email', 'telephone' ],
      properties: {
        name: {
          bsonType: 'string',
          description: 'name string required'
          },
          email: {
        bsonType: 'string',
          pattern: '^.+@.+$',
          description: 'valid email required'
          },
        telephone: {
          bsonType: 'array',
          minItems: 1,
          description: 'at least one telephone number required'
          }
      }
    }
  }
});

Cómo Actualizar Documentos Existentes en MongoDB

MongoDB ofrece varios métodos de actualización, como updateOne(), updateMany() y replaceOne(). Estos se pasan:

  • Un objeto filtro que localiza los documentos a actualizar
  • Un objeto de actualización — o un array de objetos de actualización — que describa los datos a modificar
  • Un objeto de opciones opcional. La propiedad más útil es upsert, que puede insertar un nuevo documento si no se encuentra ninguno.

El siguiente ejemplo actualiza el documento de persona en el que el nombre se establece como «Henry». Elimina el número de teléfono del trabajo, añade un número de teléfono de casa y establece una nueva fecha de nacimiento:

db.person.updateOne(
  { name: 'Henry' },
  [
    { $unset: [ 'telephone.work' ] },
    { $set: {
      'birthdate': new ISODate('1980-01-01'),
      'telephone': [ { 'home': '789789789' } ]
    } }
  ]
);

El siguiente ejemplo actualiza el documento de persona en el que el nombre es «Ian». Ese nombre no existe actualmente, pero al establecer upsert a «true» se crea:

db.person.updateOne(
  { name: 'Ian' },
  { $set: { company: 'Beta Inc' } },
  { upsert: true }
);

Puedes ejecutar comandos de consulta para examinar las actualizaciones de datos en cualquier momento.

Cómo Eliminar Documentos en MongoDB

El ejemplo de actualización anterior utilizó $unset para eliminar el número de teléfono del trabajo del documento con el nombre «Henry». Para eliminar un documento completo, puedes utilizar uno de los diversos métodos de eliminación, como deleteOne(), deleteMany() y remove() (que pueden eliminar uno o varios).

El documento recién creado para Ian se puede eliminar con un filtro adecuado:

db.person.deleteOne({ name: 'Ian' });

Uso de las Operaciones de Agregación en MongoDB

La agregación es potente, pero puede ser difícil de comprender. Define una serie — o pipeline — de operaciones en un array. Cada etapa de esa pipeline realiza una operación como filtrar, agrupar, calcular o modificar un conjunto de documentos. Una etapa también puede utilizar un comportamiento similar al de SQL JOIN con una operación $lookup. Los documentos resultantes se pasan a la siguiente etapa de la cadena para su posterior procesamiento, según sea necesario.

La mejor forma de ilustrar la agregación es con un ejemplo. Construiremos paso a paso una consulta que devuelva el nombre, la empresa y el número de teléfono del trabajo (si está disponible) de cualquier persona que trabaje para una organización con sede en EE.UU.

La primera operación ejecuta un $match para filtrar las empresas con sede en EEUU:

db.company.aggregate([
  { $match: { base: 'US' } }
]);

Esto devuelve:

{
  "_id" : ObjectId("62442429854636a03f6b853b"),
  "name" : "Alpha Inc",
  "base" : "US"
}
{
  "_id" : ObjectId("62442429854636a03f6b853c"),
  "name" : "Beta Inc",
  "base" : "US"
}

Podemos añadir un nuevo operador pipeline $lookup que haga coincidir el nombre de la empresa (localField) con la empresa (foreignField) en la colección de personas (from). El resultado se añadirá como una matriz de empleados al documento de cada empresa:

db.company.aggregate([
  { $match: { base: 'US' } },
  { $lookup: {
      from: 'person',
      localField: 'name',
      foreignField: 'company',
            as: 'employee'
          }
        }
]);

Y este es el resultado:

{
  "_id" : ObjectId("62442429854636a03f6b853b"),
  "name" : "Alpha Inc",
  "base" : "US",
  "employee" : [
    {
      "_id" : ObjectId("62442429854636a03f6b8534"),
      "name" : "Abdul",
      "company" : "Alpha Inc",
      "telephone" : [
        { "home" : "0123456789" },
        { "work" : "9876543210" }
      ]
    },
    {
      "_id" : ObjectId("62442429854636a03f6b8537"),
      "name" : "Dawn",
      "company" : "Alpha Inc"
    },
    {
      "_id" : ObjectId("62442429854636a03f6b853a"),
      "name" : "Henry",
      "company" : "Alpha Inc",
      "telephone" : [
        { "home" : "789789789" }
      ],
    }
  ]
}
{
  "_id" : ObjectId("62442429854636a03f6b853c"),
  "name" : "Beta Inc",
  "base" : "US",
  "employee" : [
    {
      "_id" : ObjectId("62442429854636a03f6b8535"),
      "name" : "Brian",
      "company" : "Beta Inc"
    },
    {
      "_id" : ObjectId("62442429854636a03f6b8538"),
      "name" : "Esther",
      "company" : "Beta Inc",
      "telephone" : [
       { "home" : "001122334455" }
      ]
    }
  ]
}

Una operación $proyecto (proyección) puede ahora eliminar todo, excepto el array de empleados. A esto le sigue una operación $unwind para destruir el array y obtener los documentos de los empleados por separado:

db.company.aggregate([
  { $match: { base: 'US' } },
  { $lookup: { from: 'person', localField: 'name', foreignField: 'company', as: 'employee' } },
  { $project: { _id: 0, employee: 1 } },
  { $unwind: '$employee' }
]);

El resultado:

{
  "employee" : {
    "_id" : ObjectId("62442429854636a03f6b8534"),
    "name" : "Abdul",
    "company" : "Alpha Inc",
    "telephone" : [
      { "home" : "0123456789" },
      { "work" : "9876543210" }
    ]
  }
}
{
  "employee" : {
    "_id" : ObjectId("62442429854636a03f6b8537"),
    "name" : "Dawn",
    "company" : "Alpha Inc"
  }
}
{
  "employee" : {
    "_id" : ObjectId("62442429854636a03f6b853a"),
    "name" : "Henry",
    "company" : "Alpha Inc",
    "telephone" : [
      { "home" : "789789789" }
    ]
  }
}
{
  "employee" : {
    "_id" : ObjectId("62442429854636a03f6b8535"),
    "name" : "Brian",
    "company" : "Beta Inc"
  }
}
{
  "employee" : {
    "_id" : ObjectId("62442429854636a03f6b8538"),
    "name" : "Esther",
    "company" : "Beta Inc",
    "telephone" : [
      { "home" : "001122334455" }
    ]
  }
}

Por último, se utiliza una operación $replaceRoot para formatear cada documento de modo que sólo se devuelva el nombre de la persona, la empresa y el número de teléfono del trabajo. A esto le sigue una operación $sort para obtener los documentos en orden ascendente de nombre. La consulta agregada completa:

db.company.aggregate([
  { $match: { base: 'US' } },
  { $lookup: { from: 'person', localField: 'name', foreignField: 'company', as: 'employee' } },
  { $project: { _id: 0, employee: 1 } },
  { $unwind: '$employee' },
  { $replaceRoot: {
    newRoot: {
      $mergeObjects: [ {
        name: "$employee.name",
        company: '$employee.company',
        work: { $first: '$employee.telephone.work' }
      }, "$name" ]
   } } },
  { $sort: { name: 1 } }
]);

El resultado:

{
  "name" : "Abdul",
  "company" : "Alpha Inc",
  "work" : "9876543210"
}
{
  "name" : "Brian",
  "company" : "Beta Inc",
}
{
  "name" : "Dawn",
  "company" : "Alpha Inc",
}
{
  "name" : "Esther",
  "company" : "Beta Inc"
}
{
  "name" : "Henry",
  "company" : "Alpha Inc"
}

Hay otras formas de conseguir este resultado, pero el punto clave es que MongoDB puede hacer la mayor parte del trabajo. Rara vez es necesario leer los documentos y manipular los datos en el código de tu aplicación directamente.

Cómo Ejecutar Operaciones Masivas en MongoDB

Por defecto, MongoDB puede manejar 1.000 operaciones concurrentes. Es poco probable que esto suponga un problema mientras se utiliza MongoDB, pero las aplicaciones pueden alcanzar este límite si realizan una serie de manipulaciones de datos en registros individuales. Las aplicaciones Node.js son especialmente problemáticas porque pueden emitir rápidamente una serie de peticiones asíncronas sin tener que esperar a que se completen.

Para eludir este problema, MongoDB proporciona una API de operaciones masivas que acepta cualquier número de actualizaciones que pueden ejecutarse en orden o en cualquier orden.

Aquí tienes un ejemplo de pseudocódigo en Node.js:

// reference the mycollection collection
const bulk = db.collection('mycollection').initializeUnorderedBulkOp();

// make any number of data changes
bulk.insertOne(...);
bulk.insertMany(...)
bulk.updateOne(...);
bulk.deleteOne(...);
// etc...

bulk.execute();

La última sentencia emite efectivamente una única petición a MongoDB, por lo que tienes menos posibilidades de llegar a ese límite de 1.000 operaciones.

Resumen

MongoDB proporciona un almacén flexible para aplicaciones como los sistemas de gestión de contenidos, las libretas de direcciones y las redes sociales, donde las estructuras de datos estrictas son demasiado rígidas y difíciles de definir. Las escrituras de datos son rápidas y la fragmentación en varios servidores es más fácil.

Escribir aplicaciones utilizando una base de datos MongoDB también puede ser liberador. Es posible almacenar cualquier dato en cualquier documento de cualquier colección en cualquier momento. Esto es especialmente práctico cuando estás desarrollando un prototipo o un Producto Mínimo Viable utilizando metodologías ágiles en las que los requisitos evolucionan con el tiempo.

Dicho esto, las consultas complejas pueden ser un reto y los conceptos de desnormalización son difíciles de aceptar cuando se está migrando desde el mundo SQL.

MongoDB es menos adecuado para las aplicaciones que tienen requisitos transaccionales estrictos en los que la integridad de los datos es esencial, como ocurre con los sistemas bancarios, de contabilidad y de control de existencias. Estos tienen campos de datos identificables que deben diseñarse antes de comenzar la codificación.

Hay muchos tipos de aplicaciones entre estos dos extremos, por lo que elegir una base de datos adecuada se hace más difícil. Afortunadamente, las bases de datos NoSQL, como MongoDB, han empezado a adoptar opciones similares a las de SQL, como los JOIN y las transacciones.

A la inversa, las bases de datos SQL, como MySQL y PostgreSQL, ofrecen ahora campos de datos JSON similares a los de NoSQL. También pueden merecer tu atención, pero como siempre, la elección final es tuya.