minivium
v0.3.1
Published
Minimalistic JSON database.
Maintainers
Readme
minivium
Minimalistic JSON database.

Features
- Minimalistic
- Atomic Writes
- JSON Database
- Query Language
- Schema and Schemaless
- Sync and Async operations
Table of Contents
- Getting Started
- Collection
- Create schema registry and data directory
- Minivium reference
- Initialise the collections
- Drop collection
- Drop all collections
- Query
- Attributes
- Alias for attributes
- Operators
- Order By
- Limit
- Offset
- Limit and Offset
- Limitations
- Contribution
- License
- Donate
Getting started
Data is saved in JSON format in collections. A collection is like a table in relational database. A collection is an array of objects. Each object represents a row.
Minivium comes with a simple query language inspired by Sequalize and Mango Query.
| Relational Database | Minivium | |---------------------|----------------------| | Table | Collection | | Row | Row, Tuple, Document |
Install the package
npm i miniviumImport
// ESM
import { minivium, SchemaRegistry } from "minivium";
// CommonJs
const { minivium, SchemaRegistry } = require("minivium");Collection
A collection consists of a name and as set of columns.
| Attribute | Purpose |
|-----------|--------------------------------------------------------------|
| name | Name of the collection |
| columns | Array of columns. Set it to empty array [] for schemaless. |
Attributes for columns.
| Attribute | Purpose |
|------------|---------------------------------------------------------------------------------------|
| name | This is the name of the column |
| isRequired | Set this to true if you want the column to have a value. Default is false. |
| isUnique | Set this to true if you want the column to have a unique value. Default is false. |
Create schema registry and data directory.
We register our collections via SchemaRegistry.
The dataDir is the path to the data directory.
Minivium will save the collection files in this directory.
const dataDir = '/path/to/data';
const schemaRegistry = new SchemaRegistry({
collections: [
{
name: 'users',
columns: [
{ name: 'id', isUnique: true },
{ name: 'username', isRequired: true, isUnique: true },
{ name: 'email', isRequired: true, isUnique: true },
{ name: 'score', isRequired: true },
{ name: 'phoneNumber' },
{ name: 'status', isRequired: true },
{ name: 'createdAt', isRequired: true },
{ name: 'updatedAt' },
]
}
]
});Minivium reference
const db = minivium({ dataDir, schemaRegistry });Initialise the collections
This will create collections mentioned in the schema registry. If a collection exists then it will be skipped.
Sync
db.init();Async
await db.initAsync();Drop collection
Sync
db.dropCollection('users');Async
await db.dropCollectionAsync('users');Drop all collections
Sync
db.dropAllCollections();Async
await db.dropAllCollectionsAsync();Query
Syntax query.type(collectionName, [data], [option])
| Particular | Purpose |
|------------------|---------------------------------------------------------------------|
| type | This represents the query type. Example insert query. |
| collectionName | This is the name of the collection we want the query to run on. |
| [data] | This represents the data to insert or update. |
| [option] | (Optional) This is the option for the query. |
Option
| Option | Purpose |
|----------------|--------------------------------------------------------------------------------------------------|
| [where] | This represents the where clause and it consists of the common operators. |
| [limit] | This helps in selecting the first N rows. Refer limit |
| [offset] | This helps in skipping M rows. Refer offset |
| [attributes] | This helps in selecting specific columns and also to give alias. Refer attributes |
Insert
Sync syntax insert(collectionName, dataToInsert)
Where, dataToInsert is an object consisting of column names
and their values.
const id = db.query.insert('users', {
username: 'yusufshakeel',
email: '[email protected]',
score: 10,
phoneNumber: '123',
status: 'active',
createdAt: '2024-12-26',
});Async syntax await insertAsync(collectionName, dataToInsert)
const id = await db.query.insertAsync('users', {
username: 'yusufshakeel',
email: '[email protected]',
score: 10,
phoneNumber: '123',
status: 'active',
createdAt: '2024-12-26',
});Note! id is generated by minivium if it is not provided.
Bulk Insert
Sync syntax bulkInsert(collectionName, dataToInsert[])
Where, dataToInsert[] is an array of objects consisting of column names
and their values.
const ids = db.query.bulkInsert('users', [
{
username: 'john',
email: '[email protected]',
score: 10,
status: 'active',
createdAt: '2024-12-27',
},
{
username: 'jane',
email: '[email protected]',
score: 10,
status: 'active',
createdAt: '2024-12-27',
}
]);Async syntax await bulkInsertAsync(collectionName, dataToInsert[])
const ids = await db.query.bulkInsertAsync('users', [
{
username: 'john',
email: '[email protected]',
score: 10,
status: 'active',
createdAt: '2024-12-27',
},
{
username: 'jane',
email: '[email protected]',
score: 10,
status: 'active',
createdAt: '2024-12-27',
}
]);Where ids is an array of ids.
Select
Sync syntax select(collectionName, [option])
Where, option consists of the where clause.
Refer Operators
const rows = db.query.select(
'users',
{ where: { id } }
);Async syntax await selectAsync(collectionName, [option])
const rows = await db.query.selectAsync(
'users',
{ where: { id } }
);If you want to select everything then skip the option with where clause.
Update
Sync syntax update(collectionName, dataToUpdate, [option])
const updatedRowCount = db.query.update(
'users',
{ phoneNumber: '1234', updatedAt: new Date().toISOString() },
{ where: { id } }
);Async syntax await updateAsync(collectionName, dataToUpdate, [option])
const updatedRowCount = await db.query.updateAsync(
'users',
{ phoneNumber: '1234', updatedAt: new Date().toISOString() },
{ where: { id } }
);If option with where clause is not provided then all the rows will be updated.
This behavior is similar to databases like PostgreSQL.
Delete
Sync syntax delete(collectionName, [option])
const deletedRowCount = db.query.delete(
'users',
{ where: { id } }
);Async syntax await deleteAsync(collectionName, [option])
const deletedRowCount = await db.query.deleteAsync(
'users',
{ where: { id } }
);If option with where clause is not provided then all the rows will be deleted.
This behavior is similar to databases like PostgreSQL.
Attributes
Set the attributes option to pick the desired columns.
db.query.select('users', { attributes: ['id', 'username'] });SQL equivalent
select id, username from users;Alias for attributes
Pass [columnName, aliasName] tuple in the attributes option to set an alias for a column.
db.query.select(
'users',
{
attributes: [
'id',
'username',
['score', 'totalScore']
]
}
);SQL equivalent
select
id,
username,
score as totalScore
from users;Operators
Minivium comes with a simple query language that is inspired by Sequalize and Mango Query.
// ESM
import { Op } from "minivium";
// CommonJs
const { Op } = require("minivium");Equal eq
db.query.select(
'users',
{
where: {
status: { [Op.eq]: 'active' }
}
}
);SQL equivalent
select * from users where status = 'active';Alternatively,
db.query.select(
'users',
{
where: {
status: 'active'
}
}
);Not equal notEq
db.query.select(
'users',
{
where: {
status: { [Op.notEq]: 'active' }
}
}
);SQL equivalent
select * from users where status != 'active';In in
db.query.select(
'users',
{
where: {
email: { [Op.in]: ['[email protected]'] }
}
}
);SQL equivalent
select * from users
where email in ['[email protected]'];The Op.in operator will work for column with string, string[], number, number[] values.
Not in notIn
db.query.select(
'users',
{
where: {
email: { [Op.notIn]: ['[email protected]'] }
}
}
);SQL equivalent
select * from users
where email not in ['[email protected]'];The Op.notIn operator will work for column with string, string[], number, number[] values.
Greater than gt
db.query.select(
'users',
{
where: {
score: { [Op.gt]: 10 }
}
}
);SQL equivalent
select * from users where score > 10;Greater than or equal to gte
db.query.select(
'users',
{
where: {
score: { [Op.gte]: 10 }
}
}
);SQL equivalent
select * from users where score >= 10;Less than lt
db.query.select(
'users',
{
where: {
score: { [Op.lt]: 10 }
}
}
);SQL equivalent
select * from users where score < 10;Less than or equal to lte
db.query.select(
'users',
{
where: {
score: { [Op.lte]: 10 }
}
}
);SQL equivalent
select * from users where score <= 10;Between between
db.query.select(
'users',
{
where: {
score: { [Op.between]: [10, 20] }
}
}
);SQL equivalent
select * from users
where score between 10 and 20;And and
db.query.select(
'users',
{
where: {
[Op.and]: [
{ status: 'active' },
{ score: { [Op.gte]: 40 } }
]
}
}
);SQL equivalent
select * from users
where status = 'active' and score >= 40;Or or
db.query.select(
'users',
{
where: {
[Op.or]: [
{ status: 'active' },
{ score: { [Op.gte]: 40 } }
]
}
}
);SQL equivalent
select * from users
where status = 'active' or score >= 40;Combination of and and or
db.query.select(
'users',
{
[Op.and]: [
{ status: 'active' },
{
[Op.or]: [
{ username: 'yusuf' },
{ score: { [Op.gte]: 40 } }
]
}
]
}
);SQL equivalent
select * from users
where status = 'active'
and (
username = 'yusuf'
or score >= 40
);Order By
Set the orderBy option to sort the rows.
orderBy is an array of objects where, each object consists of the following fields.
| Fields | Purpose |
|-----------|--------------------------------------------------------------------------------------------|
| attribute | Name of the column. It can also be the alias set via the Attributes option. |
| order | This represent the order. Default is ASC. Allowed values are ASC and DESC. |
Sort in ascending order.
db.query.select(
'users',
{
orderBy: [
{ attribute: 'username' }
]
}
);SQL equivalent
select * from users order by username;Alternatively,
select * from users order by username ASC;Sort in descending order.
db.query.select(
'users',
{
orderBy: [
{ attribute: 'username', order: Order.DESC }
]
}
);SQL equivalent
select * from users order by username DESC;Sort by multiple columns.
When sorting by multiple columns, minivium first sorts by the first column specified. If there are ties (i.e., rows with the same value in the first column), it uses the next column in the list to break the tie, and so on.
db.query.select(
'users',
{
orderBy: [
{ attribute: 'score', order: Order.DESC },
{ attribute: 'updatedAt', order: Order.ASC }
]
}
);SQL equivalent
select *
from users
order by score DESC, updatedAt ASC;Limit
Restricts the query to return only the first N rows from the collection.
db.query.select('users', { limit: 3 });SQL equivalent
select * from users limit 3;A { limit: 0 } means that the query will return 0 rows, regardless of the data in the collection.
Limit must not be negative
Offset
Skips a specified number of rows before starting to return rows from a query.
db.query.select('users', { offset: 2 });Limit must not be negative
Limit and Offset
The offset clause is typically used in conjunction with the limit clause to implement pagination.
- offset
n: Skips the firstnrows. - limit
m: Returns the nextmrows after the skipped rows.
db.query.select('users', { limit: 3, offset: 3 });SQL equivalent
select * from users limit 3 offset 3;Limitations
This will work well with small data (about 5MB). You may face performance issues with
larger data size because the content is serialized using JSON.stringify and
then saved in a file.
If you are planning to have more data then check out PostgreSQL.
Contribution
License
It's free :smiley:
MIT License Copyright (c) 2024 Yusuf Shakeel
Donate
Feeling generous :smiley: Donate via PayPal
