Minimalistic JSON database.
- Minimalistic
- Atomic Writes
- JSON Database
- Query Language
- Schema and Schemaless
- Sync and Async operations
- 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
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 |
npm i minivium
// ESM
import { minivium, SchemaRegistry } from "minivium";
// CommonJs
const { minivium, SchemaRegistry } = require("minivium");
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 . |
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' },
]
}
]
});
const db = minivium({ dataDir, schemaRegistry });
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();
Sync
db.dropCollection('users');
Async
await db.dropCollectionAsync('users');
Sync
db.dropAllCollections();
Async
await db.dropAllCollectionsAsync();
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 | 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 |
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.
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.
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.
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.
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.
Set the attributes
option to pick the desired columns.
db.query.select('users', { attributes: ['id', 'username'] });
SQL equivalent
select id, username from users;
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;
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");
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'
}
}
);
db.query.select(
'users',
{
where: {
status: { [Op.notEq]: 'active' }
}
}
);
SQL equivalent
select * from users where status != 'active';
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.
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.
db.query.select(
'users',
{
where: {
score: { [Op.gt]: 10 }
}
}
);
SQL equivalent
select * from users where score > 10;
db.query.select(
'users',
{
where: {
score: { [Op.gte]: 10 }
}
}
);
SQL equivalent
select * from users where score >= 10;
db.query.select(
'users',
{
where: {
score: { [Op.lt]: 10 }
}
}
);
SQL equivalent
select * from users where score < 10;
db.query.select(
'users',
{
where: {
score: { [Op.lte]: 10 }
}
}
);
SQL equivalent
select * from users where score <= 10;
db.query.select(
'users',
{
where: {
score: { [Op.between]: [10, 20] }
}
}
);
SQL equivalent
select * from users
where score between 10 and 20;
db.query.select(
'users',
{
where: {
[Op.and]: [
{ status: 'active' },
{ score: { [Op.gte]: 40 } }
]
}
}
);
SQL equivalent
select * from users
where status = 'active' and score >= 40;
db.query.select(
'users',
{
where: {
[Op.or]: [
{ status: 'active' },
{ score: { [Op.gte]: 40 } }
]
}
}
);
SQL equivalent
select * from users
where status = 'active' or score >= 40;
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
);
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 . |
db.query.select(
'users',
{
orderBy: [
{ attribute: 'username' }
]
}
);
SQL equivalent
select * from users order by username;
Alternatively,
select * from users order by username ASC;
db.query.select(
'users',
{
orderBy: [
{ attribute: 'username', order: Order.DESC }
]
}
);
SQL equivalent
select * from users order by username DESC;
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;
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
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
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;
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.
It's free 😃
MIT License Copyright (c) 2024 Yusuf Shakeel
Feeling generous 😃 Donate via PayPal