elisa

    0.5.3 • Public • Published

    NPM version Build Status Dependency Status devDependency Status

    API for accessing to databases.

    Proudly made with ♥ in Valencia, Spain, EU.

    Elisa.js

    Elisa is an API for connecting to databases.

    Her features are:

    • This is independent of the DBMS. A query using an Elisa driver must return the same result in any database. So, the developers can switch from a database to another without problems.
    • This has the same API for the browser and Node.js. So, the developers can access to databases using the same code in the browser app and the Node.js app.
    • This can work with key-value stores (Redis) and document collections (CouchDB, PouchDB and MongoDB). Proximately, Elisa is going to support SQL databases (MySQL, MariaDB, PostgreSQL, SQL Server, SQLite, etc.).
    • This has a synchronous API and an asynchronous one.
    • This supports the field value injection to ease the query writing.
    • This is easy for learning.
    • This is simple for using.

    Official site: elisajs.org.

    Table of contents

    Preview examples

    Asynchronous example

    //import driver to use
    const Driver = require("elisa-pouchdb").Driver;
    
    //get driver to use
    const driver = Driver.getDriver("PouchDB");
    
    //document collection operations
    driver.openConnection({host: "localhost", port: 5984, db: "testing"}, function(error, cx) {
      const bands = cx.db.getCollection("core.bands");
    
      //insert
      bands.insert([{name: "The National", year: 1999}, {name: "Echo and the Bunnymen", year: 1978}]);
    
      //update
      bands.update({name: "The National", {tags: {$add: "indie"}});
    
      //remove
      bands.remove({year: {$between: [1990, 2000]}});
    
      //find
      bands.find({tags: {$contain: "indie"}}, function(error, res) {
        for (let band of res.docs) console.log(band.id, band.name, band.year);
      });
    
      bands.findOne({name: "The National"}}, function(error, band) {
        if (band) console.log(band.id, band.name, band.year);
      });
    });
    
    //key-value store operations
    driver.openConnection({host: "localhost", port: 5984, db: "testing"}, function(error, cx) {
      const bands = cx.db.getStore("core.bands");
    
      //insert
      bands.insert([{id: "The National", year: 1999}, {id: "Echo and the Bunnymen", year: 1978}]);
    
      //update
      bands.update({id: "The National"}, {tags: {$add: "indie"}});
    
      //remove
      bands.remove({id: "The National"});
    
      //find
      bands.find({id: "The National"}, function(error, res) {
        for (let band of res.docs) console.log(band.id, band.name, band.year);
      });
    });
    

    Synchronous example

    //import driver to use
    const Driver = require("elisa-pouchdb").Driver;
    
    //get driver to use
    const driver = Driver.getDriver("PouchDB");
    
    //open connection
    const cx = driver.openConnection({type: "sync"}, {host: "localhost", port: 5984, db: "testing"});
    
    //document collection operations
    var bands = cx.db.getCollection("core.bands");
    
    //insert
    bands.insert([{name: "The National", year: 1999}, {name: "Echo and the Bunnymen", year: 1978}]);
    
    //update
    bands.update({name: "The National", {tags: {$add: "indie"}});
    
    //remove
    bands.remove({year: {$between: [1990, 2000]}});
    
    //find
    var res = bands.find({tags: {$contain: "indie"}});
    for (let band of res.docs) console.log(band.id, band.name, band.year);
    
    var band = bands.findOne({name: "The National"}});
    if (band) console.log(band.id, band.name, band.year);
    
    //key-value store operations
    var bands = cx.db.getStore("core.bands");
    
    //insert
    bands.insert([{id: "The National", year: 1999}, {id: "Echo and the Bunnymen", year: 1978}]);
    
    //update
    bands.update({id: "The National"}, {tags: {$add: "indie"}});
    
    //remove
    bands.remove({id: "The National"});
    
    //find
    var res = bands.find({id: "The National"});
    for (let band of res.docs) console.log(band.id, band.name, band.year);
    

    Drivers

    A driver is a component enabling a browser/Node.js app to interact with databases. The Elisa driver is an analogous concept to the JDBC/ODBC/ADO.NET drivers.

    To connect a database, first we have to load its driver. We must use the Driver class, concretely its getDriver() method:

    getDriver(name : string) : Driver
    

    Next, we show an example to get the PouchDB driver and the MySQL driver:

    //PouchDB driver
    const Driver = require("elisa-pouchdb").Driver; //load
    var driver = Driver.getDriver("PouchDB");       //get
    
    //MySQL driver
    const Driver = require("elisa-mysql").Driver;   //load
    var driver = Driver.getDriver("MySQL");         //get
    

    First, we have to get the Driver class of the driver package. Next, we have to get the driver to use by its name.

    Connections

    A connection is an object to access to a concrete database. We can use the Driver.createConnection() method:

    //async connection
    createConnection(opts : object) : Connection
    createConnection({type: "async"}, opts : object) : Connection
    
    //sync connection
    createConnection({type: "sync"}, opts : object) : Connection
    

    A synchronous connection performs all operations, find(), insert(), update(), etc., synchronously. An asynchronous connection, asynchronously. By default, the connections are asynchronous.

    Example:

    //async
    var cx = driver.createConnection({host: "localhost", port: 5984, db: "testing"});
    
    //sync
    var cx = driver.createConnection({type: "sync"}, {host: "localhost", port: 5984, db: "testing"});
    

    The connection options are driver dependent. Some common options are:

    • host (string). The DBMS server.
    • port (number). The DBMS port.
    • db (string). The database.
    • username (string). The username.
    • password (string). The user password.

    Opening connections

    Once we have the connection object, we have to open it, using its open() method:

    //sync connection
    open()
    
    //async connection
    open(callback : function(error))
    

    An example:

    //sync connection
    cx.open();
    
    //async connection
    cx.open(function(error) {
      ...
    });
    

    We can create the connection and open it in one step with the Driver.openConnection() method:

    //sync connection
    openConnection({type: "sync"}, opts : object) : Connection
    
    //async connection
    openConnection(opts : object, callback : function(error, cx))
    

    Example:

    //sync connection
    var cx = driver.openConnection({type: "sync"}, {host: "localhost", port: 5984, db: "testing"});
    
    //async connection
    driver.openConnection({host: "localhost", port: 5984, db: "testing"}, function(error, cx) {
      ...
    });
    

    Closing connections

    To close a connection, we can use its close() method:

    //sync connection
    close()
    
    //async connection
    close(callback ?: function(error))
    

    Connection status

    To know if a connection is opened or closed, we can use the following properties:

    opened : boolean
    closed : boolean
    

    To know if the connection is opened and currently is connected, the connected() method:

    //sync connection
    connected() : boolean
    
    //async connection
    connected(callback : function(error, connected))
    

    The connection can be lost if the server, for example, is shut down. In that case, the opened property will be true, but connected() will return false.

    Server

    The server is an object to represent the database server. Its object is got from the Connection.server property:

    server : Server
    

    This object contains the following properties:

    • host (string). The server host.
    • port (number). The server port.
    • version (string). The server version.

    For example, on MariaDB, we could get:

    console.dir(cx.server.server); //{ host: 'localhost', port: 3306, version: '10.1.14-MariaDB' }
    

    Databases

    The database is an object to represent a database in the database server. The current database is accessed using the Connection.db property:

    db : Database
    

    Example:

    console.log(cx.db.name);
    

    Namespaces

    A namespace is a logical container of database objects. If the DBMS doesn't support this concept, the driver must implement it. So for example, MariaDB, MySQL and PouchDB don't support the namespace concept, but their Elisa drivers do.

    On SQL, a namespace is similar to a schema.

    To get a namespace object, we can use the following methods of the database object:

    //sync connection
    getNamespace(name : string, opts ?: object) : Namespace
    findNamespace(name : string, opts ?: object) : Namespace
    
    //async connection
    getNamespace(name : string, opts ?: object) : Namespace
    findNamespace(name : string, opts ?: object, callback : function(error, ns))
    

    The first method, getNamespace(), doesn't check whether the namespace exists. The second one, findNamespace(), does it.

    Example:

    //sync connection
    var hr = db.getNamespace("hr");
    var hr = db.findNamespace("hr");
    
    //async connection
    var hr = db.getNamespace("hr");
    db.findNamespace("hr", function(error, ns) {
      ...  
    });
    

    The opts is used to set specific issues of the driver. For example, with the PouchDB driver, we can indicate the design document name into opts, whose default value is the namespace name:

    var hr = db.getNamespace("hr", {design: "hr"});
    

    If we only need to know if a namespace exists, we can use the Database.hasNamespace() method:

    //sync connection
    hasNamespace(name : string) : boolean
    
    //async connection
    hasNamespace(name : string, callback : function(error, exists))
    

    Key-value stores

    A store is a data container where every data has a key and a value. If the DBMS doesn't support this concept natively, the driver should do it. So for example, MariaDB, MongoDB, MySQL and PouchDB don't support the stores, but their drivers do it.

    To get a store object, we must use the following methods:

    //sync connection
    db.getStore(ns : string, store : string, opts ?: object) : Store
    db.getStore(qn : string, opts ?: object) : Store
    db.findStore(ns : string, store : string, opts ?: object) : Store
    db.findStore(qn : string, opts ?: object) : Store
    
    ns.getStore(name : string, opts ?: object) : Store
    ns.findStore(name : string, opts ?: object) : Store
    
    //async connection
    db.getStore(ns : string, store : string, opts ?: object) : Store
    db.getStore(qn : string, opts ?: object) : Store
    db.findStore(ns : string, store : string, opts?: object, callback : function(error, store))
    db.findStore(qn : string, opts ?: object, callback : function(error, store))
    
    ns.getStore(name : string, opts ?: object) : Store
    ns.findStore(name : string, opts ?: object, callback : function(error, store))
    

    The getStore() method doesn't check whether the store exists; but findStore() does.

    Examples:

    //sync connection
    var emp = db.getStore("hr.Employee"); //if no namespace is needed, db.getStore("Employee")
    var emp = db.findStore("hr.Employee");//if no namespace is needed, db.findStore("Employee")
    
    //async connection
    var emp = db.getStore("hr.Employee");
    db.findStore("hr.Employee", function(error, emp) {
      ...
    });
    

    As with getNamespace() and findNamespace(), with getStore() and findStore() we can set specific issues of the DBMS. For example, the PouchDB driver allows to set the view name using the view parameter. Example:

    var emp = db.getStore("hr.Employee", {design: "hr", view: "employee"});
    

    To know if a store exists, we can use the hasStore() method:

    //sync connection
    Database.hasStore(ns : string, store : string) : boolean
    Database.hasStore(qn : string) : boolean
    Namespace.hasStore(name : string) : boolean
    
    //async connection
    Database.hasStore(ns : string, store : string, callback : function(error, exists))
    Database.hasStore(qn : string, callback : function(error, exists))
    Namespace.hasStore(name : string, callback : function(error, exists))
    

    FQN and QN

    The full qualified name (FQN) is the full name to a store object: db.namespace.store. The qualified name (QN) is the partial name: namespace.store.

    If no namespace is needed, the FQN is db.store; and the QN, store.

    Inserting items

    To insert a value, we use the insert() method:

    //sync connection
    insert(value : object)
    insert(values : object[])
    
    //async connection
    insert(value : object, callback ?: function(error))
    insert(values : object[], callback ?: function(error))
    

    Every value must be an object where its id property is the key. Example:

    //sync connection
    bands.insert({id: "The National", year: 1999});
    bands.insert([
      {id: "The National", year: 1999},
      {id: "Echo and the Bunnymen", year: 1978}
    ]);
    
    //async connection
    bands.insert({id: "The National", year: 1999}, function(error) {
      ...
    });
    
    bands.insert([
      {id: "The National", year: 1999},
      {id: "Echo and the Bunnymen", year: 1978}
    ], function(error) {
      ...
    });
    

    Finding items

    To find an item, we can use the find() method or the findOne() method:

    //sync connection
    findOne({id: "key"}) : object
    find({id: "key"}) : object
    
    //async connection
    findOne({id: "key"}, callback : function(error, item))
    find({id: "key"}, callback : function(error, item))
    

    Example:

    //sync connection
    var band = bands.findOne({id : "The National"});
    
    //async connection
    bands.findOne({id: "The National"}, function(error, item) {
      ...
    });
    

    If we want to get all the items from a store, we must use its findAll() method:

    //sync connection
    findAll() : Result
    
    //async connection
    findAll(callback : function(error, result))
    

    The result is an object that contains:

    • length (number). The number of items.
    • docs (object[]). The documents.
    • rows (object[]). Alias of docs.

    Example:

    //sync connection
    var res = bands.findAll();
    for (let band of res.docs) console.log(band.id, band.year);
    
    bands.findAll(function(error, res) {
      if (!error) {
        for (let band of res.docs) console.log(band.id, band.year);
      }
    });
    

    If we need to know if a key exists, be able to use the Store.hasId() method:

    //sync connection
    hasId("id") : boolean
    
    //async connection
    hasId("id", callback : function(error, exists))
    

    To get the number of items, we will use the Store.count() method:

    //sync connection
    count() : number
    
    //async connection
    count(callback : function(error, count))
    

    Updating values

    To update a value property, we can use the update() method:

    //sync connection
    update({id: "key"}, update : object)
    
    //async connection
    update({id: "key"}, update : object, callback ?: function(error))
    

    The first parameter must be an object with the id property, that is, the key to update. The second parameter must contain the change operators. Example:

    //sync connection
    bands.update({id: "The National"}, {year: 1999, origin: "Cincinnati, Ohio"});
    
    //async connection
    bands.update({id: "The National"}, {year: 1999, origin: "Cincinnati, Ohio"}, function(error) {
      ...
    });
    

    Update operators

    The update object is similar to MongoDB and to Mango:

    • {field: value}. Set the new value.
    • {field: {$set: value}}. Set the new value.
    • {field: {$inc: value}}. Increment the value.
    • {field: {$dec: value}}. Decrement the value.
    • {field: {$mul: value}}. Multiply the value.
    • {field: {$div: value}}. Divide the value.
    • {field: {$add: value}}. Add an item to an array if the value doesn't exist.
    • {field: {$remove: value}}. Remove the value from the field.
    • {field: {$push: value}}. Add an item at the end of an array.
    • {field: {$concat: value}}. Concatenate a string at the end of another.
    • {field: {$pop: value}}. Remove the given number of items.

    The format is very simple:

    field: {$operator: value}
    

    Examples of operations:

    //year += 2
    {year: {$inc: 2}}
    
    //add to set
    {tags: {$add: "indie"}}
    

    Saving items

    Another way to save a document into a key-value store is using the save() method:

    //sync connection
    store.save(doc : object);
    store.save(doc : object, opts : object);
    
    //async connection
    store.save(doc : object, callback ?: function(error));
    store.save(doc : object, opts : object, callback ?: function(error));
    

    When the save() method is used, the document must have an id field. When the id exists, the document is replaced; otherwise, it is inserted.

    This method implements a UPSERT operation.

    Removing items

    To remove an item, we must use the remove() method:

    //sync connection
    remove({id: "key"}, opts ?: object)
    
    //async connection
    remove({id: "key"}, opts ?: object, callback ?: function(error))
    

    Example:

    //sync connection
    bands.remove({id: "The National"});
    
    //async connection
    bands.remove({id: "The National"}, function(error) {
      ...
    });
    

    To remove all the items, we must use the truncate() method:

    //sync connection
    truncate(opts ?: object)
    
    //async connection
    truncate(opts ? object, callback ?: function(error))
    

    Document collections

    A collection is a document container. If the DBMS doesn't support this concept natively, the driver should do it. For example, PouchDB and CouchDB are document databases, but right now these don't support the collection concept. But its Elisa drivers do it.

    To get a collection object, we must use the following methods:

    //sync connection
    db.getCollection(ns : string, coll : string, opts ?: object) : Collection
    db.getCollection(qn : string, opts ?: object) : Collection
    db.findCollection(ns : string, coll : string, opts ?: object) : Collection
    db.findCollection(qn : string, opts ?: object) : Collection
    
    ns.getCollection(name : string, opts ?: object) : Collection
    ns.findCollection(name : string, opts ?: object) : Collection
    
    //async connection
    db.getCollection(ns : string, coll : string, opts ?: object) : Collection
    db.getCollection(qn : string, opts ?: object) : Collection
    db.findCollection(ns : string, coll : string, opts ?: object, callback : function(error, coll))
    db.findCollection(qn : string, opts ?: object, callback : function(error, coll))
    
    ns.getCollection(name : string, opts ?: object) : Collection
    ns.findCollection(name : string, opts ?: object, callback : function(error, coll))
    

    The getCollection() method doesn't check whether the collection exists. findCollection() does it.

    Examples:

    //sync connection
    var emp = db.getCollection("hr.Employee");
    var emp = db.findCollection("hr.Employee");
    
    //async connection
    var emp = db.getCollection("hr.Employee");
    db.findCollection("hr.Employee", function(error, emp) {
      ...
    });
    

    To know if a collection exists, we can use the hasCollection() method:

    //sync connection
    Database.hasCollection(ns : string, coll : string) : boolean
    Database.hasCollection(qn : string) : boolean
    Namespace.hasCollection(name : string) : boolean
    
    //async connection
    Database.hasCollection(ns : string, coll : string, callback : function(error, exists))
    Database.hasCollection(qn : string, callback : function(error, exists))
    Namespace.hasCollection(name : string, callback : function(error, exists))
    

    Inserting documents

    To insert a document, we use the insert() method:

    //sync connection
    insert(doc : object)
    insert(docs : object[])
    
    //async connection
    insert(doc : object, callback ?: function(error))
    insert(docs : object[], callback ?: function(error))
    

    The doc must be an object where its id property is the key. If the id isn't set, the driver must do it.

    Example:

    //sync connection
    bands.insert({name: "The National", year: 1999});
    
    //async connection
    bands.insert({name: "The National", year: 1999}, function(error) {
      ...
    });
    

    Finding documents

    To find documents, we can use:

    //sync connection
    findOne(filter : object) : object
    find(filter : object) : Result
    findAll() : Result
    
    //async connection
    findOne(filter : object, callback : function(error, doc))
    find(filter : object, callback : function(error, result))
    findAll(callback : function(error, result))
    

    Example:

    //sync connection
    var band = bands.findOne({name: "The National"});
    console.log(band.name, band.year);
    
    var res = bands.find({tags: {$contain: "indie"}});
    for (let band of res.docs) console.log(band.name, band.year);
    
    //async connection
    bands.findOne({name: "The National"}, function(error, doc) {
      console.log(doc.name, doc.year);
    });
    
    bands.find({tags: {$contain: "indie"}}, function(error, result) {
      if (!error) {
        for (let band of result.docs) console.log(band.name, band.year);
      }
    });
    

    If we need to know if a key exists, be able to use the Collection.hasId() method:

    //sync connection
    hasId("id") : boolean
    
    //async connection
    hasId("id", callback : function(error, exists))
    

    To get the number of items, we will use the Collection.count() method:

    //sync connection
    count() : number
    
    //async connection
    count(callback : function(error, count))
    

    Filter object

    The filter object sets a condition to restrict the documents to return. The operators are:

    • {field: value}. The field is equal to another.
    • {field: {$eq: value}}. The field is equal to another.
    • {field: {$ne: value}}. The field isn't equal to another. Alias: $neq.
    • {field: {$lt: value}}. The field is less than another.
    • {field: {$lte: value}}. The field is less than or equal to another. Alias: $le.
    • {field: {$gt: value}}. The field is greater than another.
    • {field: {$gte: value}}. The field is greater than or equal to another. Alias: $ge.
    • {field: {$between: [start, end]}}. The field value is between two.
    • {field: {$nbetween: [start, end]}}. The field value is not between two. Alias: $notBetween.
    • {field: {$like: value}}. The field matches a pattern.
    • {field: {$nlike: value}}. The field doesn't match a pattern. Alias: $notLike.
    • {field: {$contains: value}}. The field contains a value. Alias: $contain.
    • {field: {$ncontains: value}}. The field doesn't contain a value. Alias: $ncontains, $notContain and $notContains.
    • {field: {$in: value}}. The field is into an array value.
    • {field: {$nin: value}}. The field is not into an array value. Alias: $notIn.

    The format is very simple:

    field: {$operator: value}
    

    The same format is used in the filter operators and in the update operators. MongoDB doesn't so.

    Example:

    res = bands.find({year: {$between: [1990, 2000]}, tags: {$contain: "indie"}});
    

    Updating documents

    To update documents, we can use the update() method:

    //sync connection
    update(filter : object, update : object)
    
    //async connection
    update(filter : object, update : object, callback ?: function(error))
    

    Example:

    //sync connection
    bands.update({name: "The National"}, {year: 1999, origin: "Cincinnati, Ohio"});
    
    //async connection
    bands.update({name: "The National"}, {year: 1999, origin: "Cincinnati, Ohio"}, function(error) {
      ...
    });
    

    Saving documents

    Similar to Store.save().

    Removing documents

    To remove an item, we must use the remove() method:

    //sync connection
    remove(filter : object, opts ?: object)
    
    //async connection
    remove(filter : object, opts ?: object, callback ?: function(error))
    

    Example:

    //sync connection
    bands.remove({name: "The National"});
    
    //async connection
    bands.remove({name: "The National"}, function(error) {
      ...
    });
    

    To empty the collection, the truncate() method:

    //sync connection
    truncate(opts ?: object)
    
    //async connection
    truncate(opts ?: object, callback ?: function(error))
    

    Note. If we execute remove({}), nothing is removed. This is the way to prevent programming errors. To empty the collection, we must use the truncate() method.

    Collection query

    The Collection.q() method returns an object to allow to perform queries with projections, filters, sorts, etc.:

    //sync/async connection
    q() : CollectionQuery
    query() : CollectionQuery
    

    Example:

    q = bands.q();
    

    Projections

    We can restrict the fields to return with the project() method:

    //sync/async connection
    project(field : string) : CollectionQuery
    project(...fields : string[]) : CollectionQuery
    project(fields : object) : CollectionQuery
    

    Examples:

    q.project("name", "year");
    q.project({name: "nm", year: "yr"});  //name as nm and year as yr
    

    Filter

    To set the filter, we can use the filter() method:

    //sync/async connection
    filter(filter : object) : CollectionQuery
    

    Example:

    q.project("name").filter({year: 1990});
    

    Note. All the query methods return the same query. So we can chain several operations in the same expression.

    Limit

    To limit the number of documents returned by the query:

    //sync/async connection
    limit(count : number) : CollectionQuery
    

    Example:

    q.project("name").filter({year: 1990}).limit(10);
    

    Skip the first documents

    To skip the first documents:

    //sync/async connection
    offset(count : number) : Collection
    

    Example:

    q.project("name").filter({year: 1990}).offset(20).limit(10)
    

    Sort

    To sort the result, we can use the sort() method:

    //sync/async connection
    sort(field : string) : QueryCollection
    sort(...fields : string[]) : QueryCollection
    sort(fields : object) : QueryCollection
    

    Example:

    q.filter({year: 1990}).sort(year);
    q.filter({year: 1990}).sort({year: "ASC"});
    

    If an object is specified, the values can be ASC or DESC.

    Run

    To run the query, we can use the find() method or the run() method:

    //sync connection
    find(filter ?: object) : Result
    run() : Result
    
    //async connection
    find(filter ?: object, callback : function(error, result))
    run(function(error, result))
    

    The find() method allows to set the filter, while the run() doesn't.

    Example:

    //sync connection
    var res = q.project("name").find({tags: {$contain: "indie"}});
    for (let band of res.docs) console.log(band.id, band.name, band.year);
    
    //async connection
    q.project("name").find({tags: {$contain: "indie"}}, function(error, res) {
      if (!error) {
        for (let band of res.docs) console.log(band.id, band.name, band.year);
      }
    });
    

    Injections

    Elisa allows to inject fields/columns to the data store queries:

    • Stores into insert() and save(). Remember that the finds, the updates and the removes are always using the id field, for this reason, the injection not needed.
    • Collections into find(), findOne(), findAll(), insert(), save(), update(), remove() and truncate().

    The injection is indicated in getStore(), findStore(), getCollection() or findCollection(), using the inject option:

    inject: {
      prop1: value,
      prop2: value,
      ...
    }
    

    Example:

    var posts = db.getCollection("blog.posts", {
      inject: {
        authorId: session.userId
      }
    });
    

    When we query the collection, the authorId is set by the driver automatically. So, posts.find({}) is similar to posts.find({authorId: theValue}). Other example: posts.insert({text: "The text."}) is similar to posts.insert({authorId: theValue, text: "The text."}).

    Keywords

    none

    Install

    npm i elisa

    Homepage

    elisajs.org

    DownloadsWeekly Downloads

    2

    Version

    0.5.3

    License

    none

    Last publish

    Collaborators

    • elisajs