Welcome to Trin.DB!
A fast RESTful persistent or in memory NoSQL database (18 KiB only!)
Github Repo: https://github.com/trinly01/TrinDB
Installation
npm install trin.db
or
yarn add trin.db
Usage
const express = require('express')
const app = express()
const port = process.env.PORT || 3000
const trinDB = require('trin.db')
app.use(express.json()) // required for RESTful APIs
app.listen(port, async () => {
app.trinDB = {
todos: await trinDB({ // Todos Service
filename: 'trinDb/todos.db', // get records from a file
inMemoryOnly: false, // Optional
restful // Optional
})
}
})
// Other Options
const restful = { // Optional
app, // express app
url: '/todos', // API end-point
hooks // Optional
}
const hooks = ({app, service}) => ({ // Hooks Example
before: {
all: [
(req, res, next) => {
console.log('before all hook')
next()
}
],
get: [],
find: [],
create: [],
patch: [],
remove: []
}
after: {
all: [
(result) => {
console.log(result)
return result
}
],
}
})
await trinDB(<object>
)
Returns a trinDB Service
Object Prop | Type | Description | Default | Mode |
---|---|---|---|---|
filename | <string> |
Path to file. Required if in persistent mode | n/a | persistent |
inMemoryOnly | <boolean> |
( Optional ) If true , database will be in non-persistent mode |
false |
in-memory |
restful | <object> |
( Optional ) { app, url, hooks }
|
n/a | persistent |
service.create(<object>
)
Returns the created <object>
/* example existing records (service.data)
{
asd: { _id: 'asd', text: 'Hello', read: true, nested: { prop: 'xander' } },
zxc: { _id: 'zxc', text: 'World', read: false, nested: { prop: 'ford' } }
}
*/
const result = service.create({
text: 'Trinmar Pogi'
})
console.log(result)
// { _id: 'qwe', text: 'Trinmar Pogi' }
console.log(service.data)
/* service.data with the newly created object
{
asd: { _id: 'asd', text: 'Hello', read: true, nested: { prop: 'xander' } },
zxc: { _id: 'zxc', text: 'World', read: false, nested: { prop: 'ford' } },
qwe: { _id: 'qwe', text: 'Trinmar Pogi' }
}
*/
RESTful API
curl --location --request POST 'http://localhost:3000/todos' \
--header 'Content-Type: application/json' \
--data-raw '{
"text": "Trinmar Pogi"
}'
service.find(<object>
)
Returns found data <object>
/* example existing records (service.data)
{
asd: { _id: 'asd', firstName: 'Trinmar', lastName: 'Pogi', age: 20 },
zxc: { _id: 'zxc', firstName: 'Trinly Zion', lastName: 'Boado', age: 1 },
qwe: { _id: 'qwe', firstName: 'Lovely', lastName: 'Boado', age: 18 }
}
*/
// Equality
result = service.find({
query: {
lastName: 'Pogi' // equality
},
limit: 10, // default 10
skip: 0 // default 0
})
console.log(result)
/*
{
total: 1,
limit: 10,
skip: 0,
data: {
asd: { _id: 'asd', firstName: 'Trinmar', lastName: 'Pogi', age: 20 }
}
}
*/
RESTful API
curl --location --request GET 'http://localhost:3000/todos?lastName=Pogi&$limit=10&$skip=0'
Complex Query (conditional >, >==, <, <==, &&, || )
// Map data or select specific props
result = service.find({
query (obj) {
return ob.age < 20
},
map (obj) {
return {
fullName: obj.firstName + ' '+ obj.lastName
}
}
})
console.log(result)
/*
{
total: 2,
limit: 10,
skip: 0,
data: {
zxc: { _id: 'zxc', firstName: 'Trinly Zion Boado' },
qwe: { _id: 'qwe', firstName: 'Lovely Boado' }
}
}
*/
service.search(keywords
)
fuzzy search finds data based on the keywords (<String>
) and returns it sorted by _score
/* example existing records (service.data)
{
asd: { _id: 'asd', firstName: 'Trinmar', lastName: 'Boado' },
zxc: { _id: 'zxc', firstName: 'Trinly Zion', lastName: 'Boado' },
qwe: { _id: 'qwe', firstName: 'Lovely', lastName: 'Boado' }
}
*/
result = service.search('ly oad')
console.log(result)
/*
{
total: 3,
data: {
qwe: { _score: 2, _id: 'qwe', firstName: 'Lovely', lastName: 'Boado', age: 18 },
zxc: { _score: 2, _id: 'zxc', firstName: 'Trinly Zion', lastName: 'Boado', age: 1 },
asd: { _score: 1, _id: 'asd', firstName: 'Trinmar', lastName: 'Pogi', age: 20 },
}
}
*/
RESTful API
curl --location --request GET 'http://localhost:3000/todos?$search=ly%20oad'
service.patch(_id, <object>
)
Returns the created <object>
// { _id: 'q12m3k', firstName: 'Trinmar', lastName: 'Boado' nested: { counter: 123 } }
const result = service.patch('q12m3k', {
lastName: 'Pogi',
children: ['Trinly Zion'],
'nested.counter': 456
})
console.log(result)
// { _id: 'q12m3k', lastName: 'Pogi' children: ['Trinly Zion'], 'nested.counter': 456 }
console.log(service.data['q12m3k'])
// { _id: 'q12m3k', firstName: 'Trinmar', lastName: 'Pogi', nested: { prop: 456 }, children: ['Trinly Zion'] }
RESTful API
curl --location --request PATCH 'http://localhost:3000/todos/:_id' \
--header 'Content-Type: application/json' \
--data-raw '{
"lastName": "Pogi",
"children": ["Trinly Zion"],
"nested.counter": 456
}'
service.remove(_id)
Returns the removed <object>
service.remove('q12m3k')
console.log(service.data['q12m3k'])
// undefined
RESTful API
curl --location --request DELETE 'http://localhost:3000/todos/:_id'
service.removeProps(_id, <object>
)
Returns the removed <object>
props
// { _id: 'q12m3k', firstName: 'Trinmar', lastName: 'Pogi', nested: { prop: 456 }, children: ['Trinly Zion'] }
service.removeProps('q12m3k', {
lastName: true,
'nested.prop': true
firstName: false
})
console.log(service.data['q12m3k'])
// { _id: 'q12m3k', firstName: 'Trinmar', children: ['Trinly Zion'] }
RESTful API
curl --location --request PATCH 'http://localhost:3000/todos/:_id' \
--header 'Content-Type: application/json' \
--data-raw '{
"$action": "removeProps"
"lastName": true,
"nested.prop": true,
"firstName": false
}'
service.inc(_id, <object>
)
Increments specific props and returns the <object>
// { _id: 'q12m3k', firstName: 'Trinmar', lastName: 'Pogi', nested: { prop: 456 }, children: ['Trinly Zion'] }
service.inc('q12m3k', {
'nested.prop': 5
})
console.log(service.data['q12m3k'])
// { _id: 'q12m3k', firstName: 'Trinmar', lastName: 'Pogi', nested: { prop: 461 }, children: ['Trinly Zion'] }
RESTful API
curl --location --request PATCH 'http://localhost:3000/todos/:_id' \
--header 'Content-Type: application/json' \
--data-raw '{
"$action": "inc"
"nested.prop": 5
}'
service.splice(_id, <object>
)
removes element by index and returns the <object>
// { _id: 'q12m3k', children: ['Trinly Zion', 'Trinmar Boado'] }
service.splice('q12m3k', {
'children': 1
})
console.log(service.data['q12m3k'])
// { _id: 'q12m3k', children: ['Trinly Zion'] }
RESTful API
curl --location --request PATCH 'http://localhost:3000/todos/:_id' \
--header 'Content-Type: application/json' \
--data-raw '{
"$action": "splice"
"children": 1
}'
service.push(_id, <object>
)
adds one or more elements to the end of an array and returns the <object>
// { _id: 'q12m3k', children: ['Trinly Zion', 'Trinmar Boado'] }
service.push('q12m3k', {
'children': 'Lovely Boado'
})
console.log(service.data['q12m3k'])
// { _id: 'q12m3k', children: ['Trinly Zion', 'Trinmar Boado', 'Lovely Boado'] }
RESTful API
curl --location --request PATCH 'http://localhost:3000/todos/:_id' \
--header 'Content-Type: application/json' \
--data-raw '{
"$action": "push"
"children": "Lovely Boado'"
}'
service.unshift(_id, <object>
)
adds one or more elements to the beginning of an array
and returns the <object>
// { _id: 'q12m3k', children: ['Trinly Zion', 'Trinmar Boado'] }
service.unshift('q12m3k', {
'children': 'Lovely Boado'
})
console.log(service.data['q12m3k'])
// { _id: 'q12m3k', children: ['Lovely Boado', 'Trinly Zion', 'Trinmar Boado'] }
RESTful API
curl --location --request PATCH 'http://localhost:3000/todos/:_id' \
--header 'Content-Type: application/json' \
--data-raw '{
"$action": "unshift"
"children": "Lovely Boado'"
}'
service.sort(data,<object>
)
Sorts the data based on the <object>
and returns the sorted data
/* example existing records (service.data)
{
asd: { _id: 'asd', firstName: 'Trinmar', lastName: 'Pogi', age: 20 },
zxc: { _id: 'zxc', firstName: 'Trinly Zion', lastName: 'Boado', age: 1 },
qwe: { _id: 'qwe', firstName: 'Lovely', lastName: 'Boado', age: 18 }
}
*/
// Descending (-1)
result = service.sort({
data: service.data, // (Optional) if not defined, service.data will be used
params: {
age: -1
}
})
console.log(result)
/*
{
asd: { _id: 'asd', firstName: 'Trinmar', lastName: 'Pogi', age: 20 },
qwe: { _id: 'qwe', firstName: 'Lovely', lastName: 'Boado', age: 18 },
zxc: { _id: 'zxc', firstName: 'Trinly Zion', lastName: 'Boado', age: 1 }
}
*/
service.copmact(filename, <object>
)
writes the compact data to a file
| param | Type | Description | Default |
|--|--|--|--|
| filename | <string>
| (Optional) Path to file | current |
| | <object>
| ( Optional ) a TrinDB object | service.data
|
service.copmact('test.db', service.data)
Github Repo: https://github.com/trinly01/TrinDB
Top comments (12)
Would love if you showed some benchmarks instead of copy-pasting the whole API here.
Hi Atta,
Nice suggestion!
You may contribute to this project by adding a benchmark.
Will love to merge it ❤️
No it's your job to provide benchmarks. :(
To be honest, I get your point, but I don't like this wording when it comes to open source projects :)
Projects are projects. You can't expect other people to provide benchmarks for your project. You are the one advertising it, so you need to provide benchmarks. So yes, it is your job. Open source is not an excuse.
npmjs.com/package/msgpack
node-msgpack is currently slower than the built-in JSON.stringify() and JSON.parse() methods. In recent versions of node.js, the JSON functions have been heavily optimized. node-msgpack is still more compact, and we are currently working performance improvements. Testing shows that, over 500k iterations, msgpack.pack() is about 5x slower than JSON.stringify(), and msgpack.unpack() is about 3.5x slower than JSON.parse().
Looks very interesting! How is the performance when you have 100.000 - 1.000.000 records?
Hi Roelof, Contributors are welcome.
Need help to test it using real world scenarios.
Currently I am interested in JSON schema validation, perhaps with Ajv.
Thanks for the suggestion. <3
I already converted it from appendFileSync to fileWriteStream
All suggestions are being considered. Some are already implemented. Thank you to all of insights and inputs ❤️
Thanks for the detailed explanation ❤️
My goal is to be pure JS implementation with no binary dependencies.
I think streams are optimized to minimize CPU and memory overheads