zoomdb
Version:
A simple local storage database.
294 lines (287 loc) • 10.6 kB
JavaScript
const fs = require("fs");
class Database {
constructor(options) {
this.path = options.path;
this.schemaName = options.schema.name;
this.schemaData = options.schema;
}
getData() {
const data = fs.readFileSync(this.path, "utf-8");
if (data !== "") return JSON.parse(data);
return data;
}
async handleData(data, schema = this.schemaData) {
if (!schema || !schema.required)
throw new Error("Invalid schema provided.");
const errors = [];
Object.keys(schema.required).forEach(async (key) => {
const fieldSchema = schema.required[key];
const dataType = fieldSchema.type;
const dataValue = data[key];
if (typeof dataValue === "undefined") {
if (fieldSchema.required) errors.push(`Field '${key}' is required.`);
} else {
if (!fieldSchema.type) throw new Error("Must put a type object");
if (dataType === "array" && !Array.isArray(dataValue))
errors.push(`Field '${key}' must be an array.`);
else if (dataType === "object" && typeof dataValue !== "object")
errors.push(`Field '${key}' must be an object.`);
else if (typeof dataValue !== dataType)
errors.push(`Field '${key}' must be of type '${dataType}'.`);
else if (dataType === "object" && fieldSchema.properties) {
try {
await this.handleData(dataValue, {
required: fieldSchema.properties,
});
} catch (innerErrors) {
console.log(innerErrors);
errors.push(
...innerErrors
.split("\n")
.map((error) => `Field '${key}.${error}`)
);
}
}
}
});
if (errors.length > 0) throw new Error(errors.join("\n"));
}
new(data) {
try {
if (!data) throw new Error("Should put the schema name and data!");
if (typeof data !== "object") throw new Error("data must be object");
this.handleData(data);
let currentData = this.getData();
if (currentData == "" || !currentData) currentData = [];
if (!currentData.find((s) => s[this.schemaName])) {
currentData.push({
[this.schemaName]: { required: sc.required, data: [data] },
});
} else {
currentData
.find((s) => s[this.schemaName])
[this.schemaName].data.push(data);
}
fs.writeFileSync(this.path, JSON.stringify(currentData, null, 2));
return data;
} catch (error) {
throw new Error(error);
}
}
delete(data) {
try {
if (!data) throw new Error("Should put the schema name and data!");
if (typeof data !== "object") throw new Error("data must be object");
this.handleData(data);
let currentData = this.getData();
if (currentData == "" || !currentData) currentData = [];
if (!currentData.find((s) => s[this.schemaName])) {
return;
} else {
const test = currentData
.filter((item) => item[this.schemaName] && item[this.schemaName].data)
.map((item) =>
item[this.schemaName].data.filter((element) => {
return dataKeys.every((key) => element[key] !== data[key]);
})
)
.flat();
currentData.find((s) => s[this.schemaName])[this.schemaName].data =
test;
}
fs.writeFileSync(this.path, JSON.stringify(currentData, null, 2));
} catch (error) {
throw new Error(error);
}
}
deleteOne(data) {
try {
if (!data) throw new Error("Should put the schema name and data!");
if (typeof data !== "object") throw new Error("data must be object");
this.handleData(data);
let currentData = this.getData();
if (currentData == "" || !currentData) currentData = [];
if (!currentData.find((s) => s[this.schemaName])) {
return;
} else {
const test = currentData
.filter((item) => item[this.schemaName] && item[this.schemaName].data)
.map((item) =>
item[this.schemaName].data.find((element) => {
return dataKeys.every((key) => element[key] === data[key]);
})
)
.flat()[0];
const index = currentData
.find((s) => s[this.schemaName])
[this.schemaName].data.indexOf(test);
if (index !== -1) {
currentData
.find((s) => s[this.schemaName])
[this.schemaName].data.splice(index, 1);
}
}
fs.writeFileSync(this.path, JSON.stringify(currentData, null, 2));
} catch (error) {
throw new Error(error);
}
}
find(data = {}) {
try {
if (!data) throw new Error("Should put the schema name and data!");
if (typeof data !== "object") throw new Error("data must be object");
this.handleData(data);
let currentData = this.getData();
if (currentData == "" || !currentData) currentData = [];
if (!currentData.find((s) => s[this.schemaName])) {
return [];
} else {
let filteredData = currentData
.filter((item) => item[this.schemaName] && item[this.schemaName].data)
.map((item) =>
item[this.schemaName].data.filter((element) => {
return dataKeys.every((key) => element[key] === data[key]);
})
)
.flat(); // flatten the array
return filteredData;
}
} catch (error) {
throw new Error(error);
}
}
findOne(data = {}) {
try {
if (!data) throw new Error("Should put the schema name and data!");
if (typeof data !== "object") throw new Error("data must be object");
this.handleData(data);
let currentData = this.getData();
if (currentData == "" || !currentData) currentData = [];
if (!currentData.find((s) => s[this.schemaName])) {
return [];
} else {
let filteredData = currentData
.filter((item) => item[this.schemaName] && item[this.schemaName].data)
.map((item) =>
item[this.schemaName].data.find((element) => {
return dataKeys.every((key) => element[key] === data[key]);
})
)
.flat(); // flatten the array
return filteredData;
}
} catch (error) {
throw new Error(error);
}
}
update(data = {}, newData = {}) {
try {
if (!data) throw new Error("Should put the schema name and data!");
if (typeof data !== "object") throw new Error("data must be object");
this.handleData(data);
this.handleData(newData);
let currentData = this.getData();
if (currentData == "" || !currentData) currentData = [];
if (!currentData.find((s) => s[this.schemaName])) {
return;
} else {
currentData.map((item) => {
if (item[this.schemaName] && item[this.schemaName].data) {
item[this.schemaName].data = item[this.schemaName].data.map(
(element) => {
if (dataKeys.every((key) => element[key] === data[key])) {
return { ...element, ...newData }; // Update data with newData
} else {
return element;
}
}
);
}
return item;
});
}
fs.writeFileSync(this.path, JSON.stringify(currentData, null, 2));
} catch (error) {
throw new Error(error);
}
}
updateOne(data = {}, newData = {}) {
try {
if (!data) throw new Error("Should put the schema name and data!");
if (typeof data !== "object") throw new Error("data must be object");
this.handleData(data);
this.handleData(newData);
let currentData = this.getData();
if (currentData == "" || !currentData) currentData = [];
if (!currentData.find((s) => s[this.schemaName])) {
return;
} else {
currentData.some((item) => {
if (item[this.schemaName] && item[this.schemaName].data) {
const foundIndex = item[this.schemaName].data.findIndex((element) =>
dataKeys.every((key) => element[key] === data[key])
);
if (foundIndex !== -1) {
item[this.schemaName].data[foundIndex] = {
...item[this.schemaName].data[foundIndex],
...newData,
};
return true; // stop searching
}
}
return false;
});
}
fs.writeFileSync(this.path, JSON.stringify(currentData, null, 2));
} catch (error) {
throw new Error(error);
}
}
}
class SetUp {
constructor(options) {
this.path = options.path;
if (!this.path) throw new Error("Faild setup, please set the path!");
}
async handelModel(schema) {
Object.keys(schema).forEach(async (field) => {
const fieldType = schema[field].type;
if (!fieldType)
throw new Error(`يجب أن يحتوي ${field} على مفتاح "type".`);
const validTypes = ["string", "number", "boolean", "object", "array"];
if (!validTypes.includes(fieldType))
throw new Error(
`نوع غير صالح لـ ${field}. يجب أن يكون أحد الأنواع التالية: ${validTypes.join(
", "
)}.`
);
if (fieldType == "object") {
if (!schema[field].properties)
throw new Error(
"If the type == object should put a properties object"
);
if (typeof schema[field].properties !== "object")
throw new Error("properties must be Object!");
await this.handelModel(schema[field].properties);
}
});
}
model(name, schema) {
try {
name = typeof name === "string" ? name : undefined;
schema = typeof schema === "object" ? schema : undefined;
if (!name || !schema)
throw new Error("يجب تحديد اسم النموذج ومخطط البيانات.");
if (Object.keys(schema).length === 0)
throw new Error("لا يمكن تحديد مخطط بيانات فارغ.");
this.handelModel(schema);
return new Database({
path: this.path,
schema: { name, required: schema, data: [] },
});
} catch (error) {
throw new Error(error);
}
}
}
module.exports = SetUp;