UNPKG

axiodb

Version:

A blazing-fast, lightweight, and scalable nodejs package based DBMS for modern application. Supports schemas, encryption, and advanced query capabilities.

379 lines 18.8 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 }); /* eslint-disable @typescript-eslint/no-explicit-any */ const FolderManager_1 = __importDefault(require("../../engine/Filesystem/FolderManager")); // Import All helpers const cache_operation_1 = __importDefault(require("../../cache/cache.operation")); 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")); // Import All Utility const Keys_1 = require("../../config/Keys/Keys"); const Searcher_utils_1 = __importDefault(require("../../utility/Searcher.utils")); const SortData_utils_1 = __importDefault(require("../../utility/SortData.utils")); const BufferLoaderWithWorker_utils_1 = __importDefault(require("../../utility/BufferLoaderWithWorker.utils")); /** * Class representing a read operation. */ class Reader { /** * Creates an instance of Read. * @param {string} collectionName - The name of the collection. * @param {string} path - The data to be read. * @param {object} baseQuery - The base query to be used. * @param {boolean} isEncrypted - The encryption status. * @param {string} encryptionKey - The encryption key. */ constructor(collectionName, path, baseQuery, isEncrypted = false, encryptionKey) { this.collectionName = collectionName; this.path = path; this.limit = 10; this.skip = 0; this.FindOneStatus = false; // Default value for FindOneStatus this.isEncrypted = isEncrypted; this.sort = {}; this.project = {}; this.baseQuery = baseQuery; this.Converter = new Converter_helper_1.default(); this.encryptionKey = encryptionKey; this.ResponseHelper = new response_helper_1.default(); this.totalCount = false; this.AllData = []; if (this.isEncrypted === true) { this.cryptoInstance = new Crypto_helper_1.CryptoHelper(this.encryptionKey); } } /** * Reads the data from a file. * @returns {Promise<any>} A promise that resolves with the response of the read operation. */ exec() { return __awaiter(this, void 0, void 0, function* () { var _a, _b; try { let SearchedData = []; // At first check if the data is in cache or not const responseFromCache = yield cache_operation_1.default.getCache(this.Converter.ToString(this.baseQuery)); if (responseFromCache !== false) { SearchedData = responseFromCache; // if the data is in cache then use it instead of searching // Check if any sort is passed or not if (Object.keys(this.sort).length === 0) { return yield this.ApplySkipAndLimit(SearchedData); // if no sort is passed then return searched data } const Sorter = new SortData_utils_1.default(SearchedData, this.sort); const SortedData = yield Sorter.sort(); // Sort the data return yield this.ApplySkipAndLimit(SortedData); // Apply Skip and Limit & return the data } else { let ReadResponse; // Read Response Holder if (((_a = this.baseQuery) === null || _a === void 0 ? void 0 : _a.documentId) !== undefined) { const FilePath = Array.isArray((_b = this.baseQuery) === null || _b === void 0 ? void 0 : _b.documentId) == true ? this.baseQuery.documentId.map((id) => `.${id}${Keys_1.General.DBMS_File_EXT}`) : [`.${this.baseQuery.documentId}${Keys_1.General.DBMS_File_EXT}`]; ReadResponse = yield this.LoadAllBufferRawData(FilePath); // Send the data to the client directly return this.ApplySkipAndLimit(ReadResponse.data); } else { ReadResponse = yield this.LoadAllBufferRawData(); } if ("data" in ReadResponse) { // Check if any query is passed or not if (Object.keys(this.baseQuery).length === 0) { // Check if any sort is passed or not if (Object.keys(this.sort).length === 0) { return yield this.ApplySkipAndLimit(ReadResponse.data); // Apply Skip and Limit & return the data } const Sorter = new SortData_utils_1.default(ReadResponse.data, this.sort); const SortedData = yield Sorter.sort(); // Sort the data return yield this.ApplySkipAndLimit(SortedData); // Apply Skip and Limit & return the data } // Search the data from the AllData using Searcher const searcher = new Searcher_utils_1.default(ReadResponse.data); SearchedData = yield searcher.find(this.baseQuery); yield cache_operation_1.default.setCache(this.Converter.ToString(this.baseQuery), SearchedData); // Check if any sort is passed or not if (Object.keys(this.sort).length === 0) { return yield this.ApplySkipAndLimit(SearchedData); // if no sort is passed then return searched data } const Sorter = new SortData_utils_1.default(SearchedData, this.sort); const SortedData = yield Sorter.sort(); // Sort the data return yield this.ApplySkipAndLimit(SortedData); // Apply Skip and Limit & return the data } } return this.ResponseHelper.Error("Failed to read data"); } catch (error) { return this.ResponseHelper.Error(error); } }); } /** * set limit to the query * @param {number} limit - The limit to be set. * @returns {Reader} - An instance of the Reader class. */ Limit(limit) { // Check if limit is a number or not if (typeof limit !== "number") { throw new Error("Limit should be a number"); } this.limit = limit; return this; } /** * to be skipped to the query * @param {number} skip - The skip to be set. * @returns {Reader} - An instance of the Reader class. */ Skip(skip) { // Check if skip is a number or not if (typeof skip !== "number") { throw new Error("Skip should be a number"); } this.skip = skip; return this; } /** * to be sorted to the query * @param {object} sort - The sort to be set. * @returns {Reader} - An instance of the Reader class. */ Sort(sort) { // check if sort is an object or not if (typeof sort !== "object") { throw new Error("Sort should be an object"); } this.sort = sort; return this; } /** * Sets whether to include the total count of matching documents in the result. * * @param count - Boolean flag indicating whether to include the total count * @returns The Reader instance for method chaining */ setCount(count) { this.totalCount = count; return this; } findOne(status = false) { this.FindOneStatus = status; return this; } setProject(project) { // check if project is an object or not if (typeof project !== "object") { throw new Error("Project should be an object"); } this.project = project; 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 directory const resultData = yield (0, BufferLoaderWithWorker_utils_1.default)(DataFilesList, this.cryptoInstance, this.path, this.isEncrypted); this.AllData = resultData; // Store all data in AllData 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); this.AllData = resultData; // Store all data in AllData // 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); } }); } /** * Applies skip and limit to the provided data array. * * This method checks if both `limit` and `skip` are defined. If they are, * it slices the `FinalData` array according to the `skip` and `limit` values * and returns the sliced data. If either `limit` or `skip` is not defined, * it returns the original `FinalData` array. * * @param {any[]} FinalData - The array of data to apply skip and limit to. * @returns {Promise<SuccessInterface | ErrorInterface>} - A promise that resolves to a success interface containing the sliced data or the original data. */ ApplySkipAndLimit(FinalData) { return __awaiter(this, void 0, void 0, function* () { // Check if FindOneStatus is true if (this.FindOneStatus === true) { // If FindOneStatus is true then return the first document if (FinalData.length > 0) { if (Object.keys(this.project).length !== 0) { const projectionresponse = yield this.ApplyProjection([FinalData[0]]); if ("data" in projectionresponse) { return this.ResponseHelper.Success({ documents: projectionresponse.data.documents[0], }); } } return this.ResponseHelper.Success({ documents: FinalData[0], }); } else { return this.ResponseHelper.Error("No documents found"); } } // Check if limit is passed or not if (this.limit !== undefined && this.skip !== undefined) { // Apply Skip and Limit const limitedAndSkippedData = FinalData.slice(this.skip, this.skip + this.limit); if (this.totalCount) { // Apply Projectd if total count is true if (Object.keys(this.project).length !== 0) { const projectionresponse = yield this.ApplyProjection(limitedAndSkippedData); if ("data" in projectionresponse) { return this.ResponseHelper.Success({ documents: projectionresponse.data.documents, totalDocuments: FinalData.length, }); } } return this.ResponseHelper.Success({ documents: limitedAndSkippedData, totalDocuments: limitedAndSkippedData.length, }); } else { if (Object.keys(this.project).length !== 0) { const projectionresponse = yield this.ApplyProjection(limitedAndSkippedData); if ("data" in projectionresponse) { return this.ResponseHelper.Success({ documents: projectionresponse.data.documents, }); } } return this.ResponseHelper.Success({ documents: limitedAndSkippedData, }); } } return this.ResponseHelper.Success({ documents: FinalData, }); }); } // Apply Projection ApplyProjection(FinalData) { return __awaiter(this, void 0, void 0, function* () { // Special keys const SpecialKeys = ["documentId"]; // Apply Project if (Object.keys(this.project).length !== 0) { const projectedData = FinalData.map((data) => { const projectedObject = {}; const keys = Object.keys(this.project); const hasInclude = keys.some((key) => this.project[key] === 1); const hasExclude = keys.every((key) => this.project[key] === 0); if (hasInclude) { for (const [key, value] of Object.entries(this.project)) { if (value === 1) { projectedObject[key] = key in data ? data[key] : null; } } } else if (hasExclude) { for (const key in data) { if (!(key in this.project)) { projectedObject[key] = data[key]; } } } else { throw new Error("Invalid projection: mixing inclusion and exclusion is not allowed."); } // Always include documentId (and optionally updatedAt) SpecialKeys.forEach((key) => { if (key in data) { projectedObject[key] = data[key]; } }); return projectedObject; }); return this.ResponseHelper.Success({ documents: projectedData, }); } return this.ResponseHelper.Success({ documents: FinalData, }); }); } } exports.default = Reader; //# sourceMappingURL=Reader.operation.js.map