UNPKG

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
"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