NeDB: una base de datos JavaScript ligera

    Cuando piensa en una base de datos, lo primero que se le puede ocurrir es MySQL, MongoDB o PostgreSQL. Si bien todas estas son excelentes opciones para almacenar datos, todas están sobrecargadas para la mayoría de las aplicaciones.

    Considere una aplicación de chat de escritorio escrita con el Electrón framework en JavaScript. Si bien los datos del chat (mensajes, contactos, historial, etc.) probablemente se originen en un servidor API, también deberían almacenarse localmente dentro de la aplicación. Potencialmente, podría tener miles de mensajes, todos los cuales deberían almacenarse para facilitar el acceso y la búsqueda.

    Entonces, ¿Qué haces? Una opción es almacenar todos estos datos en un archivo en algún lugar y simplemente buscarlos cada vez que necesite recuperarlos, pero esto puede ser ineficiente. Otra opción es simplemente no almacenar en caché los datos localmente y hacer una llamada al servidor de API cada vez que necesite más datos, pero entonces su aplicación responderá menos y utilizará muchos más datos de red.

    Una mejor idea es utilizar una base de datos incorporada / ligera, como NeDB. Esto tiene más sentido porque su aplicación no atenderá a miles de usuarios ni manejará gigabytes de datos.

    NeDB es muy parecido a SQLite ya que es una versión más pequeña e integrable de un sistema de base de datos mucho más grande. En lugar de ser un almacén de datos SQL más pequeño, NeDB es un almacén de datos NoSQL más pequeño que imita MongoDB.

    Una base de datos liviana generalmente almacena sus datos en la memoria o en un archivo de texto sin formato (con índices para búsquedas rápidas). Esto ayuda a reducir el espacio total de la base de datos en el sistema, que es perfecto para aplicaciones más pequeñas. A modo de comparación, el archivo tar de MySQL (para Mac OSX) es de 337 MB, mientras que NeDB (sin comprimir, no minificado) tiene solo 1,5 MB.

    Una de las mejores cosas de NeDB específicamente es que su API es un subconjunto de la API de MongoDB, por lo que si está familiarizado con MongoDB, no debería tener problemas para trabajar con NeDB después de la configuración inicial.

    Nota: A partir de v1.8.0, NeDB aún no se ha actualizado a algunos de los nuevos nombres de métodos de Mongo, como insertOne, insertMany y la eliminación de findOne.

    Introducción a NeDB

    Primero, instale el módulo con NPM:

    $ npm install nedb --save
    

    El módulo está escrito en JavaScript puro, por lo que no debería haber ningún problema al compilar plugins nativos como ocurre a veces con los controladores MongoDB.

    Si planea usarlo en el navegador, use Bower para instalar:

    $ bower install nedb
    

    Como todos los clientes de bases de datos, el primer paso es conectarse a la base de datos backend. Sin embargo, en este caso no hay una aplicación externa a la que conectarse, por lo que solo necesitamos indicarle la ubicación de sus datos. Con NeDB, tiene algunas opciones para guardar sus datos. La primera opción es guardar los datos en la memoria:

    var Datastore = require('nedb');
    var db = new Datastore();
    
    // Start issuing commands right away...
    

    Esto lo iniciará sin datos y, cuando salga de la aplicación, se perderán todos los datos guardados. Aunque es ideal para usar durante pruebas o sesiones más cortas (como en el navegador).

    O la otra opción es guardar los datos en un archivo. La diferencia aquí es que debe especificar la ubicación del archivo y cargar los datos.

    var Datastore = require('nedb');
    var db = new Datastore({ filename: 'path/to/your/file' });
    
    db.loadDatabase(function(err) {
        // Start issuing commands after callback...
    });
    

    Si no quieres llamar db.loadDatabase para cada base de datos que cargue, siempre puede usar el autoload: true opción también.

    Una cosa importante a tener en cuenta es que cada archivo es equivalente a uno colección en MongoDB. Por lo tanto, si tiene varias colecciones, deberá cargar varios archivos al inicio. Entonces, su código podría verse así:

    var Datastore = require('nedb');
    var users = new Datastore({ filename: 'users.db', autoload: true });
    var tweets = new Datastore({ filename: 'tweets.db', autoload: true });
    var messages = new Datastore({ filename: 'messages.db', autoload: true });
    

    Guardar datos

    Después de cargar sus datos desde archivos (o crear almacenamiento en memoria), querrá comenzar a guardar datos.

    Al igual que los controladores Mongo, usará insert para crear un nuevo documento:

    var Datastore = require('nedb');
    var users = new Datastore();
    
    var scott = {
        name: 'Scott',
        twitter: '@ScottWRobinson'
    };
    
    users.insert(scott, function(err, doc) {
        console.log('Inserted', doc.name, 'with ID', doc._id);
    });
    
    // Prints to console...
    // (Note that ID will likely be different each time)
    //
    // "Inserted Scott with ID wt3Nb47axiOpme9u"
    

    Esta inserción se puede ampliar fácilmente para guardar varios documentos a la vez. Usando el mismo método, simplemente pase una matriz de objetos y cada uno se guardará y se le devolverá en la devolución de llamada:

    var Datastore = require('nedb');
    var users = new Datastore();
    
    var people = [];
    
    var scott = {
        name: 'Scott Robinson',
        age: 28,
        twitter: '@ScottWRobinson'
    };
    
    var elon = {
        name: 'Elon Musk',
        age: 44,
        twitter: '@elonmusk'
    };
    
    var jack = {
        name: 'Jack Dorsey',
        age: 39,
        twitter: '@jack'
    };
    
    people.push(scott, elon, jack);
    
    users.insert(people, function(err, docs) {
        docs.forEach(function(d) {
            console.log('Saved user:', d.name);
        });
    });
    
    // Prints to console...
    //
    // Saved user: Scott Robinson
    // Saved user: Elon Musk
    // Saved user: Jack Dorsey
    

    La actualización de documentos existentes funciona de manera muy similar, excepto que deberá proporcionar una consulta para indicarle al sistema qué documentos deben actualizarse.

    Cargando datos

    Ahora que tenemos un montón de datos guardados, es hora de recuperarlos de la base de datos. Nuevamente, seguiremos la misma convención que Mongo con el find método:

    var Datastore = require('nedb');
    var users = new Datastore();
    
    // Save a bunch of user data here...
    
    users.findOne({ twitter: '@ScottWRobinson' }, function(err, doc) {
        console.log('Found user:', doc.name);
    });
    
    // Prints to console...
    //
    // Found user: Scott Robinson
    

    Y nuevamente, podemos usar una operación similar para recuperar múltiples documentos. Los datos devueltos son solo una matriz de documentos coincidentes:

    var Datastore = require('nedb');
    var users = new Datastore();
    
    // Save a bunch of user data here...
    
    users.find({ age: { $lt: 40 }}, function(err, docs) {
        docs.forEach(function(d) {
            console.log('Found user:', d.name);
        });
    });
    
    // Prints to console...
    //
    // Found user: Jack Dorsey
    // Found user: Scott Robinson
    

    Es posible que haya notado en este último ejemplo de código que NeDB, como era de esperar, es capaz de realizar consultas más complejas, como comparaciones de números. Los siguientes operadores están disponibles para buscar / comparar documentos:

    • $lt, $lte: menor que, menor que o igual
    • $gt, $gte: mayor, mayor o igual que
    • $in: valor contenido en la matriz
    • $nin: valor no contenido en la matriz
    • $ne: no es igual
    • $exists: comprueba la existencia (o no existencia) de una propiedad determinada
    • $regex: coincide con la cadena de una propiedad con expresiones regulares

    También puede utilizar las operaciones estándar de clasificación, limitación y omisión. Si no se devuelve la llamada al find método, luego un Cursor En su lugar, se le devolverá el objeto, que luego podrá usar para ordenar, limitar y omitir. A continuación, se muestra un ejemplo de ordenación alfabética por nombre:

    var Datastore = require('nedb');
    var users = new Datastore();
    
    // Save a bunch of user data here...
    
    users.find({}).sort({name: 1}).exec(function(err, docs) {
        docs.forEach(function(d) {
            console.log('Found user:', d.name);
        });
    });
    
    // Prints to console...
    //
    // Found user: Elon Musk
    // Found user: Jack Dorsey
    // Found user: Scott Robinson
    

    Las otras dos operaciones, saltar y limitar, funcionan de manera muy similar a esto.

    Hay bastantes operadores más admitidos por el find y findOne métodos, pero no los veremos todos aquí. Puede leer en detalle sobre el resto de estas operaciones en el encontrar documentos sección del archivo README.

    Eliminar datos

    No hay mucho que decir sobre la eliminación de datos aparte de que funciona de manera similar a la find métodos. Utilizará los mismos tipos de consultas para encontrar los documentos relevantes en la base de datos. Los que se encuentran se eliminan.

    var Datastore = require('nedb');
    var users = new Datastore();
    
    // Save a bunch of user data here...
    
    users.remove({ name: { $regex: /^Scott/ } }, function(err, numDeleted) {
         console.log('Deleted', numDeleted, 'user(s)');
    });
    
    // Prints to console...
    //
    // Deleted 1 user(s)
    

    Por defecto, el remove El método solo elimina un documento. Para eliminar varios documentos con una sola llamada, debe configurar el multi opción a true.

    var Datastore = require('nedb');
    var users = new Datastore();
    
    // Save a bunch of user data here...
    
    users.remove({}, { multi: true }, function(err, numDeleted) {
         console.log('Deleted', numDeleted, 'user(s)');
    });
    
    // Prints to console...
    //
    // Deleted 3 user(s)
    

    Datos de indexación

    Al igual que con cualquier otra base de datos, puede establecer índices en sus datos para una recuperación más rápida o para hacer cumplir ciertas restricciones, como valores únicos. Para crear el índice, use el ensureIndex método.

    Los tres tipos de índices admitidos actualmente son:

    • unique: asegúrese de que el campo dado sea único en toda la colección
    • sparse: no indexar documentos en los que el campo dado no está definido
    • expireAfterSeconds: elimina el documento después del número de segundos especificado (tiempo para viviro TTL)

    El índice TTL es especialmente útil, en mi opinión, ya que le evita tener que escribir código para escanear y eliminar con frecuencia datos que han caducado.

    Esto puede resultar útil, por ejemplo, con solicitudes de restablecimiento de contraseña. Si tienes un PasswordReset objeto almacenado en su base de datos, no querrá que sea válido para siempre. Para ayudar a proteger al usuario, probablemente debería caducar y eliminarse después de unos días. Este índice TTL puede manejar su eliminación por usted.

    En el siguiente ejemplo, hemos colocado el unique restricción en los identificadores de Twitter de los documentos. Esto significa que si un usuario se guarda con el mismo identificador de Twitter que otro usuario, se lanzará un error.

    var Datastore = require('nedb');
    var users = new Datastore();
    
    users.ensureIndex({ fieldName: 'twitter', unique: true });
    
    var people = [];
    
    var jack = {
        name: 'Jack Dorsey',
        age: 39,
        twitter: '@jack'
    };
    
    var jackSmith = {
        name: 'Jack Smith',
        age: 68,
        twitter: '@jack'
    };
    
    people.push(jack, jackSmith);
    
    users.insert(people, function(err, docs) {
        console.log('Uh oh...', err);
    });
    
    // Prints to console...
    //
    // Uh oh... Can't insert key @jack, it violates the unique constraint
    

    Llevándolo más lejos

    Si bien la API de NeDB es fácil de usar y todo, su código puede volverse bastante difícil de trabajar si no está bien pensado y organizado. Aquí es donde los mapeadores de documentos de objetos (que es como un ORM) ven a jugar.

    Utilizando la ODM de camuflaje (que creé), simplemente puede tratar los almacenes de datos NeDB como clases de JavaScript. Esto le permite especificar un esquema, validar datos, extender esquemas y más. Camo incluso también funciona con MongoDB, por lo que puede usar NeDB en entornos de prueba / desarrollo y luego usar Mongo para su sistema de producción sin tener que cambiar nada de su código.

    Aquí hay un ejemplo rápido de cómo conectarse a la base de datos, declarar un objeto de clase y guardar algunos datos:

    var connect = require('camo').connect;
    var Document = require('camo').Document;
    
    class User extends Document {
        constructor() {
            super();
    
            this.name = String;
            this.age = Number;
            this.twitter = Sring;
        }
    
        get firstName() {
            return this.name.split(' ')[0];
        }
    }
    
    var scott = User.create({
        name: 'Scott Robinson',
        age: 28,
        twitter: '@ScottWRobinson'
    });
    
    var elon = User.create({
        name: 'Elon Musk',
        age: 44,
        twitter: '@elonmusk'
    });
    
    connect('nedb://memory').then(function(db) {
        return Promise.all([scott.save(), elon.save()]);
    }).then(function(users) {
        users.forEach(function(u) {
            console.log('Saved user:', u.firstName);
        });
    
        return elon.delete();
    }).then(function() {
        console.log('Deleted Elon!')
    });
    
    // Prints to console...
    //
    // Saved user: Scott
    // Saved user: Elon
    // Deleted Elon!
    

    Hay mucho más en este ODM de lo que he mostrado aquí. Para obtener más información, consulte este artículo o el README del proyecto para la documentación.

    Conclusión

    Dado que NeDB es bastante pequeño (¡y bastante rápido!), Es muy fácil agregarlo a casi cualquier proyecto. Y con Camo en la mezcla, solo necesitas unas pocas líneas de código para declarar objetos basados ​​en clases que son mucho más fáciles de crear, eliminar y manipular.

    Si alguna vez ha utilizado NeDB en uno de sus proyectos, nos encantaría saberlo. ¡Háznoslo saber en los comentarios!

     

    Etiquetas:

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *