minivium v0.3.0
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 minivium
Import
// 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: 'yusufshakeel@example.com',
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: 'yusufshakeel@example.com',
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: 'john@example.com',
score: 10,
status: 'active',
createdAt: '2024-12-27',
},
{
username: 'jane',
email: 'jane@example.com',
score: 10,
status: 'active',
createdAt: '2024-12-27',
}
]);
Async syntax await bulkInsertAsync(collectionName, dataToInsert[])
const ids = await db.query.bulkInsertAsync('users', [
{
username: 'john',
email: 'john@example.com',
score: 10,
status: 'active',
createdAt: '2024-12-27',
},
{
username: 'jane',
email: 'jane@example.com',
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]: ['yusuf@example.com'] }
}
}
);
SQL equivalent
select * from users
where email in ['yusuf@example.com'];
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]: ['yusuf@example.com'] }
}
}
);
SQL equivalent
select * from users
where email not in ['yusuf@example.com'];
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 firstn
rows. - limit
m
: Returns the nextm
rows 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
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago