nosqlite3

0.2.0 • Public • Published

NoSQLite3

A single file NoSQL database for Node.js, utilizing the Java library NoSQLite.

It's a lightweight embedded document database, ideal for small web applications.

It features:

  • Embedded key-value object store
  • Single file store
  • Very fast and lightweight MongoDB like API
  • Full text search capability
  • Observable store
const { collection } = require('nosqlite3');

let john = {
  name: 'John Doe',
  age: 32
};

// save john to the users-collection
await collection("users").save(john); 

// get all users
let users = await collection("users").find();  

Requirements

Requires at least Java version 8 at "JAVA_HOME" path.

If you don't have a Java Runtime Environment you can download it here.

Table of content

Installation

npm install nosqlite3

Getting started

Collections is used with the collection()-method to manipulate the database. collection() takes a collection-name as parameter. Each collection stores data separately. If collection() is used without a name, it defaults to a 'default_coll' collection. All calls to the database are asynchronous, so you need to await the response.

First call to collection() will initiate a database connection. The first time it will create a database-file in your project. Easy to deploy or share.

Examples

const { collection, Filter } = require('nosqlite3');

let john = new users("John");

// generates an UUID like: "lic4XCz2kxSOn4vr0D8BV"
await collection("users").save(john);    

let jane = await collection("users").findById("lic4XCz2kxSOn4vr0D8BV");

jane.setAge(30);

// updates document with same UUID
await collection("users").save(jane); 

// delete Jane
await collection("users").deleteById("lic4XCz2kxSOn4vr0D8BV"); 

// get all users
let users = await collection("users").find(); 

// get all users named 'John'
let usersNamedJohn = await collection("users").find(Filter.eq("name", "John"));

// or with the statement syntax
let usersNamedJohn = await collection("users").find("name==John"); 

Document

Collections can be used as a simple key/value store, but it's true potential is when using it with document objects. When using objects the property _id is used to identify the object in the database.

If there's no _id when saving a document, it gets created with a generated UUID.

If it's present, the document with matching id in the collection gets updated.

const { collection } = require('nosqlite3');

let user = {
  name: 'John',
  age: 32
}

// save without _id
await collection("users").save(user);

// user now has the property '_id'
user._id  // 'lic4XCz2kxSOn4vr0D8BV',

Observe collection

You can register a callback watcher to a collection. The watcher listens on changes to that collection, and automatically triggers provided handler.

Watch a collection on changes:

// watchData has 3 fields. 
// model - is the document class that was triggered 
// event - is the event triggered - 'insert', 'update' or 'delete'
// data - is a list with effected documents
collection("users").watch(watchData => {
    let effectedUsers = watchData.data;

    switch(watchData.event) {
        case "insert": // on created document
        break;

        case "update": // on updated document
        break;

        case "delete": // on deleted document
        break;
    }
});

Watch a collection on changes on a specific event:

collection("users").watch("insert", watchData => {
    let effectedUsers = watchData.data;
    // do logic with inserted documents
});

collection("users").watch("update", watchData => {
    let effectedUsers = watchData.data;
    // do logic with updated documents
});

collection("users").watch("delete", watchData => {
    let effectedUsers = watchData.data;
    // do logic with deleted documents
});

Collection methods

To use the collection you need to add which document to query for in the collection parameter, ex collection("users") will only query for Users. Data is stored in the collection as JSON, and the find()-methods will by default parse this JSON.

Setting a collection to parse: false is MUCH MUCH faster, because no parsing is required. This is good when only sending data from a collection directly over the network.

Table 1. Collection methods

Operation Method Description
Get all documents find(Filter) Returns a list with objects. If no filter is used find() will return ALL documents.
Get one document findOne(Filter) Returns first found document.
Get document with id findById(id) Returns the object with matching id.
Create or Update a document save(Object) Creates a new document in the collection if no id is present. If theres an id save() will update the existing document in the collection. Can save an array of documents.
Update documents updateField(fieldName, newValue) Update all documents fields with new value.
Update a document field with Object updateField(Object, fieldName, newValue) Updates the document field with matching id.
Update a document field with id updateFieldById(id, fieldName, newValue) Updates the document field with matching id.
Update documents changeFieldName(newFieldName, oldFieldName) Change field name on all documents.
Update documents removeField(fieldName) Removes field from all documents.
Delete a document delete(Document) Deletes the document with matching id.
Delete documents delete(Filter) Deletes all documents matching the filter.
Delete a document with id deleteById(id) Deletes the document with matching id.
Get number of documents count() Returns the count of all documents in a collection.
Watch a collection watch(callback) Register a watcher that triggers on changes in the collection.
Watch a collection on an event watch(event, callback) Register a watcher that triggers on changes at target event in the collection.

Table 1.2. Collection as a key/value store methods

When using the collection as a key/value store you can save any data you want.

Note: you can't filter with find() when storing raw data.

await collection("randomData").put("snuggles", { name: "Snuggles" });
await collection("randomData").put("1+2", 3);

let snuggles = await collection("randomData").get("snuggles");
Operation Method Description
Get value by key get(key) Returns an object as JSON.
Store object at key put(key, value) Stores the value as JSON at target key. Replaces value if key exists.
Store object at key putIfAbsent(key, value) Stores the value as JSON at target key. Does not replace value if key exists.
Remove value by key remove(key) Removes both key and value.

List all collections

The method collectionNames() returns a list of all different collections currently stored.

const { collectionNames } = require('nosqlite3');

let collections = collectionNames();

Filters

Filter are the selectors in the collection’s find operation. It matches documents in the collection depending on the criteria provided and returns a list of objects.

const { Filter } = require('nosqlite3');

Filter.eq('field', 'value');

// or with deconstruct
const { eq } = Filter;

eq('field', 'value');

Table 2. Comparison Filter

Filter Operator Method Description
Equals == eq(field, value) Matches values that are equal to a specified value.
NotEquals != ne(field, value) Matches values that are not equal to a specified value.
Greater > gt(field, value) Matches values that are greater than a specified value.
GreaterEquals >= gte(field, value) Matches values that are greater than or equal to a specified value.
Lesser < lt(field, value) Matches values that are less than a specified value.
LesserEquals <= lte(field, value) Matches values that are less than or equal to a specified value.
In ==[1, 2] in(field, values[]) Matches any of the values specified in an array.

Table 3. Logical Filters

Filter Operator Method Description
Not "!statement" not(Filter) Inverts the effect of a filter and returns results that do not match the filter.
Or "statement1 statement2"
And "statement1 && statement2" and(Filter...) Joins filters with a logical AND returns all ids of the documents that match the conditions of both filters.

Table 4. Text Filters

Filter Operator Method Description
Text =~ text(field, value) Performs full-text search. Same syntax as SQL LIKE
Regex ~~ regex(field, value) Selects documents where values match a specified regular expression.

FindOptions

A FindOptions is used to specify search options. It provides pagination as well as sorting mechanism. The config syntax with an object is more clear and easier to read.

Example

// find(filter, sortBy, limit, offset)

// sorts all documents by age in ascending order then take first 10 documents and return as a List
let users = await collection("users").find(null, "age=asc", 10, 0);

// or with FindOptions
let users = await collection("users").find({
  sortBy: "age=asc",
  limit: 10
});
// sorts the documents by age in descending order
let users = await collection("users").find(null, "age=desc", 0, 0);

// or with FindOptions
let users = await collection("users").find({
  filter: "age=desc"
});
// fetch 10 documents starting from offset = 2
let users = await collection("users").find(10, 2);

// or with FindOptions
let users = await collection("users").find({
  limit: 10,
  offset: 2
});

Collection Examples

These examples uses the deconstructed Filter syntax

const { collection, Filter } = require('nosqlite3');

// with deconstruction of Filter methods
const { eq, ne, gt, gte, lt, lte, not, and, or, text, regex } = Filter;

// 'in' is a reserved keywords, and cannot be deconstructed
Filter.in(); // filter from a list

and()

// matches all documents where 'age' field has value as 30 and
// 'name' field has value as John Doe
await collection("users").find(and(eq("age", 30), eq("name", "John Doe")));
// with the statement syntax
await collection("users").find("age==30 && name==John Doe");

or()

// matches all documents where 'age' field has value as 30 or
// 'name' field has value as John Doe
await collection("users").find(or(eq("age", 30), eq("name", "John Doe")));
// with the statement syntax
await collection("users").find("age==30 || name==John Doe");

not()

// matches all documents where 'age' field has value not equals to 30
// and name is not John Doe
await collection("users").find(not(and((eq("age", 30), eq("name", "John Doe"))));
// with the statement syntax
await collection("users").find("!(age==30 && name==John Doe)");

eq()

// matches all documents where 'age' field has value as 30
await collection("users").find(eq("age", 30));
// with the statement syntax
await collection("users").find("age==30");

ne()

// matches all documents where 'age' field has value not equals to 30
await collection("users").find(ne("age", 30));
// with the statement syntax
await collection("users").find("age!=30");

gt()

// matches all documents where 'age' field has value greater than 30
await collection("users").find(gt("age", 30));
// with the statement syntax
await collection("users").find("age>30");

gte()

// matches all documents where 'age' field has value greater than or equal to 30
await collection("users").find(gte("age", 30));
// with the statement syntax
await collection("users").find("age>=30");

lt()

// matches all documents where 'age' field has value less than 30
await collection("users").find(lt("age", 30));
// with the statement syntax
await collection("users").find("age<30");

lte()

// matches all documents where 'age' field has value lesser than or equal to 30
await collection("users").find(lte("age", 30));
// with the statement syntax
await collection("users").find("age<=30");

in()

// matches all documents where 'age' field has value in [20, 30, 40]
await collection("users").find(Filter.in("age", 20, 30, 40));

List ages = List.of(20, 30, 40);
await collection("users").find(Filter.in("age", ages));

// with the statement syntax
await collection("users").find("age==[20, 30, 40]");

text() Same syntax as SQL LIKE

  • The percent sign (%) represents zero, one, or multiple characters
  • The underscore sign (_) represents one, single character
// matches all documents where 'address' field start with "a"
await collection("users").find(text("address", "a%"));

// with the statement syntax, applies to all text() examples
await collection("users").find("address=~a%");

// matches all documents where 'address' field end with "a"
await collection("users").find(text("address", "%a"));

// matches all documents where 'address' field have "or" in any position
await collection("users").find(text("address", "%or%"));

// matches all documents where 'address' field have "r" in the second position
await collection("users").find(text("address", "_r%"));

// matches all documents where 'address' field start with "a" and are at least 2 characters in length
await collection("users").find(text("address", "a_%"));

// matches all documents where 'address' field start with "a" and are at least 3 characters in length
await collection("users").find(text("address", "'a__%"));

// matches all documents where 'address' field start with "a" and ends with "o"
await collection("users").find(text("address", "a%o"));

regex() Pass regex as a string.

// matches all documents where 'name' value starts with 'jim' or 'joe'.
await collection("users").find(regex("name", "^(jim|joe).*"));
// with the statement syntax
await collection("users").find("name~~^(jim|joe).*");

Filter nested objects

It's just as easy to filter nested objects in a collection. Each nested property is accessible with a dot-filter for each level.

// matches all documents where a User's cat has an age of 7
await collection("users").find(eq("cat.age", 7));
// with the statement syntax
await collection("users").find("cat.age==7");

// matches all documents where a User's headphone has a brand of Bose
await collection("users").find(eq("accessory.headphones.brand", "Bose"));
// with the statement syntax
await collection("users").find("accessory.headphones.brand==Bose");

CollectionConfig

CollectionConfig can be passed when enabling collections to set certain options. Options available are:

  • dbPath - The default path is "db/data.db". You can override that with this option.
  • parse - IF true, automatically parse all JSON-data from the database.

Note: options must be called before any other call with collection()!

You can pass one or multiple options when enabling collections:

// default options 
let config = {
  dbPath: 'db/data.db',
  parse: true
};

collection(config);

Package Sidebar

Install

npm i nosqlite3

Weekly Downloads

2

Version

0.2.0

License

MIT

Unpacked Size

10.1 MB

Total Files

9

Last publish

Collaborators

  • aarkan1