Motor de base de datos embebida, rapida y robusta para Node.js
npm install megadbxmegadbx es un motor de base de datos embebida para Node.js, diseñado para ser rápido y confiable. Combina la flexibilidad de un document store con características avanzadas como integridad de datos, concurrencia segura, compresión, control de versiones, índices optimizados y alta robustez, todo en un paquete ligero y eficiente.
MegaDBAsync se renombró a MegaDBSafe.await o .then().await o .then().MegaDBAsync por MegaDBSafe.---
- Persistencia optimizada en archivos JSON comprimidos
- Escritura atómica en disco con compresión opcional.
- Mirror y Journal para recuperación segura.
- Integridad de datos avanzada
- Hash SHA-256 por bloque.
- CRC32 por páginas de datos.
- xxHash para detección rápida de corrupción.
- Compresión selectiva de campos (gzip o lz4).
- Búsquedas rápidas
- Bloom Filters por bloque para aceleración de consultas.
- LRU Cache para acceso inmediato a datos recientes.
- Automatización
- flush() periódico configurable.
- createBackup() automático con intervalos programados.
- Snapshots & Backups
- Crear y restaurar snapshots completos.
- Backups comprimidos con rotación flexible (por contador o timestamp).
- Modo asíncrono y multiproceso (MegaDB, MegaDBSafe, MegaDBFull)
- WAL (Write-Ahead Log) para consistencia y recuperación.
- Concurrencia segura con cola FIFO interna.
- Operaciones totalmente no bloqueantes, ideal para servidores y microservicios.
- Consultas con filtros avanzados ($gt, $lt, $in, $regex, etc.).
- Extensiones avanzadas (MegaDBFull)
- Índices: secundarios, compuestos, trie (prefijos) y skiplist (rangos).
- MVCC: múltiples versiones de registros sin bloqueos pesados.
- Append-only log (AOF) para almacenamiento auditable.
- Merkle Tree para verificación criptográfica de integridad.
- Transacciones entre múltiples bases de datos.
- Interfaz visual interactiva para explorar y gestionar bases.
- ...y mucho más.
---
megadbx implementa un sistema de persistencia diseñado para garantizar integridad de datos, resistencia a fallos y consistencia en disco, incluso en casos de caída del proceso o del sistema operativo.
El ciclo de escritura y persistencia combina varias capas de protección:
Esto asegura que nunca exista un archivo a medio escribir, o queda el archivo anterior, o el nuevo completo.
.journal) que contiene la nueva versión.Este patrón es similar al Write-Ahead Logging (WAL).
crcPages: sumas CRC32 por páginas, para verificar integridad parcial.xxhash: un hash fuerte del bloque completo.keyCrc: checksums de subdocumentos individuales.Con esto se puede detectar y reparar corrupción de datos a varios niveles.
Una de las optimizaciones más importantes de megadbx es que no vuelve a leer ni parsear los archivos JSON en cada operación.
En su lugar, utiliza varias capas de caché e índices en memoria que reducen drásticamente el acceso a disco.
block_XXXX.json). blocksCache. get, has, etc.), la respuesta se sirva inmediatamente sin necesidad de recorrer el bloque asociado. indexes.json. indexes.json falta o está dañado, MegaDBX puede reconstruir el índice completo a partir de los datos de los bloques.---
En conjunto, estas técnicas permiten:
- Atomicidad: nunca quedan datos parciales.
- Durabilidad: los cambios confirmados sobreviven a fallos del proceso o del sistema operativo.
- Recuperación rápida: con journal y mirror, la base de datos se repara sola si encuentra corrupción.
- Seguridad extra: snapshots y backups permiten volver atrás en caso de error humano.
- Evite lecturas repetitivas de JSON en disco.
- Mantenga la mayoría de las operaciones de consulta en memoria.
- Ofrezca tiempos de respuesta consistentes incluso en bases con muchos datos.
---
Y así como estas, se incluye otras optimizaciones internas con el objetivo de ofrecer un equilibrio entre velocidad, durabilidad y facilidad de uso.
(lz4 es opcional, solo si usas compressAlg: "lz4")
``bash
npm install megadbx
npm install xxhashjs
Opcional (si usas compress 'lz4'):
npm install lz4
Opcional (Si usas la clase MegaDBVisual, no te pide esto si no llamas a la clase)
npm install express
`
---
: Toda la base de datos (db) => {key1: value1, key2: value2, etc} => {documento1, documento2, etc}
- documento(s): Lo que se guardó en la base de datos => {key: value}
- clave/key: La clave con la que se guardo el documento => key
- value/valor: El valor guardado en el documento => value
- query: objeto de filtro profesional usando operadores relacionales => { edad: { $gt: 18 } } etc.
- path: Ruta dentro del documento ("usuario1") o coleccion (si usa wildcard)
- wildcard: Simbolo especial para acceder a toda la coleccion o sub documentos de un documento. "*"
- dot notation: Simbolo especial para acceder anidadamente a una ruta dentro de un documento '.'Operadores relacionales y textuales
* Existen operadores que nos ayudan a la hora de filtrar o buscar documentos, estos operadores siguen una regla estricta:
-
$gt: Mayor que: valor > $gt
- $gte: Mayor o igual que: valor >= $gte
- $lt: Menor que: valor < $lt
- $lte: Menor o igual que: valor <= $lte
- $eq: Igualdad estricta: valor === $eq
- $ne: distinto: valor !== $ne
- $in: Pertenece a la lista - $in debe ser un array: $in.ncludes(valor)
- $nin: No pertenece a la lista - $nin debe ser un array: $in.includes(valor) es false
- $between: Rango inclusivo [min, max] - Esta dentro del rango min y max: valor >= min && valor <= max
- $prefix: Coincidencia de prefijo de string: (String).startsWith
- $regex: Expresion regular (string), opcional $flags (los flags son del RegExp): new RegExp($regex, $flags||'').test(String(valor))
Clases principales
(MegaDB) Constructor y metodos:
* MegaDB
* set
* has
* get
* delete
* all
* keys
* values
* count
* find
* aggregate
* update
* watch
* stats
* flush
* close
* createBackup
* restoreBackup
* createSnapshot
* restoreSnapshot
---
(MegaDBSafe) Constructor y metodos:
* MegaDBSafe
* ready
* set
* has
* get
* delete
* all
* keys
* values
* count
* find
* aggregate
* update
* watch
* stats
* flush
* close
* createBackup
* restoreBackup
* createSnapshot
* restoreSnapshot
---
(MegaDBFull) Constructor y metodos:
* MegaDBFull
* ready
* set
* has
* get
* delete
* all
* keys
* values
* count
* find
* aggregate
* update
* watch
* history
* stats
* flush
* close
* createBackup
* restoreBackup
* createSnapshot
* restoreSnapshot
* verifyIntegrityFast
* rebuildCompositeIndexes
---
(Transaction) Constructor y metodos:
* Transaction
* get
* set
* has
* delete
* commit
* rollback
---
(MegaDBVisual) Constructor y metodos:
* MegaDBVisual
* createPage
---Dato importante, leer esto despues de entender como funciona MegaDB/MegaDBSafe/MegaDBFull
1.
MegaDBMegaDB
`js
const { MegaDB } = require("megadbx");
const db = new MegaDB(collectionName, options = {});
`- collectionName (string): nombre de la colección (carpeta en ./db/).
- options (objeto, opcional):
| Opción | Default | Explicación |
| ---------------------- | --------------------------------- | --------------------------------------------------------- |
|
dir | require.main.filename | Directorio donde se creara la carpeta del db/ |
| mirrorDirName | mirror | Carpeta para guardar los 'espejos' del db |
| blockSize | 100 | Cantidad de claves por bloque. |
| flushInterval | 30000 ms | Cada cuánto guardar datos a disco. |
| crcPageSize | 4096 bytes | Tamaño de página para CRC. |
| schema | null | Validación de tipos. |
| secondaryIndexes | [] | Campos indexados. |
| rebuildIndexOnLoad | false | Reconstruye el los campos indexados al inicializar el db. |
| compressFields | [] | Campos a comprimir. |
| bloomK | 4 | Hashes en BloomFilter. |
| expectedKeysPerBlock | 1000 | Estimación de cuantas claves tendra cada bloque. |
| bloomM | expectedKeysPerBlock * 10 | Tamaño (en bits) del Bloom filter por bloque. |
| lruCap | 128 | Tamaño de caché LRU. |
| integrityOnRead | true | Verificar integridad en lectura. |
| integrityCooldownMs | 180000 ms | Tiempo entre verificaciones de integridad. |
| backupMode | counter | Nombres de backup (counter o timestamp). |
| backupInterval | 3600000 ms | Intervalo entre backups. |
| maxBackups | 10 | Backups a conservar. |
| compressAlg | gzip | Algoritmo de compresión (gzip o lz4). |
| useGlobalIndex | false | Mantiene y persiste un indice global simple.. |
| indexFlushInterval | 1800000 ms | Cada cuánto se guarda cuando useGlobalIndex está en true |
| keyCacheCap | 1024 | Tamaño de una cache de claves-valor para acelerar get() |
(Explicacion detallada)
dir: Directorio donde se creara la base de datos, la carpeta sera [dir]/db/[collectionName], el directorio default es require.main.filename (la ruta donde esta la carpeta de tu proyecto/app), exp:
`js
const { MegaDB } = require("megadbx");
/*
//Estructura actual de la carpeta global:
(Estoy usando el archivo db.js) [📂] proyecto
[📂] base de datos
[📄] handler.js
[📄] db.js
[📂] comandos
[📄] comando1.js
*/
//Crear la db a la altura de la carpeta base de datos/comandos.
let db = new MegaDB('usuarios'); //No se le pasa nada porque ya lo tiene por default.
/*
//Estructura actual de la carpeta global:
[📂] proyecto
[📂] base de datos
[📄] handler.js
[📄] db.js
[📂] comandos
[📄] comando1.js
[📂] db
[📂] usuarios
*/
// o tambien usando dir:
let db = new MegaDB('usuarios', {
dir: path.join(__dirname + "..") // usando path, tambien puede ser los puntos ..
});
/*
//Estructura actual de la carpeta global:
[📂] proyecto
[📂] base de datos
[📄] handler.js
[📄] db.js
[📂] comandos
[📄] comando1.js
[📂] db
[📂] usuarios
*/
`mirrorDirName: Carpeta donde se crearan los respaldos, sirven como espejo para recuperar archivos por si algo falla al escribir (default: 'mirror')blockSize: Tamaño de bloque en cantidad de claves (default: 100).
- Divide los datos en varios bloques para no tener un JSON gigante.
- megadbx no guarda toda la colección en un solo archivo (como haría un usuarios.json enorme).
- En lugar de eso, divide la colección en varios bloques (archivos pequeños).
- blockSize define el número aproximado de claves que se guardan en cada bloque.
- Por defecto es 100, eso significa que cada archivo (block_0.json, block_1.json, etc…) contendrá alrededor de 100 claves.
`js
//Supongamos que guardas 500 usuarios en la colección "usuarios":
for (let i = 1; i <= 500; i++) {
await db.set(u${i}, { nombre: Persona${i} });
}
`
`
Caso 1 — blockSize: 100 (default)
db/usuarios/
├── block_0.json ← contiene ~100 users
├── block_1.json ← contiene ~100 users
├── block_2.json ← contiene ~100 users
├── block_3.json ← contiene ~100 users
├── block_4.json ← contiene ~100 users
`
- 500 usuarios = 5 bloques.
- Bloques más pequeños → más archivos, más fragmentación.
- Ideal si quieres minimizar pérdidas (solo se daña un bloque chiquito).Valores pequeños (ej. 50):
- ✅ Más seguro (si se daña un bloque, pierdes menos).
- ❌ Más archivos que manejar, un poco más lento para recorrer todo.
Valores grandes (ej. 500 o 1000):
- ✅ Menos archivos, búsquedas más rápidas en algunos casos.
- ❌ Si se corrompe un bloque, pierdes más datos.
Valor por defecto (100):
- Equilibrio entre seguridad y rendimiento.
No se asusten, hay metodos avanzados de seguridad/respaldo, tanto internas automaticas y otras a su criterio para reducir esto a nulo ^^
flushInterval: Cada cuánto (ms) se guardan los cambios automáticamente en disco (se escriben a disco los cambios pendientes) en segundo plano.
- Más corto = menos pérdida posible si se cae el proceso, pero más I/O
- Default: 30000 ms => 30s.
- Los valores se reflejan en disco al hacer flush, en memoria se actualiza en tiempo real.crcPageSize: Tamaño de “página” para calcular CRC por fragmentos del archivo, ayuda a detectar corrupción y reparar desde el espejo (mirror) (default: 4096=4KB).schema: Esquema de validación por campo al hacer set/set, es un mapa simple de campo -> 'string'|'number'|'boolean'|'object'
`js
schema: { edad: "number", nombre: "string" }
`
secondaryIndexes: Lista de campos para indexar y acelerar busquedas por igualdad en find/find, usando filtros $eq/$in/$ne/$nin/$gt/$lt/$gte/$lte/$between/$prefix/$regex sobre el valor.- Guarda en un diccionario inverso las rutas de las claves asociadas a un valor.
- Esto evita tener que recorrer todos los datos ya que directamente accede a la ruta.
- Soporta rutas anidadas usando dot notation que es el punto '.'
- Muy util cuando tiene miles de datos. Ej:
`js
//Este ejemplo fue testeado con 50,000 usuarios añadidos al db//usando propiedades planas al igual que dot notation
secondaryIndexes: ["nombre", "perfil.id", "perfil.edad"]
await db.set("u1", { nombre: "mega", email: "mega@gmail.com" });
await db.set("u2", { nombre: "ratsa", email: "ratsa@gmail.com" });
await db.set("u3", { perfil: { id: "Ana", edad: 25 } });
await db.set("u4", { perfil: { id: "Luis", edad: 30 } });
await db.set("u5", { perfil: { id: "Ana", edad: 40 } });
//
//
//En memoria al usar MegaDB:
{
"email": {
"mega@gmail.com": ["u1"],
"ratsa@gmail.com": ["u2"]
},
"perfil.id": {
"Ana": ["u3","u5"],
"Luis": ["u4"]
},
"perfil.edad": {
"25": ["u3"],
"30": ["u4"],
"40": ["u5"]
}
}
//En disco al usar MegaDBSafe/MegaDBFull => Se crea en el archivo indexes.json
{
"email": {
"mega@gmail.com": ["u1"],
"ratsa@gmail.com": ["u2"]
},
"perfil.id": {
"Ana": ["u3","u5"],
"Luis": ["u4"]
},
"perfil.edad": {
"25": ["u3"],
"30": ["u4"],
"40": ["u5"]
}
}
//usamos find o find sin usar el secondaryIndexes:
//
console.time("find normal");
let data = await db.find("*", {nombre: { $eq: "mega"}});
console.timeEnd("find normal");
console.log(data);
console.time("find normal2");
let data2 = await db.find("*", { "perfil.id": { $eq: "Ana" } });
console.timeEnd("find normal2");
console.log(data2);
/*
Imprime:
find normal: 941.061ms => casi 1 segundo
[{ nombre: "mega", email: "mega@gmail.com" }]
find normal2: 981.025ms => casi 1 segundo
[
{ perfil: { id: 'Ana', edad: 25 } },
{ perfil: { id: 'Ana', edad: 40 } }
]
*/
//usamos find o find y el secondaryIndexes:
console.time("find secondaryIndexes");
let data = await db.find("*", {nombre: { $eq: "mega"}});
console.timeEnd("find secondaryIndexes");
console.log(data);
console.time("find secondaryIndexes2");
let data2 = await db.find("*", { "perfil.id": { $eq: "Ana" } });
console.timeEnd("find secondaryIndexes2");
console.log(data2);
/*
Imprime:
find secondaryIndexes: 201.005ms => menos de medio segundo
{ nombre: "mega", email: "mega@gmail.com" }
find secondaryIndexes2: 196.032ms => menos de medio segundo
[
{ perfil: { id: 'Ana', edad: 25 } },
{ perfil: { id: 'Ana', edad: 40 } }
]
*/
`
DATO IMPORTANTE:En MegaDB:
- secondaryIndexes al guardar en memoria, solo guarda los datos que se hicieron posterior a la inicializacion del db, esto quiere decir que si tenias datos agregados anteriormente y volviste a inicializar el db solo guardará en memoria los nuevos datos añadidos hasta que el db se apague y asi sucesivamente entre cada inicio y apagado (desventaja por tenerlo en memoria).
- Existe la opcion de revertir esto, asi al iniciar el db siempre se cargan todos los datos y se reconstruye el secondaryIndexes en memoria en base a tu db actual (ver
rebuildIndexOnLoad).En MegaDBSafe o MegaDBFull
- secondaryIndexes ahora persiste en disco (se guarda en indexes.json) asi al iniciar el db solo se toman los datos actuales del archivo (evitamos recargar todos los datos) para mejorar la flexibilidad y rapidez al hacer la solicitud, de igual forma se va actualizando de acuerdo a los nuevos datos añadidos.
- Que sucederia si tenemos 2 usuarios que cumplen el requisito del
secondaryIndexes pero el usuario1 fue añadido con el secondaryIndexes y luego el usuario2 a la db sin usar el secondaryIndexes? Al hacer la solicitud de busqueda con find/find los datos del usuario1 seran retornados en cuestion de milisegundos al estar dentro del indexes.json, en cambio el usuario2 al estar solo en la db sus datos seran retornados en cuestion de segundos (tomando de ejemplo los 50,000 usuarios añadidos).
- Para sincronizar estos datos tomando el ejemplo de arriba, se puede usar la opcion rebuildIndexOnLoad, esto hará que se actualice el indexes.json con los datos del db que cumplen el requisito del secondaryIndexes, pueden usarlo 1 vez para sincronizar y luego deshabilitarlo.rebuildIndexOnLoad: Reconstruye los campos indexados del secondaryIndexes tomando los datos ya existentes de los bloques (la db), sirve para sincronizar los datos existentes y no solo tomar los nuevos datos añadidos (default: false).compressFields: Lista de rutas de campos (array de paths, soporta dot notation '.' y wildcards '*') a comprimir con gzip o lz4 al persistir en disco, reduce tamaño en disco al guardarse comprimidos (por campo). Al leer se descomprime solo, util para textos grandes, Ej:
`js
compressFields: [
"user1", // campo top-level, comprime todo el subarbol (objeto)
"user2.descripcion", // anidado dot notation .
".descripcion", // usa wildcard , aplica a todas las claves raiz que tengan el subcampo descripcion (user1.descripcion, user2.descripcion, etc)
"datos.*.prof"// dot notation/anidado + wildcard, aplica a todas las claves de datos que tengan el subcampo prof (datos.a.prof, datos.b.prof, etc)
]
await db.set('user1', { titulo: 'Ejemplo', descripcion: 'Texto muuuuuuuy largo.....' }); //aplica "user1" y "*.descripcion"
await db.set('user2', { titulo: 'Ejemplo', descripcion: 'Texto muuuuuuuy largo.....' }); //aplica "user2.descripcion" y "*.descripcion" await db.set('datos', {
mario: {prof: "programador", edad: 20},
pedro: {prof: "agronomo", edad: 25},
juan: false,
}); //aplica "datos.*.prof" a mario y pedro
`
- Puedes combinar wildcards y dot notation incluso mas de 1 vez, soporta ya sean separadas o seguidas de otra, data...obj1, ..obj2, etc, a tu criterio.bloomK: Número de hashes del Bloom filter por bloque (filtro probabilístico para saber si un bloque puede contener una clave, busquedas mas rapidas). Más alto = menos falsos positivos, más CPU.. Normalmente no se tocan (default: 4).expectedKeysPerBlock: Estimación de cuántas claves tendrá cada bloque, e usa para dimensionar el Bloom filter si no fijas bloomM. Normalmente no se tocan (default: 1000).bloomM: Tamaño (en bits) del Bloom filter por bloque, si no lo pasas se calcula por defecto como expectedKeysPerBlock * 10 (≈1.25 KB por 1000 claves).- Tip rápido: si quieres bajar falsos positivos, sube
bloomM o bloomKintegrityOnRead: Activa verificaciones de integridad (CRC por páginas + hash) de forma periódica/al leer los bloques (db). Si detecta corrupción intenta auto-recuperar desde el mirror => espejo, (default: true).integrityCooldownMs: Tiempo mínimo entre verificaciones de integridad, integrityOnRead y integrityCooldownMs equilibran seguridad vs rendimiento. (default: 180000 ms => 3 minutos para no “castigar” el disco).backupMode: "counter" o "timestamp". Define cómo nombrar los backups.backupInterval: Cada cuánto (ms) se crea un backup automático comprimido de todos los bloques(db), esto se rota de acuerdo a maxBackups (default: 1 hora)maxBackups: Cuántos backups guardar antes de borrar los viejos (default: 10).compressAlg: Define qué algoritmo de compresión se usará para los bloques o campos que indiques en compressFields, al igual que en los snapshots / backups (default: gzip)| Característica | Gzip | LZ4 |
| ------------------------------ | ------------------------------------------------- | ---------------------------------------------------------- |
| Compresión | Alta (archivos más pequeños) | Media (archivos más grandes que gzip) |
| Velocidad de compresión | Lenta (más CPU) | Muy rápida (optimizada para tiempo real) |
| Velocidad de descompresión | Buena, pero más lenta que LZ4 | Extremadamente rápida |
| CPU requerida | Mayor | Mucho menor |
| Soporte nativo en Node.js | Sí (
zlib) | No (se instala lz4) |
| instalacion | No requiere nada | Al instalar requiere python / compilador C/C++ |useGlobalIndex: Mantiene y persiste un índice global simple en index.json (mapea claves raíz → bloque), para acelerar la carga/consultas tras reiniciar, opcional para dbs con miles de datos (default: false).indexFlushInterval: Cada cuánto se persiste index.json cuando useGlobalIndex está en true, si lo pones en 0 no hay intervalo periódico (default: 1800000 ms => 30 minutos)lruCap: Capacidad de la caché LRU de bloques en memoria (default: 128), más grande = menos lecturas de disco, más RAM.keyCacheCap: Define el tamaño máximo de la caché LRU (Least Recently Used) para claves individuales, controla cuántas claves recientes se guardan en memoria para acelerar lecturas repetidas (default: 1024)
Esto significa que cuando llamas a get(path), la base de datos guarda en memoria los últimos resultados para que futuras lecturas sean instantáneas sin tener que:
- Leer el bloque desde disco.
- Descomprimir el campo (si estaba en compressFields).
- Pasar por validaciones de integridad.Si accedes mucho a las mismas claves, súbelo.
`js
const db = new MegaDB("users", {
keyCacheCap: 2 // solo guardará 2 claves en caché
});await db.set("user.1", { name: "mega", age: 13 });
await db.set("user.2", { name: "ratsa", age: 14 });
await db.set("user.3", { name: "Charlie", age: 40 });
// Primera lectura (va a disco y guarda en caché)
console.log(await db.get("user.1"));
// Segunda lectura (lee desde caché → mucho más rápida)
console.log(await db.get("user.1"));
// Al llegar a 3 claves, la más antigua se expulsa (user.1 o user.2)
console.log(await db.get("user.3"));
`
Pequeñas base de datos
- 1024 claves cacheadas es más que suficiente, el consumo de RAM es bajísimo (kilobytes) y el hit-rate será decente.Bases medianas (decenas o cientos de miles de claves)
- 1024 puede quedarse corto → si accedes siempre a distintas claves, la caché se reciclará muy rapido y tendras pocos hits.
- Subirlo a 8192 (8k) o 16384 (16k) da mejor rendimiento con un costo de memoria aún bajo.
Bases muy grandes (millones de claves)
Lo crítico es medir:
- si los accesos son locality-friendly (siempre consultas un subconjunto chico de claves), 1024 sigue bastando.
- si son muy aleatorios, incluso 65536 puede ser razonable.
- cada entrada en la caché es básicamente {key, value} más overhead → unos cientos de bytes por clave en node, sigues ganando velocidad en lecturas repetidas.
set
$3
Crea o actualiza un valor en la ubicación indicada por path.-
path (string): ruta jerárquica donde almacenar el valor, soporta dot notation '.'
Ejemplo: "users.1.name" → dentro de users, clave 1, propiedad name.
- value (any): el valor que quieres guardar (puede ser string, number, boolean, objeto, etc.).
- retorna (promesa): true si se guardo correctamente.Si tienes un
schema definido en las opciones del constructor, set validará el tipo de valor antes de guardarlo.`js
await db.set("users.1", { name: "mega", age: 13 });
await db.set("users.2", { name: "ratsa", age: 14 });
await db.set("users.1.email", "mega@gmail.com");
/*
{
"users": {
"1": {
"name": "mega",
"age": 13,
"email": "mega@gmail.com"
},
"2": {
"name": "ratsa",
"age": 14
}
}
}
*/await db.set("counter", { id: "1239858345", count: 48 });
/*
{
"users": {......},
"counter": {
"id": "1239858345",
"count": 48
}
}
*/
`
---has
$3
Verificas si existe una clave en la coleccion de la ruta
path.*
path (string): ruta a la clave que quieres verificar, soporta dot notation '.'
* Retorna (promesa): true si existe o false si no existe.`js
await db.set("users.1", { name: "mega", age: 13 });
await db.set("users.2", { name: "ratsa", age: 14 }); console.log(await db.has("users")); // true
console.log(await db.has("users.1")); // true
console.log(await db.has("users.1.name")); // true
console.log(await db.has("users.2.age")); // true
console.log(await db.has("users.1.status")); //false
console.log(await db.has("users.1.name.data")); //false
console.log(await db.has("docs")); //false
`
---get
$3
Obtiene un documento almacenado por su clave.
*
path (string): ruta al documento que quieres obtener, soporta dot notation '.'
* Retorna (promesa): el valor almacenado o undefined si no existe.`js
await db.set("users.1", { name: "mega", age: 13 });
await db.set("users.2", { name: "ratsa", age: 14 });
await db.set("docs": []); const user1 = await db.get("users.1");
console.log(user1); // { name: "mega", age: 13 }
const age = await db.get("users.2.age");
console.log(age); // 14
const nodata = await db.get("users.3");
console.log(nodata); // undefined
const document = await db.get("docs");
console.log(document); // []
`
---delete
$3
Elimina un documento almacenado por su clave.
*
path (string): ruta del documento a eliminar, soporta dot notation '.'
* Retorna (promesa): true si se eliminó, false si no existía.`js
await db.set("users.1", { name: "mega", age: 13 });
await db.set("users.2", { name: "ratsa", age: 14 });
await db.delete("users.2.age"); // elimina el campo "age" del usuario 2
await db.delete("users.1"); // elimina todo el objeto del usuario 1console.log(await db.get("users.1")); // undefined
console.log(await db.get("users")); //{"2": {"name": "ratsa"}}
`
---all
$3
Obtiene todos los documentos de la coleccion.
* Retorna (promesa): un objeto con todos los documentos actuales.
`jsawait db.set("users.1", { name: "mega", age: 13 });
await db.set("users.2", { name: "ratsa", age: 14 });
console.log(await db.all());
/* retorna:
{
"users":
"1": {
"name": "mega",
"age": 13
},
"2": {
"name": "ratsa",
"age": 14
}
}
*/
`keys
$3
Devuelve todas las claves de la coleccion.
path (string): ruta de las claves a obteber, soporta dot notation '.' y wildcard '' cuando quieres obtener las claves principales de la coleccion.
* Retorna (promesa): un array con las claves encontradas.`js
await db.set("users.1", { name: "mega", age: 13 });
await db.set("users.2", { name: "ratsa", age: 14 });
await db.set("docs": []); console.log(await db.keys("users")); // ["1", "2"]
console.log(await db.keys("users.1")); // ["name", "age"]
console.log(await db.keys("*")); // wildcard => ["users", "docs"]
console.log(await db.keys("notfound")); // []
console.log(await db.keys("users.2.data")); // []
`
---values
$3
Devuelve todos los valores de la coleccion.
path (string): ruta de los valores a obteber, soporta dot notation '.' y wildcard '' cuando quieres obtener los valores principales de la db.
* Retorna (promesa): un array con los valores encontradas.`js
await db.set("users.1", { name: "mega", age: 13 });
await db.set("users.2", { name: "ratsa", age: 14 });
await db.set("docs": []);
await db.set("status": false);console.log(await db.values("users"));
// [{name: "mega", "age": 13}, {name: "ratsa", "age", 14}]
console.log(await db.values("users.1")); //["mega", 13]
console.log(await db.values("users.1.name")); // []
console.log(await db.values("users.3")); // []
console.log(await db.values("*"));
// wildcard => [{"1": {...}, "2": {...}}, [], false]
`
---count
$3
Devuelve el numero total de documentos.
path (string): ruta donde se hara el conteo de datos, se usa wildcard '' para escanear toda la coleccion, tambien soporta dot notation '.'
* query (object) Objeto de filtro, cada clave del objeto es un campo o una ruta de campo.
* Retorna (promesa): el conteo de elementos que paso el filtro de query.Soporta 2 tipos de filtros:
Un filtro simple => comparacion de igualdad estricta (===)
`js
{ edad: 30 } // equivale a edad === 30
`
*Un filtro avanzado => comparacion con operadores, ver aqui
* Puedes usar incluso mas de un filtro colocando la ,
`js
{ edad: { $gt: 30 }} // equivale a edad > 30
`
`js
await db.set("users.1", { name: "mega", edad: 13 });
await db.set("users.2", { name: "megast", edad: 14 });
await db.set("users.3", { name: "pedro", edad: 15 });
await db.set("users.4", { name: "ratsa", edad: 16 });console.log(await db.count("users", {edad: {$gt: 14}})); // retorna 2 porque solo hay 2 mayores a 14
console.log(await db.count("users", {edad: {$lt: 16}})); // retorna 3 porque solo hay 3 menores a 16
console.log(await db.count("users", {edad: {$gt: 14}, name: {$prefix: 'r'} }));
// retorna 1 porque se filtro primero por edad mayor a 14 => 2 y tambien se filtro con $prefix dejando de esos 2 resultados solo 1 => name: "ratsa"
console.log(await db.count("", {etc...})); //puedes usar wildcard '' para la coleccion entera.
console.log(await db.count("data.dato2", {etc...})); //puedes usar dot notation
`
---find
$3
Busca y devuelve un array con los documentos (o sub-documentos) que cumplen el filtro query dentro de la ruta indicada por path. Está pensado para búsquedas expresivas (filtros por documentos incluyendo sub documentos anidados) y operadores relacionales y textuales, aprovechando índices secundarios (
secondaryIndexes) cuando están configurados.
path (string, opcional): Ruta del documento donde se aplicará la búsqueda, se usa wildcard '' para escanear toda la coleccion, tambien soporta dot notation '.'
* query (objeto, opcional): Objeto de filtro, cada clave del objeto es un campo o una ruta de campo:
* retorna (promesa): Un array con los documentos que cumplieron el filtro.Soporta 2 tipos de consulta:
`js
const modo_1 = await db.find(path, query);
const modo_2 = await db.find(query);
`
modo_1
- Especificas el path donde se aplicara el filtro, puedes usar wildcard '*' para indicar que es toda la coleccion.
- path puede recibir dot notation '.' para acceder a un documento o sub documento especifico.
- query es el objeto con el filtro a usar, puedes usar dot notation para buscar incluso una ruta mas exacta.modo_2
- No se especifica path, solo el query, find asume path = '*' (toda la coleccion) y query = objeto/filtro.Un filtro simple => comparacion de igualdad estricta (===)
`js
{ edad: 30 } // equivale a edad === 30
`
Un filtro avanzado => comparacion con operadores, ver aqui
`js
{ edad: { $gt: 30 }} // equivale a edad > 30
`* Ejemplos (con explicaciones)
`js
await db.set('usuario1', {nombre: "u1", edad: 20});
await db.set('usuario2', {nombre: "u2", edad: 19});
await db.set('usuario3', {nombre: "u3", edad: 18});console.log( await db.find({nombre: "u1"}) ); //simple, devuelve [{nombre: "u1", "edad": 20}]
console.log( await db.find({edad: {$eq: 20}}) ); //devuelve [{nombre: "u1", edad: 20}]
console.log( await db.find({edad: {$gt: 19}}) ); //devuelve [{nombre: "u1", edad: 20}]
console.log( await db.find({edad: {$lt: 19}}) ); //devuelve [{nombre: "u3", edad: 18}]
console.log( await db.find({nombre: {$in: ["u1", "u3"]}}) );
/* devuelve 2, porque el valor de la propiedad "nombre" esta dentro del array ["u1", "u3"]
[
{nombre: "u1", edad: 20},
{nombre: "u3", edad: 18}
]
*/
console.log( await db.find({nombre: {$nin: ["u1", "u3"]}}) );
/* devuelve 1, porque el valor de la propiedad "nombre" no esta dentro del array ["u1", "u3"]
[
{nombre: "u2", edad: 19}
]
*/
console.log( await db.find({nombre: {$prefix: "u"}}) );
/* devuelve 3, porque el valor de la propiedad "nombre" comienza con "u"
[
{nombre: "u1", edad: 20},
{nombre: "u2", edad: 19},
{nombre: "u3", edad: 18}
]
*/
console.log( await db.find({nombre: {$regex: '^u', $flags: 'i'}}) );
/* devuelve 3 porque el valor de la propiedad "nombre" comienza con 'u' o 'U'
[
{nombre: "u1", edad: 20},
{nombre: "u2", edad: 19},
{nombre: "u3", edad: 18}
]
*/
`
* Puedes usarlo tambien usando dot notation:
`js
await db.set("usuarios.pablo",{
id: "u1",
nombre: "pablo pablon",
edad: 42,
rol: "admin",
direccion: { ciudad: "xxxx", distrito: "aaaaaa" },
bio: "texto largo..."
});
await db.set("usuarios.pedro",{
id: "u2",
nombre: "pedro pedron",
edad: 17,
rol: "moderador",
direccion: { ciudad: "yyyyy", distrito: "eeeeee" },
bio: "texto largo..."
});
await db.set("usuarios.juan",{
id: "u3",
nombre: "juan juanon",
edad: 25,
rol: "trusted",
direccion: { ciudad: "zzzzz", distrito: "iiiiii" },
bio: "texto largo..."
});
// esto me crea { usuarios: {pablo: {...}, pedro: {...}, {juan: {...}}} }
console.log( await db.find('usuarios', { 'direccion.ciudad': 'yyyyy' }) ); //devuelve el documento de pedro
console.log( await db.find('usuarios', { nombre: { $prefix: 'ju' } }) ); //devuelve el documento de juan
console.log( await db.find('usuarios', { edad: { $between: [20, 40] } }) ); //devuelve el documento de juan porque su edad es mayor o igual a 20 y menor o igual a 40
console.log( await db.find('usuarios', { rol: { $in: ['admin', 'moderador', 'editor'] } }) );
//devuelve el documento de pedro y pablo porque su propiedad "rol" esta dentro del array//Tambien puedes usar dot notation en el path:
await db.find("dato1.dato2", {propiedad1: { $nin: ["valores", "a", "verificar"] }});
await db.find("dato1.dato2", {"data4.data5": "valor"});
`
* En miles de datos esto se agiliza si usas SecondayIndexes
---aggregate
$3
Ejecuta operaciones de agregación sobre los documentos que se encuentran en la ruta indicada por path.
Permite tanto:
- Usar un query simple (similar a find) para filtrar,
- Como definir un pipeline de etapas ($match, $group, $sort, $limit) para realizar transformaciones y cálculos más complejos.
- Ideal para consultas analíticas, resumenes estadisticos, o transformación de datos.
path (string): Ruta del documento donde se aplicará la búsqueda, se usa wildcard '\' para escanear toda la coleccion, tambien soporta dot notation '.'
* pipeline (array): Pipeline de etapas, cada elemento del array debe ser un objeto con una sola clave (ej: { $match: {...} }).
* retorna (promesa): Un array con los resultados de la agregación.* Etapas soportadas en el pipeline:
*
$match => Filtra documentos (igual que find, puedes usar operadores relacionales)
* $group => Agrupa por un campo (_id) y permite acumuladores ($sum, $avg, $min, $max).
- $ => Si vas a usar un campo especifico para evaluar alguna operacion usando algun acumulador, debes de colocar $ antes del nombre de la propiedad a evaluar, '$rol' => propiedad {'rol': valor}
- _id => Obligatorio si quieres agrupar por un campo especifico, el nombre del campo a usar debe comenzar con $, por ejemplo $rol => {user1:{rol: "admin"}}
- $sum => Suma valores numericos de un campo. su uso tipico es contar registros o sumar cantidades.
- Exp:
- totalEdad: {$sum: '$edad'}
- total: {$sum: 1}
- $avg => Calcula el promedio de un campo numerico.
- Exp:
- promedio: {$avg: '$edad'}
- $min => Devuelve el valor minimo de un campo dentro de cada grupo.
- Exp:
- edadMinima: {$min: '$edad'}
- $max => Devuelve el valor maximo de un campo dentro de cada grupo.
- Exp:
- edadMaxima: {$max: '$edad'}
* $sort => Ordena resultados por uno o varios campos que se usó en $group.
- El sort se aplica al resultado final de $group
- Si creaste en $group una propiedad llamada totalEdad y quieres usarlo en el sort, debes colocar esa propiedad.
- Las direcciones de orden soportadas son:
- -1 => Orden descendente (mayor a menor / Z - A / 9 - 0)
- 1 => Orden ascendente (menor a mayor / A - Z / 0 - 9)
- Puedes ordenar por 1 o mas campos seguidos de una coma ,
- Exp:
- {$sort: {_id: 1, totalEdad: -1}} => ordena de forma ascendente el campo _id y en base a lo ordenado ordena tambien pero ahora por totalEdad de forma descendente (sort inteligente multicampo).
* $limit => Limita la cantidad de resultados, devuelve solo los primeros N numeros del flujo.
- El limit se aplica al resultado final de $sort
- Si tienes 5 resultados finales y colocas un limite de 2, te devolvera solo los 2 primeros resultados. #### Ejemplos, agregamos primero estos datos a la db.
`js
await db.set("usuarios.pablo",{
id: "u1",
nombre: "pablo pablon",
edad: 42,
rol: "admin",
direccion: { ciudad: "xxxx", distrito: "aaaaaa" },
bio: "texto largo..."
});
await db.set("usuarios.pedro",{
id: "u2",
nombre: "pedro pedron",
edad: 17,
rol: "moderador",
direccion: { ciudad: "yyyyy", distrito: "eeeeee" },
bio: "texto largo..."
});
await db.set("usuarios.juan",{
id: "u3",
nombre: "juan juanon",
edad: 25,
rol: "trusted",
direccion: { ciudad: "zzzzz", distrito: "iiiiii" },
bio: "texto largo..."
});
await db.set("usuarios.patricio",{
id: "u4",
nombre: "patricio patrico",
edad: 26,
rol: "trusted",
direccion: { ciudad: "ttttt", distrito: "wwwwww" },
bio: "texto largo..."
});
`
En base a esos datos se haran los siguientes ejemplos:##### Filtrar con $match y agrupar con $group (queremos contar cuántos usuarios hay por cada rol)
`js
let data = await db.aggregate("usuarios", [
{ $match: { id: { $prefix: "u" } } },
{ $group: { _id: "$rol", total: { $sum: 1 } } }
]);
console.log(data);
/*
[
{ _id: "trusted", total: 2 },
{ _id: "admin", total: 1 },
{ _id: "moderador", total: 1 }
]
*/
`
##### Usar acumuladores $sum, $avg, $min, $max (queremos obtener estadísticas de edades por rol)
`js
let data = await db.aggregate("usuarios", [
{ $group: {
_id: "$rol",
total: { $sum: 1 },
edadPromedio: { $avg: "$edad" },
edadMinima: { $min: "$edad" },
edadMaxima: { $max: "$edad" }
}}
]);
console.log(data);
/*
[
{ _id: "trusted", total: 2, edadPromedio: 25.5, edadMinima: 25, edadMaxima: 26 },
{ _id: "admin", total: 1, edadPromedio: 42, edadMinima: 42, edadMaxima: 42 },
{ _id: "moderador", total: 1, edadPromedio: 17, edadMinima: 17, edadMaxima: 17 }
]
*/
`
##### Ordenar con $sort, vamos a ordenar los roles por numero de usuarios (descendente) y en caso de empate, por edad promedio (ascendente)
`js
let data = await db.aggregate("usuarios", [
{ $group: {
_id: "$rol",
total: { $sum: 1 },
edadPromedio: { $avg: "$edad" }
}},
{ $sort: { total: -1, edadPromedio: 1 } }
]);
console.log(data);
/*
[
{ _id: "trusted", total: 2, edadPromedio: 25.5 },
{ _id: "admin", total: 1, edadPromedio: 42 },
{ _id: "moderador", total: 1, edadPromedio: 17 }
]
*/
`
##### Limitar resultados con $limit, vamos a mostrar solo el primer rol con más usuariosjs
let data = await db.aggregate("usuarios", [
{ $group: { _id: "$rol", total: { $sum: 1 } } },
{ $sort: { total: -1 } },
{ $limit: 1 }
]);
console.log(data);
/*
[
{ _id: "trusted", total: 2 }
]
*/
`
Y asi como estos puedes hacerlo de acuerdo a lo que necesites, en el caso de wildcard para toda la coleccion => aggregate("*", [...etc])update
$3
Actualiza uno o varios valores dentro de un documento en la ruta indicada por path.
El parámetro ops es un objeto que define las operaciones a ejecutar y puedes aplicar mas de una operación en el mismo objeto.
*
path (string): Ruta del documento donde se aplicará la actualización, soporta dot notation '.'
* ops (object): Objeto con una o más operaciones de actualización.
* retorna (promesa): El documento actualizado (o parte de él, según la implementación).* Operaciones soportadas en ops:
-
$set => Establece un nuevo valor para una propiedad o la crea si no existe.
- Exp:
- {$set: {nombre: "nuevo nombre"}}
- {$set: {"direccion.ciudad": "xxxx"}}
- {$set: {nombre: "nuevo nombre", edad: 15}}
- $inc => Incrementa (o decrementa) un valor numerico.
- Exp:
- {$inc: {edad: 1}}
- {$inc: {edad: -1}}
- {$inc: {edad: 1, trabajos: 2}}
- $unset => Elimina una propiedad del documento.
- Exp:
- {$unset: {edad: ''}}
- {$unset: {edad: '', nombre: ''}}
- $push => Inserta un valor al final de un array, debes pasar un array con los elementos a agregar ["data1", "etc"]
- Exp:
- {$push: {frutas: ["manzana", "durazno"]}}
- {$push: {frutas: ["manzana, durazno"], libre: ["lunes"]}}
- $pull => Elimina un valor de un array (si existe), debes pasar un array con los elementos a eliminar ["data1", "etc"]
- Exp:
- {$pull: {frutas: ["manzana"]}}
- {$pull: {frutas: ["manzana, durazno"], libre: ["lunes"]}}* Puedes agregar mas de una operación y en cada operación mas de 1 dato usando la coma ,
#### Ejemplos, agregamos primero estos datos a la db.
`js
await db.set("mario", {nick: "mario30000", edad: 13, frutas: []});
await db.set("pedro", {nick: "pedro20000", edad: 11, frutas: []});
await db.set("juan", {nick: "juan10000", edad: 15, frutas: []});
`
#### Cambiar nick y sumar edad
`js
let nuevos_datos = await db.update("mario", {
$set: {nick: "marioPRO30000"},
$inc: {edad: 1}
});
console.log(nuevos_datos);
/*
{nick: "marioPRO30000", edad: 14, frutas: []}
*/
`
#### Eliminar campo frutas y añadir una propiedad verduras
`js
let nuevos_datos = await db.update("pedro", {
$unset: {frutas: ""},
$set: {verduras: []}
});
console.log(nuevos_datos);
/*
{nick: "pedro20000", edad: 11, verduras: []}
*/
`
#### agregar varias fruta y luego eliminar una
`js
let nuevos_datos = await db.update("juan", {
$push: {frutas: ["manzana", "durazno"]}
});
console.log(nuevos_datos);
/*
{nick: "juan10000", edad: 15, frutas: ["manzana", "durazno"]}
*/
let nuevos_datos = await db.update("juan", {
$pull: {frutas: ["durazno"]}
});
console.log(nuevos_datos);
/*
{nick: "juan10000", edad: 15, frutas: ["manzana"]}
*/
`
#### tambien puedes agregar varias operaciones.
`js
await db.update("juan"{
$set: {"juegos.overwatch": "TAG#AAAA", amigos: []}, // crea un objeto "juegos" y dentro una propiedad "overwatch": "TAG#AAAA", tambien amigos: []
$push: {frutas: ["platano"], etc....}, //agrega platano, etc...
$inc: {etc...} //aumenta, etc...
});
//con dot notation => update("path1.path2.path3.etc..")
`watch
$3
Permite observar cambios en tiempo real sobre un documento o ruta específica dentro de la base de datos.
Cada vez que ocurre una operación de escritura en esa ruta, se dispara el callback, recibiendo información detallada del cambio.
path (string): Ruta a observar, puede ser un documento exacto (usuarios.pablo) o una colección completa usando wildcard "usuarios." o directamente "usuarios", tambien '\*' para toda la coleccion.
* callback (function): Función que se ejecuta cada vez que hay un cambio.
* El callback recibe 4 parametros:
* path (string) => Ruta completa del valor afectado.
* newVal (any) => Valor nuevo (después de la operación).
* oldVal (any) => Valor anterior (antes de la operación).
* op (string) => Tipo de operación que disparó el cambio:
- "set" => se estableció o modificó un valor.
- "delete" => se eliminó un valor.
- "update" => se aplicó una operación de actualización.#### Ejemplos, teniendo en cuenta que esto se añadio al db:
`js
await db.set("mario", {nick: "mario30000", edad: 13, frutas: []});
await db.set("pedro", {nick: "pedro20000", edad: 11, frutas: []});
await db.set("juan", {nick: "juan10000", edad: 15, frutas: []});
`
#### Observar un documento especifico
`js
//Observamos solo a mario
db.watch("mario", (path, newVal, oldVal, op) => {
console.log([WATCH] Ruta: ${path});
console.log(Operación: ${op});
console.log(Antes:, oldVal);
console.log(Ahora:, newVal);
});//Esto dispara el watch porque actualizamos
await db.set("mario", { nick: "mario30000", edad: 14, frutas: [] });
/* en los console.log
[WATCH] Ruta: mario
Operación: set
Antes: { nick: "mario30000", edad: 13, frutas: [] }
Ahora: { nick: "mario30000", edad: 14, frutas: [] }
*/
`
#### Observar toda la colección
`js
//Observamos a todos los usuarios
db.watch("*", (path, newVal, oldVal, op) => {
console.log([WATCH] Cambio en ${path} (${op}));
});//Esto dispara el watch
await db.set("juan", { nick: "juan10000", edad: 16, frutas: [] });
//En el console.log [WATCH] Cambio en juan (set)
`
#### Detectar eliminacion
`js
//Observamos a pedro
db.watch("pedro", (path, newVal, oldVal, op) => {
if (op === "delete") {
console.log(El usuario en ${path} fue eliminado., oldVal);
}
});//Esto dispara el watch
await db.delete("pedro");
//en el console.log El usuario en pedro fue eliminado. { nick: "pedro20000", edad: 11, frutas: [] }
`
#### Reaccionar a cambios en arrays
`js
db.watch("mario", (path, newVal, oldVal, op) => {
if (op === "update") {
console.log([WATCH] Se actualizó la fruta de ${path});
console.log("Antes:", oldVal.frutas);
console.log("Ahora:", newVal.frutas);
}
});//Esto dispara el watch
await db.update("mario", { $push: { frutas: ["manzana"] } });
/* en el console.log
[WATCH] Se actualizó la fruta de mario
Antes: []
Ahora: [ 'manzana' ]
*/
//Puedes usar dot notation si tienes sub documentos dentro de un documento => db.watch("datos.usuario1", ...)
`stats
$3
Devuelve un objeto con estadísticas internas de la base de datos.
Está orientado a monitoreo y depuración, mostrando la cantidad de claves indexadas, bloques en memoria pendientes de escritura, uso de caché y más.
*
retorna: Un objeto con diferentes métricas internas:
* keys => cantidad total de claves registradas en el índice principal.
* dirtyBlocks => número de bloques “sucios”, es decir, modificados en memoria que aún no han sido volcados a disco.
* cachedBlocks => número de bloques actualmente mantenidos en caché para acceso rápido.
* secondaryIndexes => cantidad de índices secundarios definidos.
* keyCache => información sobre la caché de claves:
- size => número actual de claves en la caché.
- capacity => capacidad máxima de la caché (o null si no hay límite).
`js
//ejemplo db.stats()
{
keys: 3,
dirtyBlocks: 1,
cachedBlocks: 1,
secondaryIndexes: 2,
keyCache: { size: 3, capacity: 1000 }
}
`flush
$3
Fuerza la escritura inmediata en disco de todos los datos que se encuentran en memoria (que aún no se ha persistido por el flushInterval automatico).
Este método garantiza la consistencia de los datos en disco (guardado forzado manual)- Supongamos que pusiste en el constructor de MegaDB flushInterval: 120000 (2 minutos) para que los datos se acumulen y se escriban en disco en ese intervalo de tiempo, prendiste tu proyecto y en el minuto 1 guardaste un usuario con el set(), estos datos se actualizaran en memoria pero no en el db (bloque JSON) ya que aun falta 1 minuto para que llegue a los 2 minutos, por lo tanto si accedes al JSON manualmente aun veras los datos iguales hasta que el flushInterval haga su trabajo (llegue a los 2 minutos) y actualice el JSON correspondiente.
- El flush() sirve para forzar el guardado automaticamente, tomando el ejemplo de arriba, si colocas el flush() despues del set() que hiciste en el minuto 1, inmediatamente veras los datos reflejados en el bloque del JSON ya que el flush fuerza el guardado en disco.
`js
await db.set(etc...); //etc..
await db.flush();
`
close
$3
Cierra la base de datos de manera ordenada, asegurando que todos los cambios pendientes en memoria se persistan en disco y deteniendo procesos internos.
* Qué hace internamente:
* Hace un flush de todos los datos que aun estan pendientes por escribirse en disco.
* Se detienen los timers internos (Interval) y el proceso de limpieza del índice
* Se recomienda usarlo al final del ciclo de vida de tu proyecto o al cerrar conexiones.
`js
await db.close();
`createBackup
$3
Crea un respaldo persistente del estado actual de la base de datos.
Los backups se guardan en el directorio configurado para backups, junto con un nombre único (counter o timestamp, dependiendo de backupMode).
* retorna (promesa): true si se creo, false si no.
`js
await db.createBackup();
`restoreBackup
$3
Restaura un backup previamente creado con createBackup() o automaticamente por el backupInterval, sobrescribe el estado actual de la base de datos con el contenido del backup.*
name (string): nombre del backup a restaurar (ej: "backup-2025-09-05T18-00-00" o dependiendo del nombre).
* retorna (promesa): true si se restauró, false si no.
`js
await db.restoreBackup("backup-2025-09-05T18-00-00");
`createSnapshot
$3
Genera un snapshot en memoria del estado actual de la base de datos.
El snapshot es un buffer serializado, útil para enviar por red, guardar temporalmente o clonar estados.
* retorna (promesa): Buffer binario con el snapshot serializado.
`js
const snapshot = await db.createSnapshot(); //const snapshot contiene el buffer
`restoreSnapshot
$3
Restaura un snapshot previamente creado con createSnapshot(), sobrescribe completamente el estado actual de la base de datos.*
buffer (Buffer): snapshot previamente guardado.
* retorna (promesa): true si se realizo, false si no.
`js
const db_antiguo = await db.createSnapshot();//despues de crear el snapshot guardas miles de datos pero por X motivos quieres regresar al estado antes de los miles de datos guardados
let estado = await db.restoreSnapshot(db_antiguo);
console.log(estado); //true, te restablecio el db a antes de los cambios.
`
---
2.
MegaDBSafeMegaDBSafe hereda de MegaDB, pero añade varias capacidades nuevas pensadas para concurrencia, durabilidad y multi-proceso:
* WAL (Write-Ahead Log)
* Todas las escrituras (set, delete, update) primero se registran en un log (.wal).
* Si la app se cae, al reiniciar se hace replay de ese log para restaurar la consistencia.
* IndexStore
* Permite persistir índices secundarios (secondaryIndexes) en un archivo separado y recargarlos rápido al iniciar.
* OpQueue (QueuedLock)
* Serializa operaciones en memoria FIFO dentro de un mismo proceso.
* Garantiza que múltiples llamados de escritura/guardado concurrentes no choquen.
* Stats extendido
* Mejoras internas para un guardado/lectura mas profesional.
* Esta pensado para proyectos medianos/grandes.Conceptos clave mas importantes
* WAL (Write-Ahead Log)
* Es un log secuencial en disco donde se escriben todas las operaciones antes de aplicarlas a los bloques.
* Garantiza durabilidad, si la app se cae, basta con leer el WAL para aplicar los cambios faltantes.
* OpQueue (QueuedLock)
* Una cola FIFO en memoria que serializa operaciones concurrentes dentro del mismo proceso.
* Evita que dos escrituras dentro de la misma app se pisen.
* IndexStore
* Se encarga de persistir y restaurar los índices secundarios (secondaryIndexes) en un archivo separado, evitando reconstruirlos desde cero en cada arranque.MegaDBSafe
`js
const { MegaDBSafe } = require("megadbx");
const db = new MegaDBSafe(collectionName, options = {});
`- collectionName (string): nombre de la colección (carpeta en ./db/).
- options (objeto, opcional):
MegaDBSafe contiene todo lo que MegaDB ya tiene, puedes seguir usando sus opciones del constructor y otras nuevas:
| Opción | Default | Explicación |
| ---------------------- | ---------------- | ---------------------------------------------------------------------------- |
|
replayWal | true | Si al iniciar encuentra un archivo .wal, lo reproduce |(Explicacion detallada)
replayWal: Si al iniciar encuentra un archivo .wal, lo reproduce para aplicar operaciones pendientes y restaurar consistencia.
- true → siempre reejecuta las operaciones pendientes (.wal).
- false → ignora el WAL (solo recomendado para debugging o si ya sabes que el estado está íntegro)
- El wal no es acumulativo, se reinicia entre cada inicio, guarda las operaciones que se agregó al iniciar el proyecto/app, los lee al iniciar nuevamente el proyecto/app y resetea.Puedes seguir usando las opciones que se le pasa al constructor MegaDB
ready
$3
Carga correctamente los procesos de arranque, esto garantiza que todos los procesos, metodos, etc, funcionen sin errores de sincronización.
Es indispensable al iniciar MegaDBSafe, obligatorio despues de llamar al constructor y antes de ejecutar alguna funcion (solo se llama 1 vez).
`js
const db = new MegaDBSafe({
dir: './',
secondaryIndexes: ["rol"],
replayWal: true
});await db.ready(); //esperar a que la DB esté lista
//resto de la logica de tu proyecto...
`
set
$3
Es lo mismo que MegaDB.set(path, value).has
$3
Es lo mismo que MegaDB.has(path).get
$3
Es lo mismo que MegaDB.get(path).delete
$3
Es lo mismo que MegaDB.delete(path).all
$3
Es lo mismo que MegaDB.all(path).keys
$3
Es lo mismo que MegaDB.keys(path).values
$3
Es lo mismo que MegaDB.values(path).count
$3
Es lo mismo que MegaDB.count(path, query).find
$3
Es lo mismo que MegaDB.find(path, query = {}).aggregate
$3
Es lo mismo que MegaDB.aggregate(path, pipeline).update
$3
Es lo mismo que MegaDB.update(path, ops).watch
$3
Es lo mismo que MegaDB.watch(path, callback).stats
$3
Es lo mismo que MegaDB.stats() pero contiene mas cosas.
* retorna (promesa): Muy a parte de retornar las metricas keys, dirtyBlocks, cachedBlocks, secondaryIndexes, keyCache, tambien muestra:
* wal (object): estado del Write-Ahead Log.
* enabled: si está activo (replayWal: true).
* file: archivo donde se almacena el WAL.
* pendingOps: número de operaciones en el log que aún no se han aplicado.
* locks (object): información sobre los locks de concurrencia.
* queueLength: número de operaciones esperando turno en el OpQueue.
* indexStore (object): estado de los índices persistidos.
* loaded: true/false, indica si los índices secundarios se cargaron desde disco.
* file: archivo asociado al almacenamiento de índices.
`js
//ejemplo: await db.stats()
{
keys: 120,
dirtyBlocks: 2,
cachedBlocks: 8,
secondaryIndexes: 2,
keyCache: { size: 80, capacity: 1000 },
wal: {
enabled: true,
pendingOps: 3,
file: "./db/data/wal.log"
},
locks: {
queueLength: 2
},
indexStore: {
loaded: true,
file: "./db/data/indexes.json"
}
}
``