json-as-sql
Version:
Use JSON files like an SQL database with full CRUD, filters, sorting, and more.
189 lines (163 loc) • 4.88 kB
JavaScript
// JSON-based version of the same DB utility
const fs = require('fs');
const path = require('path');
class JsonDB {
constructor(filePath) {
this.filePath = path.resolve(filePath);
if (!fs.existsSync(this.filePath)) {
fs.writeFileSync(this.filePath, JSON.stringify([]));
}
}
_readData() {
const content = fs.readFileSync(this.filePath, 'utf-8');
const data = JSON.parse(content);
return Array.isArray(data) ? data : [];
}
_writeData(data) {
fs.writeFileSync(this.filePath, JSON.stringify(data, null, 2));
}
_parseValue(value, isDate) {
if (isDate) return new Date(value).getTime();
if (!isNaN(value)) return parseFloat(value);
return value.toString().toLowerCase();
}
_applyFilter(data, where) {
return data.filter(row => {
return Object.entries(where).every(([key, condition]) => {
const rowVal = row[key] || '';
const isDate = key.toLowerCase().includes('date');
if (typeof condition === 'object' && condition !== null) {
const { op, value } = condition;
const a = this._parseValue(rowVal, isDate);
const b = this._parseValue(value, isDate);
switch (op) {
case '>': return a > b;
case '<': return a < b;
case '>=': return a >= b;
case '<=': return a <= b;
case '!=': return a != b;
case '=': return a == b;
case 'contains': return rowVal.toLowerCase().includes(String(value).toLowerCase());
default: return false;
}
} else {
return rowVal == condition;
}
});
});
}
_applySorting(data, orderBy = []) {
return data.sort((a, b) => {
for (let { column, direction } of orderBy) {
const isDate = column.toLowerCase().includes('date');
const valA = this._parseValue(a[column], isDate);
const valB = this._parseValue(b[column], isDate);
if (valA < valB) return direction === 'desc' ? 1 : -1;
if (valA > valB) return direction === 'desc' ? -1 : 1;
}
return 0;
});
}
_applySelectFields(data, fields) {
return data.map(row => {
const newObj = {};
fields.forEach(f => newObj[f] = row[f]);
return newObj;
});
}
async createTable(columns = []) {
if (!columns || !Array.isArray(columns) || columns.length === 0) {
throw new Error('You must pass an array of column names');
}
this._writeData([]);
return {
success: true,
message: 'Table created (JSON file initialized)',
columns
};
}
async select(where = {}, options = {}) {
let data = this._readData();
data = this._applyFilter(data, where);
if (options.orderBy) data = this._applySorting(data, options.orderBy);
if (options.offset) data = data.slice(options.offset);
if (options.limit) data = data.slice(0, options.limit);
if (options.selectFields) data = this._applySelectFields(data, options.selectFields);
return data;
}
async insertOne(rowObj) {
const data = this._readData();
data.push(rowObj);
this._writeData(data);
return {
success: true,
insertedData: rowObj
};
}
async insertMany(rows) {
const data = this._readData();
const insertRows = Array.isArray(rows) ? rows : [rows];
const updated = [...data, ...insertRows];
this._writeData(updated);
return {
success: true,
insertedCount: insertRows.length
};
}
async update(where, newData) {
const data = this._readData();
const updated = [];
const result = data.map(row => {
if (this._applyFilter([row], where).length > 0) {
const updatedRow = { ...row, ...newData };
updated.push(updatedRow);
return updatedRow;
}
return row;
});
this._writeData(result);
return {
success: true,
updatedCount: updated.length,
updatedRows: updated
};
}
async delete(where) {
const data = this._readData();
const filtered = [];
const deleted = [];
for (const row of data) {
if (this._applyFilter([row], where).length > 0) {
deleted.push(row);
} else {
filtered.push(row);
}
}
this._writeData(filtered);
return {
success: true,
deletedCount: deleted.length
};
}
async dropTable() {
fs.unlinkSync(this.filePath);
return { success: true, message: 'JSON table deleted' };
}
async truncateTable() {
this._writeData([]);
return {
success: true,
message: 'JSON table truncated (all data cleared)'
};
}
async showTableDetail() {
const data = this._readData();
const first = data[0] || {};
return {
success: true,
columns: Object.keys(first),
path: this.filePath
};
}
}
module.exports = JsonDB;