jdb-lite
Version:
A lightweight schema-based JSON database library with validation
182 lines • 6.87 kB
JavaScript
import * as fs from 'fs';
import * as path from 'path';
import { ObjectId } from './objectid';
export class DatabaseManager {
constructor(dbPath = '.db_json') {
this.collections = new Map();
this.dbPath = path.resolve(dbPath);
this.ensureDbDirectory();
}
ensureDbDirectory() {
if (!fs.existsSync(this.dbPath)) {
fs.mkdirSync(this.dbPath, { recursive: true });
}
}
getCollectionPath(collectionName) {
return path.join(this.dbPath, `${collectionName}.json`);
}
loadCollection(collectionName) {
if (this.collections.has(collectionName)) {
return this.collections.get(collectionName);
}
const filePath = this.getCollectionPath(collectionName);
if (!fs.existsSync(filePath)) {
// Create empty collection file immediately
const emptyCollection = [];
this.collections.set(collectionName, emptyCollection);
this.saveCollection(collectionName);
return emptyCollection;
}
try {
const data = fs.readFileSync(filePath, 'utf-8');
const collection = JSON.parse(data);
this.collections.set(collectionName, collection);
return collection;
}
catch (error) {
console.warn(`Failed to load collection ${collectionName}:`, error);
const emptyCollection = [];
this.collections.set(collectionName, emptyCollection);
this.saveCollection(collectionName);
return emptyCollection;
}
}
saveCollection(collectionName) {
const collection = this.collections.get(collectionName);
if (!collection)
return;
const filePath = this.getCollectionPath(collectionName);
try {
fs.writeFileSync(filePath, JSON.stringify(collection, null, 2), 'utf-8');
}
catch (error) {
console.error(`Failed to save collection ${collectionName}:`, error);
throw error;
}
}
async insertOne(collectionName, doc) {
const collection = this.loadCollection(collectionName);
// Generate _id if not provided
if (!doc._id) {
doc._id = new ObjectId().toString();
}
// Add timestamps if they don't exist
const now = new Date();
if (!doc.createdAt)
doc.createdAt = now;
if (!doc.updatedAt)
doc.updatedAt = now;
collection.push({ ...doc });
this.saveCollection(collectionName);
return doc;
}
async insertMany(collectionName, docs) {
const collection = this.loadCollection(collectionName);
const insertedDocs = [];
for (const doc of docs) {
if (!doc._id) {
doc._id = new ObjectId().toString();
}
const now = new Date();
if (!doc.createdAt)
doc.createdAt = now;
if (!doc.updatedAt)
doc.updatedAt = now;
const insertedDoc = { ...doc };
collection.push(insertedDoc);
insertedDocs.push(insertedDoc);
}
this.saveCollection(collectionName);
return insertedDocs;
}
async find(collectionName, filter = {}) {
const collection = this.loadCollection(collectionName);
if (Object.keys(filter).length === 0) {
return [...collection];
}
return collection.filter(doc => this.matchesFilter(doc, filter));
}
async findOne(collectionName, filter = {}) {
const collection = this.loadCollection(collectionName);
if (Object.keys(filter).length === 0) {
return collection[0] || null;
}
return collection.find(doc => this.matchesFilter(doc, filter)) || null;
}
async findById(collectionName, id) {
return this.findOne(collectionName, { _id: id });
}
async updateOne(collectionName, filter, update) {
const collection = this.loadCollection(collectionName);
const docIndex = collection.findIndex(doc => this.matchesFilter(doc, filter));
if (docIndex === -1) {
return { modifiedCount: 0 };
}
// Apply update
const updatedDoc = { ...collection[docIndex], ...update };
updatedDoc.updatedAt = new Date();
collection[docIndex] = updatedDoc;
this.saveCollection(collectionName);
return { modifiedCount: 1 };
}
async updateMany(collectionName, filter, update) {
const collection = this.loadCollection(collectionName);
let modifiedCount = 0;
for (let i = 0; i < collection.length; i++) {
if (this.matchesFilter(collection[i], filter)) {
collection[i] = { ...collection[i], ...update };
collection[i].updatedAt = new Date();
modifiedCount++;
}
}
if (modifiedCount > 0) {
this.saveCollection(collectionName);
}
return { modifiedCount };
}
async deleteOne(collectionName, filter) {
const collection = this.loadCollection(collectionName);
const docIndex = collection.findIndex(doc => this.matchesFilter(doc, filter));
if (docIndex === -1) {
return { deletedCount: 0 };
}
collection.splice(docIndex, 1);
this.saveCollection(collectionName);
return { deletedCount: 1 };
}
async deleteMany(collectionName, filter) {
const collection = this.loadCollection(collectionName);
const initialLength = collection.length;
const filteredCollection = collection.filter(doc => !this.matchesFilter(doc, filter));
this.collections.set(collectionName, filteredCollection);
this.saveCollection(collectionName);
return { deletedCount: initialLength - filteredCollection.length };
}
async countDocuments(collectionName, filter = {}) {
const docs = await this.find(collectionName, filter);
return docs.length;
}
async dropCollection(collectionName) {
const filePath = this.getCollectionPath(collectionName);
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
}
this.collections.delete(collectionName);
}
matchesFilter(doc, filter) {
for (const [key, value] of Object.entries(filter)) {
if (doc[key] !== value) {
return false;
}
}
return true;
}
ensureCollection(collectionName) {
this.loadCollection(collectionName);
}
getCollectionNames() {
const files = fs.readdirSync(this.dbPath);
return files.filter(file => file.endsWith('.json')).map(file => file.replace('.json', ''));
}
}
//# sourceMappingURL=database.js.map