axiodb
Version:
A blazing-fast, lightweight, and scalable nodejs package based DBMS for modern application. Supports schemas, encryption, and advanced query capabilities.
446 lines • 24.3 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const Converter_helper_1 = __importDefault(require("../../Helper/Converter.helper"));
const Crypto_helper_1 = require("../../Helper/Crypto.helper");
const response_helper_1 = __importDefault(require("../../Helper/response.helper"));
const DataTypes_models_1 = require("../../Schema/DataTypes.models");
const FileManager_1 = __importDefault(require("../../engine/Filesystem/FileManager"));
const FolderManager_1 = __importDefault(require("../../engine/Filesystem/FolderManager"));
const Searcher_utils_1 = __importDefault(require("../../utility/Searcher.utils"));
const SortData_utils_1 = __importDefault(require("../../utility/SortData.utils"));
const outers_1 = require("outers");
// Validator
const validator_models_1 = __importDefault(require("../../Schema/validator.models"));
const Create_operation_1 = __importDefault(require("./Create.operation"));
const cache_operation_1 = __importDefault(require("../../cache/cache.operation"));
const Keys_1 = require("../../config/Keys/Keys");
const BufferLoaderWithWorker_utils_1 = __importDefault(require("../../utility/BufferLoaderWithWorker.utils"));
class UpdateOperation {
constructor(collectionName, path, baseQuery, isSchemaNeeded, schema, isEncrypted = false, encryptionKey) {
this.allDataWithFileName = [];
this.collectionName = collectionName;
this.path = path;
this.baseQuery = baseQuery;
this.isEncrypted = isEncrypted;
this.encryptionKey = encryptionKey;
this.updatedAt = new Date().toISOString();
this.sort = {};
if (isSchemaNeeded == true && !schema) {
throw new Error("Schema is required when isSchemaNeeded is true");
}
else {
this.isSchemaNeeded = isSchemaNeeded;
this.schema = schema;
}
this.Insertion = new Create_operation_1.default(this.collectionName, this.path);
this.ResponseHelper = new response_helper_1.default();
this.Converter = new Converter_helper_1.default();
if (this.isEncrypted === true) {
this.cryptoInstance = new Crypto_helper_1.CryptoHelper(this.encryptionKey);
}
this.allDataWithFileName = []; // To store all data with file name
}
/**
* Updates a single document that matches the base query.
*
* This method performs the following operations:
* 1. Searches for documents matching the base query
* 2. If documents are found, selects the first document (or first after sorting if sort criteria are provided)
* 3. Deletes the existing document file
* 4. Inserts a new file with updated data using the same document ID
*
* @param newData - The new data to replace the existing document
* @returns A Promise resolving to:
* - Success with updated data and previous data if successful
* - Error if any step fails
* @throws May throw errors during file operations or data processing
*/
UpdateOne(newData) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
try {
// check if the data is an empty object or not
if (Object.keys(newData).length === 0 || newData === undefined) {
throw new Error("Data cannot be an empty.");
}
// check if the data is an object or not
if (typeof newData !== "object") {
throw new Error("Data must be an object.");
}
if (this.isSchemaNeeded == true) {
// delete the extra fields from the schema if not present in the data
for (const key in newData) {
if (this.schema[key]) {
const newSchema = {
[key]: this.schema[key],
};
this.schema = newSchema;
}
}
// Validate the data
const validator = yield (0, validator_models_1.default)(this.schema, newData, true);
if (validator === null || validator === void 0 ? void 0 : validator.details) {
outers_1.Console.red("Validation Error", validator.details);
return this.ResponseHelper.Error(validator.details);
}
// Insert the updatedAt field in schema & data
this.schema.updatedAt = DataTypes_models_1.SchemaTypes.date().required();
newData.updatedAt = new Date().toISOString();
}
// if documentId is provided in the baseQuery then read the file with the documentId
let ReadResponse; // Read Response Holder
if (((_a = this.baseQuery) === null || _a === void 0 ? void 0 : _a.documentId) !== undefined) {
const FilePath = [
`.${this.baseQuery.documentId}${Keys_1.General.DBMS_File_EXT}`,
];
ReadResponse = yield this.LoadAllBufferRawData(FilePath);
}
else {
ReadResponse = yield this.LoadAllBufferRawData();
}
if ("data" in ReadResponse) {
const SearchedData = yield new Searcher_utils_1.default(ReadResponse.data).find(this.baseQuery, "data");
if (SearchedData.length === 0) {
return this.ResponseHelper.Error("No data found with the specified query");
}
let selectedFirstData = SearchedData[0]; // Select the first data
let fileName = selectedFirstData === null || selectedFirstData === void 0 ? void 0 : selectedFirstData.fileName; // Get the file name
const documentOldData = selectedFirstData.data; // Get the old data
// Sort the data if sort is provided then select the first data for deletion
if (Object.keys(this.sort).length === 0) {
selectedFirstData = SearchedData[0]; // Select the first data
fileName = selectedFirstData === null || selectedFirstData === void 0 ? void 0 : selectedFirstData.fileName; // Get the file name
}
else {
const Sorter = new SortData_utils_1.default(SearchedData, this.sort);
const SortedData = yield Sorter.sort("data"); // Sort the data
selectedFirstData = SortedData[0]; // Select the first data
fileName = selectedFirstData === null || selectedFirstData === void 0 ? void 0 : selectedFirstData.fileName; // Get the file name
}
const documentId = fileName.startsWith(".")
? fileName.slice(1).split(".")[0]
: fileName.split(".")[0];
// Update All new Fields in the old data
for (const key in newData) {
documentOldData[key] = newData[key];
// also change the updatedAt field
documentOldData.updatedAt = this.updatedAt;
}
// Delete the file
const deleteResponse = yield this.deleteFileUpdate(fileName);
if ("data" in deleteResponse) {
// Insert the new Data in the file
const InsertResponse = yield this.insertUpdate(documentOldData, documentId);
if ("data" in InsertResponse) {
yield cache_operation_1.default.clearAllCache(); // clear the cache
return this.ResponseHelper.Success({
message: "Data updated successfully",
newData: documentOldData,
previousData: selectedFirstData.data,
documentId: documentId,
});
}
else {
return this.ResponseHelper.Error("Failed to insert data");
}
}
else {
return this.ResponseHelper.Error("Failed to delete file");
}
}
else {
return this.ResponseHelper.Error("Failed to read raw data");
}
}
catch (error) {
console.log(error);
return this.ResponseHelper.Error("Failed to update data");
}
});
}
/**
* Updates multiple documents that match the base query.
*
* This method performs the following operations:
* 1. Searches for documents matching the base query
* 2. Deletes the existing documents
* 3. Inserts new files with updated data for each document
*
* @param newData - The new data to replace the existing documents
* @returns A Promise resolving to:
* - Success with updated data and previous data if successful
* - Error if any step fails
* @throws May throw errors during file operations or data processing
*/
UpdateMany(newData) {
return __awaiter(this, void 0, void 0, function* () {
try {
// check if the data is an empty object or not
if (Object.keys(newData).length === 0 || newData === undefined) {
throw new Error("Data cannot be an empty.");
}
// check if the data is an object or not
if (typeof newData !== "object") {
throw new Error("Data must be an object.");
}
newData.updatedAt = new Date().toISOString();
if (this.isSchemaNeeded == true) {
// Insert the updatedAt field in schema & data
this.schema.updatedAt = DataTypes_models_1.SchemaTypes.date().required();
// delete the extra fields from the schema if not present in the data
for (const key in newData) {
if (this.schema[key]) {
const newSchema = {
[key]: this.schema[key],
};
this.schema = newSchema;
}
}
// Validate the data
const validator = yield (0, validator_models_1.default)(this.schema, newData, true);
if (validator === null || validator === void 0 ? void 0 : validator.details) {
outers_1.Console.red("Validation Error", validator.details);
return this.ResponseHelper.Error(validator.details);
}
}
const ReadResponse = yield this.LoadAllBufferRawData();
if ("data" in ReadResponse) {
const SearchedData = yield new Searcher_utils_1.default(ReadResponse.data).find(this.baseQuery, "data");
if (SearchedData.length === 0) {
return this.ResponseHelper.Error("No data found with the specified query");
}
const documentIds = [];
for (let i = 0; i < SearchedData.length; i++) {
let selectedData = SearchedData[i]; // Select the first data
let fileName = selectedData === null || selectedData === void 0 ? void 0 : selectedData.fileName; // Get the file name
const documentOldData = selectedData.data; // Get the old data
// Sort the data if sort is provided then select the first data for deletion
if (Object.keys(this.sort).length === 0) {
selectedData = SearchedData[i]; // Select the first data
fileName = selectedData === null || selectedData === void 0 ? void 0 : selectedData.fileName; // Get the file name
}
else {
const Sorter = new SortData_utils_1.default(SearchedData, this.sort);
const SortedData = yield Sorter.sort("data"); // Sort the data
selectedData = SortedData[i]; // Select the first data
fileName = selectedData === null || selectedData === void 0 ? void 0 : selectedData.fileName; // Get the file name
}
const documentId = fileName.startsWith(".")
? fileName.slice(1).split(".")[0]
: fileName.split(".")[0];
documentIds.push(documentId);
// Update All new Fields in the old data
for (const key in newData) {
documentOldData[key] = newData[key];
// also change the updatedAt field
documentOldData.updatedAt = newData.updatedAt;
}
// Delete the file
const deleteResponse = yield this.deleteFileUpdate(fileName);
if ("data" in deleteResponse) {
// Insert the new Data in the file
const InsertResponse = yield this.insertUpdate(documentOldData, documentId);
if ("data" in InsertResponse) {
continue;
}
else {
return this.ResponseHelper.Error("Failed to insert data");
}
}
else {
return this.ResponseHelper.Error("Failed to delete file");
}
}
yield cache_operation_1.default.clearAllCache(); // clear the cache
return this.ResponseHelper.Success({
message: "Data updated successfully",
effectedData: SearchedData.length,
documentIds: documentIds,
});
}
else {
return this.ResponseHelper.Error("Failed to read raw data");
}
}
catch (error) {
return this.ResponseHelper.Error("Failed to update data");
}
});
}
/**
* to be sorted to the query this.createdAt = new Date().toISOString();
this.updatedAt = this.createdAt; // Initially updatedAt is same as createdAt
* @param {object} sort - The sort to be set.
* @returns {DeleteOperation} - An instance of the DeleteOperation class.
*/
Sort(sort) {
this.sort = sort;
return this;
}
/**
* Loads all buffer raw data from the specified directory.
*
* This method performs the following steps:
* 1. Checks if the directory is locked.
* 2. If the directory is not locked, it lists all files in the directory.
* 3. Reads each file and decrypts the data if encryption is enabled.
* 4. Stores the decrypted data in the `AllData` array.
* 5. If the directory is locked, it unlocks the directory, reads the files, and then locks the directory again.
*
* @returns {Promise<SuccessInterface | ErrorInterface>} A promise that resolves to a success or error response.
*
* @throws {Error} Throws an error if any operation fails.
*/
LoadAllBufferRawData(documentIdDirectFile) {
return __awaiter(this, void 0, void 0, function* () {
try {
// Check if Directory Locked or not
const isLocked = yield new FolderManager_1.default().IsDirectoryLocked(this.path);
if ("data" in isLocked) {
// If Directory is not locked
if (isLocked.data === false) {
// Read List the data from the file
const ReadResponse = yield new FolderManager_1.default().ListDirectory(this.path);
if ("data" in ReadResponse) {
// Store all files in DataFilesList
const DataFilesList = documentIdDirectFile !== undefined
? documentIdDirectFile
: ReadResponse.data;
// Read all files from the directoryory
const resultData = yield (0, BufferLoaderWithWorker_utils_1.default)(DataFilesList, this.cryptoInstance, this.path, this.isEncrypted, true);
this.allDataWithFileName = resultData; // Store all data with file name
return this.ResponseHelper.Success(resultData);
}
return this.ResponseHelper.Error("Failed to read directory");
}
else {
// if Directory is locked then unlock it
const unlockResponse = yield new FolderManager_1.default().UnlockDirectory(this.path);
if ("data" in unlockResponse) {
// Read List the data from the file
const ReadResponse = yield new FolderManager_1.default().ListDirectory(this.path);
if ("data" in ReadResponse) {
// Store all files in DataFilesList
const DataFilesList = documentIdDirectFile !== undefined
? documentIdDirectFile
: ReadResponse.data;
// Read all files from the directory
const resultData = yield (0, BufferLoaderWithWorker_utils_1.default)(DataFilesList, this.cryptoInstance, this.path, this.isEncrypted, true);
this.allDataWithFileName = resultData; // Store all data with file name
// Lock the directory after reading all files
const lockResponse = yield new FolderManager_1.default().LockDirectory(this.path);
if ("data" in lockResponse) {
return this.ResponseHelper.Success(resultData);
}
else {
return this.ResponseHelper.Error(`Failed to lock directory: ${this.path}`);
}
}
return this.ResponseHelper.Error(`Failed to read directory: ${this.path}`);
}
else {
return this.ResponseHelper.Error(`Failed to unlock directory: ${this.path}`);
}
}
}
else {
return this.ResponseHelper.Error(isLocked);
}
}
catch (error) {
return this.ResponseHelper.Error(error);
}
});
}
/**
* Deletes a file from the specified path.
*
* This method checks if the directory is locked before attempting to delete the file.
* If the directory is locked, it tries to unlock it, delete the file, and then lock it again.
*
* @param fileName - The name of the file to be deleted
* @returns A response object indicating success or failure
* Success response: { status: true, message: "File deleted successfully" }
* Error response: { status: false, message: <error message> }
* @private
*/
deleteFileUpdate(fileName) {
return __awaiter(this, void 0, void 0, function* () {
// Check if Directory Locked or not
const isLocked = yield new FolderManager_1.default().IsDirectoryLocked(this.path);
if ("data" in isLocked) {
// If Directory is not locked
if (isLocked.data === false) {
const deleteResponse = yield new FileManager_1.default().DeleteFile(`${this.path}/${fileName}`);
if ("data" in deleteResponse) {
return this.ResponseHelper.Success("File deleted successfully");
}
else {
return this.ResponseHelper.Error("Failed to delete file");
}
}
else {
const unlockResponse = yield new FolderManager_1.default().UnlockDirectory(this.path);
if ("data" in unlockResponse) {
const deleteResponse = yield new FileManager_1.default().DeleteFile(`${this.path}/${fileName}`);
if ("data" in deleteResponse) {
const lockResponse = yield new FolderManager_1.default().LockDirectory(this.path);
if ("data" in lockResponse) {
return this.ResponseHelper.Success("File deleted successfully");
}
else {
return this.ResponseHelper.Error("Failed to lock directory");
}
}
else {
return this.ResponseHelper.Error("Failed to delete file");
}
}
else {
return this.ResponseHelper.Error("Failed to unlock directory");
}
}
}
else {
return this.ResponseHelper.Error("Failed to delete file");
}
});
}
/**
* Inserts a document into the collection.
* @param {object} data - The data to be inserted.
* @returns {Promise<any>} - A promise that resolves with the response of the insertion operation.
*/
insertUpdate(data, ExistingdocumentId) {
return __awaiter(this, void 0, void 0, function* () {
// Check if data is empty or not
if (!data) {
throw new Error("Data cannot be empty");
}
// Check if data is an object or not
if (typeof data !== "object") {
throw new Error("Data must be an object.");
}
// Encrypt the data if crypto is enabled
if (this.isEncrypted && this.cryptoInstance !== undefined) {
data = yield this.cryptoInstance.encrypt(this.Converter.ToString(data));
}
// Save the data
return yield this.Insertion.Save(data, ExistingdocumentId);
});
}
}
exports.default = UpdateOperation;
//# sourceMappingURL=Update.operation.js.map