Security News
Input Validation Vulnerabilities Dominate MITRE's 2024 CWE Top 25 List
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
A persistence database specializing in state space search problems!
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:
npm install beastdb --save
Just require the beastdb module.
const {DB} = require("beastdb");
To create a database just pass the storage option with a path.
const db = await DB.open({storage: {path: './tttdb'}});
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.
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 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.
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);
}
}
To create and update records we use insert and update operations. There is no restriction to add fields on insert or update.
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);
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'});
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.
Records only suport this types of values,
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
});
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', ...])
});
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 are immutable so every write operation will return a new IMap or ISet.
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
});
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');
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:
get(key) return the value of the given key
has(key) checks if IMap has the given 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);
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;
creates a new iterator to iterate all map values.
const r = [];
for await (let value of myIMap.values()) {
r.push(value);
}
return r;
creates a new iterator to iterate all map keys.
const r = [];
for await (let key of myIMap.keys()) {
r.push(key);
}
return r;
It returns an array with all map keys.
It returns a json representation of IMap, the format is the same as norml JS Map.
example: [['keyA', 'valueA'], ['keyB', 'valueB'], ['keyC', 'valueC']]
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
});
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);
Adds a value to ISet
Notes: * Values can be any type described on the #Types sections.
Removes a value from the ISet.
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, ...
creates a new iterator to iterate all ISet values.
const r = [];
for await (let value of myISet.values()) {
r.push(value);
}
return r;
It returns an array with all ISet values,
example: [1, 2, 3, 4]
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
});
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);
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);
Pops the last value of array, it returns the new iarray and value.
let [newIArray, popedValue] = await db.iArray().chain.push(1).pop();
The size of the array
const size = iarray.length;
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);
It returns an array with all IArray values in inserted order,
example: [1, 2, 3, 4]
The name beast derives from brute-force and was chosen as a synonym of brute.
FAQs
A persistence database specializing in state space search problems!
We found that beastdb demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.
Research
Security News
A threat actor's playbook for exploiting the npm ecosystem was exposed on the dark web, detailing how to build a blockchain-powered botnet.