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
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 });
/* 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