npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

nosqlite3

v0.2.0

Published

A lightweight embedded document database, ideal for small web applications.

Downloads

3

Readme

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" | or(Filter...) | Joins filters with a logical OR returns all ids of the documents that match the conditions of either filter. | | 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);