asta.db
Version:
Enhanced database solution with QuickDB-like features
339 lines (300 loc) • 8.39 kB
JavaScript
const fs = require("fs");
const path = require("path");
class AstaDB {
constructor(options = {}) {
this.dataPath = options.dataPath || "./astadb.json";
this.data = this.loadData();
}
loadData() {
try {
if (fs.existsSync(this.dataPath)) {
const rawData = fs.readFileSync(this.dataPath, "utf8");
return JSON.parse(rawData) || {};
}
} catch (error) {
console.error("Error loading data:", error);
}
return {};
}
saveData() {
try {
fs.writeFileSync(
this.dataPath,
JSON.stringify(this.data, null, 2),
"utf8"
);
} catch (error) {
console.error("Error saving data:", error);
}
}
setDataPath(newPath) {
this.dataPath = newPath;
this.data = this.loadData();
return this;
}
all() {
// إرجاع مصفوفة من الإدخالات { id, value } لتكون متوافقة مع QuickDB
const entries = [];
const flatten = (obj, prefix = "") => {
for (const key in obj) {
const fullKey = prefix ? `${prefix}.${key}` : key;
if (
typeof obj[key] === "object" &&
obj[key] !== null &&
!Array.isArray(obj[key])
) {
flatten(obj[key], fullKey);
} else {
entries.push({ id: fullKey, value: obj[key] });
}
}
};
flatten(this.data);
return entries;
}
set(key, value) {
const keys = key.split(".");
let current = this.data;
for (let i = 0; i < keys.length - 1; i++) {
const part = keys[i];
if (!current[part] || typeof current[part] !== "object") {
current[part] = {};
}
current = current[part];
}
current[keys[keys.length - 1]] = value;
this.saveData();
return this;
}
get(key, defaultValue = null) {
if (!key) return this.all();
const keys = key.split(".");
let current = this.data;
for (const part of keys) {
if (current[part] === undefined) {
return defaultValue;
}
current = current[part];
}
return current;
}
fetch(key, defaultValue = null) {
return this.get(key, defaultValue);
}
push(key, value) {
const array = this.get(key, []);
if (!Array.isArray(array)) {
throw new Error(`Value at ${key} is not an array`);
}
array.push(value);
this.set(key, array);
return this;
}
delete(key) {
const keys = key.split(".");
let current = this.data;
for (let i = 0; i < keys.length - 1; i++) {
const part = keys[i];
if (current[part] === undefined) {
return this;
}
current = current[part];
}
delete current[keys[keys.length - 1]];
this.saveData();
return this;
}
deleteAll() {
this.data = {};
this.saveData();
return this;
}
has(key) {
return this.get(key) !== null;
}
add(key, amount = 1) {
const value = this.get(key, 0);
if (typeof value !== "number") {
throw new Error(`Value at ${key} is not a number`);
}
this.set(key, value + amount);
return this;
}
subtract(key, amount = 1) {
return this.add(key, -amount);
}
startsWith(prefix) {
const results = {};
const search = (data, currentPath = "") => {
for (const key in data) {
const fullPath = currentPath ? `${currentPath}.${key}` : key;
if (fullPath.startsWith(prefix)) {
results[fullPath] = data[key];
}
if (typeof data[key] === "object") {
search(data[key], fullPath);
}
}
};
search(this.data);
return results;
}
count(key) {
const value = this.get(key);
if (Array.isArray(value)) {
return value.length;
} else if (typeof value === "object" && value !== null) {
return Object.keys(value).length;
}
return 0;
}
ensure(key, defaultValue) {
if (!this.has(key)) {
this.set(key, defaultValue);
}
return this.get(key);
}
includes(key, value) {
const data = this.get(key);
if (!Array.isArray(data)) {
throw new Error(`Value at ${key} is not an array`);
}
return data.includes(value);
}
filter(key, condition) {
const data = this.get(key);
if (!Array.isArray(data)) {
throw new Error(`Value at ${key} is not an array`);
}
if (typeof condition === "function") {
return data.filter(condition);
} else if (typeof condition === "object") {
return data.filter((item) => {
for (const prop in condition) {
if (item[prop] !== condition[prop]) {
return false;
}
}
return true;
});
}
throw new Error("Condition must be a function or object");
}
find(key, condition) {
const data = this.get(key);
if (!Array.isArray(data)) {
throw new Error(`Value at ${key} is not an array`);
}
if (typeof condition === "function") {
return data.find(condition);
} else if (typeof condition === "object") {
return data.find((item) => {
for (const prop in condition) {
if (item[prop] !== condition[prop]) {
return false;
}
}
return true;
});
}
throw new Error("Condition must be a function or object");
}
isArray(key) {
return Array.isArray(this.get(key));
}
ensureArray(key) {
const value = this.get(key);
if (!Array.isArray(value)) {
this.set(key, []);
return [];
}
return value;
}
remove(key, callback) {
const data = this.get(key);
if (!Array.isArray(data)) {
throw new Error(`Value at ${key} is not an array`);
}
const removed = data.filter(callback);
this.set(
key,
data.filter((item) => !callback(item))
);
return removed;
}
// وظائف إضافية لتكون متوافقة تمامًا مع QuickDB
table(name) {
// هذه الوظيفة تحاكي سلوك الجداول في QuickDB
return {
set: (key, value) => this.set(`${name}.${key}`, value),
get: (key, defaultValue) => this.get(`${name}.${key}`, defaultValue),
delete: (key) => this.delete(`${name}.${key}`),
all: () => this.all().filter((entry) => entry.id.startsWith(`${name}.`)),
push: (key, value) => this.push(`${name}.${key}`, value),
add: (key, amount) => this.add(`${name}.${key}`, amount),
subtract: (key, amount) => this.subtract(`${name}.${key}`, amount),
has: (key) => this.has(`${name}.${key}`),
startsWith: (prefix) => this.startsWith(`${name}.${prefix}`),
deleteAll: () => {
const entries = this.all().filter((entry) =>
entry.id.startsWith(`${name}.`)
);
entries.forEach((entry) => this.delete(entry.id));
return entries.length;
},
};
}
// دعم لـ Array.prototype methods
array(key) {
const array = this.get(key, []);
if (!Array.isArray(array)) {
throw new Error(`Value at ${key} is not an array`);
}
return {
push: (...items) => {
array.push(...items);
this.set(key, array);
return array.length;
},
pop: () => {
const item = array.pop();
this.set(key, array);
return item;
},
shift: () => {
const item = array.shift();
this.set(key, array);
return item;
},
unshift: (...items) => {
array.unshift(...items);
this.set(key, array);
return array.length;
},
splice: (...args) => {
const result = array.splice(...args);
this.set(key, array);
return result;
},
slice: (...args) => array.slice(...args),
find: (...args) => array.find(...args),
filter: (...args) => array.filter(...args),
map: (...args) => array.map(...args),
reduce: (...args) => array.reduce(...args),
forEach: (...args) => array.forEach(...args),
includes: (...args) => array.includes(...args),
indexOf: (...args) => array.indexOf(...args),
join: (...args) => array.join(...args),
length: array.length,
get: () => array,
set: (newArray) => {
if (!Array.isArray(newArray)) {
throw new Error("Value must be an array");
}
this.set(key, newArray);
return newArray;
},
};
}
}
module.exports = AstaDB;