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

beastdb

v1.3.2

Published

A persistence database specializing in state space search problems!

Downloads

51

Readme

beastDB

A persistence database specializing in state space search problems!

Overview

BeastDB purpose is to help model, consult and persiste states in a eficience way, it does this by using leveldb as storage and introducing two immutable data types ISet and IMap.

ISet and IMap are implemented using immutable structural sharing, making copy and change operations very fast and memory eficient and strutural sharing representation is preserved on storage, making it very fast to load and save states.

This is the main diference bettwen BeastDB and other databases, but its not the only one:

  • Create Database
  • Tables, key and indexes
  • Records
  • Types
  • Query

Install

    npm install beastdb --save

Use

Just require the beastdb module.

    const {DB} = require("beastdb");

Create Database

To create a database just pass the storage option with a path.

  const db = await DB.open({storage: {path: './tttdb'}});

Tables, key and indexes

Tables are created on the fly like this:

  const t = db.tables.tictactoe;

Key and indexes are optional, but they need to created on start.

  const t = await db.tables.tictactoe
    .key('stateID', ['game', 'turn', 'moves'])
    .index('state')
    .index('win', 'state')
    .index('game')
    .save();

After creating key and index we must call save to storage changes.

key

There is only one key per table, on a SQL database this would be a primary key. A key is unique and immutable, when creating a compound key all fields in the key are not allowed to change.

If a key is not provided then table would use id has primary key, and uuid is used as a key generator.

If a key is provided with no fields then key name is used as primary key and uuid is used as a key generator, ex.

    t.key('stateID')

Indexes

Indexes can have one or more fields, currently you can't make queries without them, ex.

  t.index('state')
  .index('win', 'state')

This allows us to query 'state' alone and 'win' and 'state' togheter.

findByIndex(searchObject)

It finds a record using a index or key, the index must be declared, if the search uses more then one field then there must exist an index with all the search fields,

ex. t.index('win', 'state'); db.tables.games.findByIndex({win: 'O', state: 'done');

You can make indexes and search for any field type, for example IMap:

const gameState = await db.iMap().chain
        .set(0, await db.iMap().chain.set(0, 'X').set(1, '#').set(2, 'O'))
        .set(1, await db.iMap().chain.set(0, '#').set(1, 'O').set(2, 'O'))
        .set(2, await db.iMap().chain.set(0, 'X').set(1, '#').set(2, 'X'))
;
    
for await (let state of t.findByIndex({game: gameState})) {
        const childs = await state.data.childs;

        for (let child of childs) {
            print(child);
        }
}

Records

To create and update records we use insert and update operations. There is no restriction to add fields on insert or update.

Insert

Insert takes two arguments the record and on conflict strategy:

    const record = await t.insert({
        moves: 0,
        state: 'expand',
        turn: '',
        childs: [],
        game: await db.iMap().chain
            .set(0, await db.iMap().chain.set(0, '#').set(1, '#').set(2, '#'))
            .set(1, await db.iMap().chain.set(0, '#').set(1, '#').set(2, '#'))
            .set(2, await db.iMap().chain.set(0, '#').set(1, '#').set(2, '#'))
    }, null);
  • null : ignore if alredy exists,
  • object: on conflict update with object data, ex:
    const record = await t.insert({
        moves: 0,
        state: 'expand',
        turn: '',
        childs: [],
        game: await db.iMap().chain
            .set(0, await db.iMap().chain.set(0, '#').set(1, '#').set(2, '#'))
            .set(1, await db.iMap().chain.set(0, '#').set(1, '#').set(2, '#'))
            .set(2, await db.iMap().chain.set(0, '#').set(1, '#').set(2, '#'))
    }, {state: 'existing'});
  • async function (oldData, newData) {t.update(...)} : a function to solve the conflict

Update

After a record is created it can be updated like this:

    await record.update({
       state: 'done', myNewField: 'do it'
    });

Updates can't contain any field of primary key.

Types

Records only suport this types of values,

Record

A record can have other records as value, this can be from the same table or from another table, ex:

    await record.update({
       parent: await db.tables.myOtherTable.insert({'doit': 'now'})
    });

It can even be recursive:

    await record.update({
       self: record
    });

Date, Map, Set

Javascript Date, Map and Set is accepted.

Map and Set can have any value of the types described in this section, ex.

    await record.insert({
       map: new Set([new Date(), record, 1, 'string', ...])
    });

JSON

Any json value like number, string, object, array ...

Objects and Arrays can be nested and can have any type of value described on this section, ex:

    await record.insert({
       o: {
          date: new Date(),
          records: [record1, record2],
          mySet: new Set([1, 2, 3])
       }    
    });

IMap and ISet

IMap and ISet are immutable so every write operation will return a new IMap or ISet.

IMap

Since IMap is created on every write operation we can start with an empty imap and then add new labels like this:

    const empty = await db.iMap();
    let s1 = await empty.set('label1', value);
    s1 = await s1.set('label2', value);    

After completing our imap we can save it to a record, this will only save the last IMap and discard all intermidiate states.

    await record.insert({
       mymap: s1    
    });

chain

The IMap chain fields allows to chain async methods like this:
    await db.iMap().chain
        .set(0, await db.iMap().chain.set(0, '#').set(1, '#').set(2, '#'))
        .set(1, await db.iMap().chain.set(0, '#').set(1, '#').set(2, '#'))
        .set(2, await db.iMap().chain.set(0, '#').set(1, '#').set(2, '#'))
Withoud the chain field the sets would have to be used sequentialy, like this.
    let a = await db.iMap().set(0, 'example 0');
    a = await a.set(1, 'example 1');
    a = await a.set(2, 'example 2');

async set(key, value)

It sets a key value pair to the iMap
    let a = await db.iMap().set(0, 'example 0');
    a = await a.set(1, 'example 1');
    a = await a.set(2, 'example 2');

Notes:

  • All keys on IMap are strings, all non string keys will be converted to string
  • Values can be any type described on the #Types sections.

async get(key)

get(key) return the value of the given key

async has(key)

has(key) checks if IMap has the given key.

async remove(key)

It removes a key from a IMap
    let a = await db.iMap().set(0, 'example 0');
    a = await a.set(1, 'example 1');
    a = await a.set(2, 'example 2');

    a.remove(1);

async iterators

It creates a iterator to iteratate [key, value] from IMap.
    const r = [];
    for await (let [key, value] of myIMap) {
        r.push([key, value]);
    }

    return r;

async *values()

creates a new iterator to iterate all map values.
    const r = [];
    for await (let value of myIMap.values()) {
        r.push(value);
    }

    return r;

async *keys()

creates a new iterator to iterate all map keys.
    const r = [];
    for await (let key of myIMap.keys()) {
        r.push(key);
    }

    return r;

async keysToArray()

It returns an array with all map keys.

async toArray()

It returns a json representation of IMap, the format is the same as norml JS Map.

example: [['keyA', 'valueA'], ['keyB', 'valueB'], ['keyC', 'valueC']]

ISet

Since ISet is created on every write operation we can start with an empty iset and then add new values like this:

    const empty = await db.iSet();
    let s1 = await empty.add(value1);
    s1 = await s1.add(value2);    

After completing our iset we can save it to a record, this will only save the last ISet and discard all intermidiate states.

    await record.insert({
       myset: s1
    });

chain

The ISet chain fields allows to chain async methods like this:
    await db.iSet().chain.add(1).add(2).add(3)
Withoud the chain field the adds would have to be used sequentialy, like this.
    let a = await db.iSet().add(1);
    a = await a.add(2);
    a = await a.add(3);

async add(value)

Adds a value to ISet

Notes: * Values can be any type described on the #Types sections.

async remove(value)

Removes a value from the ISet.

async iterators

It creates a iterator to iteratate [key, value] from ISet.
    const r = [];
    for await (let [key, value] of myISet) {
        r.push([key, value]);
    }

    return r;

In this case the keys would be 0, 1, ...

async *values()

creates a new iterator to iterate all ISet values.
    const r = [];
    for await (let value of myISet.values()) {
        r.push(value);
    }

    return r;

async toArray()

It returns an array with all ISet values,

example: [1, 2, 3, 4]

IArray

Since IArray is created on every write operation we can start with an empty iarray and then add new values like this:

    const empty = await db.iArray();
    let s1 = await empty.push(value1);
    s1 = await s1.push(value2);    

After completing our iarray we can save it to a record, this will only save the last IArray and discard all intermidiate states.

    await record.insert({
       myarray: s1
    });

chain

The IArray chain fields allows to chain async methods like this:
    await db.iArray().chain.push(1).push(2).push(3)
Withoud the chain field the adds would have to be used sequentialy, like this.
    let a = await db.iArray().push(1);
    a = await a.push(2);
    a = await a.push(3);

async push(value)

Pushes a value to the last position of the iarray and returns a new iarray.

    let a = await db.iArray().push(1);
    a = await a.push(2);
    a = await a.push(3);

async pop()

Pops the last value of array, it returns the new iarray and value.

    let [newIArray, popedValue] = await db.iArray().chain.push(1).pop();

async setIndex(index, value)

length

The size of the array

  const size = iarray.length;

setIndex(index, value)

Sets the index value to value, index must be inside of interval [0, this.size]

  let newIarray = await iarray.chain.push(1).push(2).push(3).setIndex(1, 2);

async toArray()

It returns an array with all IArray values in inserted order,

example: [1, 2, 3, 4]

Example

  • Tic-Tac-Toe: https://github.com/fsvieira/beastdb/tree/main/examples/tic-tac-toe

The Name

The name beast derives from brute-force and was chosen as a synonym of brute.