axiodb
Version:
A blazing-fast, lightweight, and scalable nodejs package based DBMS for modern application. Supports schemas, encryption, and advanced query capabilities.
273 lines • 14.2 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 response_helper_1 = __importDefault(require("../../Helper/response.helper"));
const Crypto_helper_1 = require("../../Helper/Crypto.helper");
const FolderManager_1 = __importDefault(require("../../engine/Filesystem/FolderManager"));
const Converter_helper_1 = __importDefault(require("../../Helper/Converter.helper"));
const outers_1 = require("outers");
const BufferLoaderWithWorker_utils_1 = __importDefault(require("../../utility/BufferLoaderWithWorker.utils"));
/**
* Class that performs aggregation operations on data.
*
* This class allows for MongoDB-like aggregation pipeline operations on collection data.
* It supports various stages including $match, $group, $sort, $project, $limit, $skip,
* $unwind, and $addFields.
*
* The class can handle both encrypted and non-encrypted data collections.
*/
class Aggregation {
constructor(collectionName, path, Pipeline, isEncrypted = false, encryptionKey) {
// property to store the data
this.AllData = [];
this.collectionName = collectionName;
this.path = path;
this.isEncrypted = isEncrypted;
this.encryptionKey = encryptionKey;
this.AllData = [];
this.Pipeline = Pipeline;
this.ResponseHelper = new response_helper_1.default();
this.Converter = new Converter_helper_1.default();
this.cryptoInstance = new Crypto_helper_1.CryptoHelper(this.encryptionKey);
}
/**
* Executes the aggregation pipeline on the data.
*
* This method processes the aggregation pipeline stages in sequence:
* - $match: Filters documents based on specified conditions
* - $group: Groups documents by specified fields and applies aggregation operations
* - $sort: Sorts documents based on specified fields and order
* - $project: Reshapes documents by including specified fields
* - $limit: Limits the number of documents in the result
* - $skip: Skips a specified number of documents
* - $unwind: Deconstructs an array field from input documents
* - $addFields: Adds new fields to documents
*
* The method first validates if the pipeline is an array, loads all buffer data,
* and then processes each stage of the pipeline sequentially.
*
* @throws {Error} If the pipeline is not an array
* @returns {Array<any>} The result of the aggregation pipeline
*/
exec() {
return __awaiter(this, void 0, void 0, function* () {
if (!Array.isArray(this.Pipeline)) {
throw new Error("Pipeline must be an array of aggregation stages.");
}
// Load all buffer raw data from the specified directory
yield this.LoadAllBufferRawData().then((response) => {
if ("data" in response) {
outers_1.Console.green("Data Loaded Successfully for Aggregation");
}
});
let result = [...this.AllData];
for (const stage of this.Pipeline) {
if (stage.$match) {
result = result.filter((item) => {
return Object.entries(stage.$match).every(([key, value]) => {
var _a;
const itemValue = (_a = item[key]) !== null && _a !== void 0 ? _a : ""; // Ensure item[key] exists
if (typeof value === "string" ||
typeof value === "number" ||
typeof value === "boolean") {
return itemValue === value;
}
if (typeof value === "object" && value !== null) {
if (value instanceof RegExp) {
return value.test(itemValue);
}
if ("$regex" in value) {
const regexPattern = value.$regex;
const regexOptions = "$options" in value ? value.$options : "";
try {
const regex = new RegExp(String(regexPattern), regexOptions);
return regex.test(itemValue);
}
catch (error) {
outers_1.Console.red(`Invalid regex: ${regexPattern} with options: ${regexOptions}`, error);
return false;
}
}
}
return false;
});
});
}
if (stage.$group) {
const groupedData = {};
for (const item of result) {
let groupKey;
if (typeof stage.$group._id === "string") {
groupKey = stage.$group._id.startsWith("$")
? item[stage.$group._id.substring(1)]
: stage.$group._id;
}
else if (typeof stage.$group._id === "object") {
groupKey = JSON.stringify(Object.entries(stage.$group._id).reduce((acc, [k, v]) => {
const fieldPath = v.replace("$", "");
acc[k] = item[fieldPath];
return acc;
}, {}));
}
else {
groupKey = "null";
}
if (!groupedData[groupKey]) {
groupedData[groupKey] = { _id: groupKey };
}
for (const [key, operation] of Object.entries(stage.$group)) {
if (key === "_id")
continue;
if (operation.$avg) {
const field = operation.$avg.replace("$", "");
groupedData[groupKey][key] = groupedData[groupKey][key] || {
sum: 0,
count: 0,
};
groupedData[groupKey][key].sum += item[field];
groupedData[groupKey][key].count += 1;
}
if (operation.$sum) {
const field = operation.$sum.replace("$", "");
groupedData[groupKey][key] =
(groupedData[groupKey][key] || 0) + item[field];
}
}
}
result = Object.values(groupedData).map((group) => {
for (const key in group) {
if (group[key] && group[key].sum !== undefined) {
group[key] = group[key].sum / group[key].count;
}
}
return group;
});
}
if (stage.$sort) {
const [[key, order]] = Object.entries(stage.$sort);
const numOrder = Number(order);
result.sort((a, b) => {
if (a[key] < b[key])
return -numOrder;
if (a[key] > b[key])
return numOrder;
return 0;
});
}
if (stage.$project) {
result = result.map((item) => {
const projected = {};
for (const key in stage.$project) {
if (stage.$project[key] === 1) {
projected[key] = item[key];
}
}
return projected;
});
}
if (stage.$limit) {
result = result.slice(0, stage.$limit);
}
if (stage.$skip) {
result = result.slice(stage.$skip);
}
if (stage.$unwind) {
const field = stage.$unwind.replace("$", "");
result = result.flatMap((item) => {
return Array.isArray(item[field])
? item[field].map((value) => (Object.assign(Object.assign({}, item), { [field]: value })))
: [item];
});
}
if (stage.$addFields) {
result = result.map((item) => (Object.assign(Object.assign({}, item), stage.$addFields)));
}
}
return this.ResponseHelper.Success(result);
});
}
/**
* 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() {
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 = 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;
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 = 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;
// 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);
}
});
}
}
exports.default = Aggregation;
//# sourceMappingURL=Aggregation.Operation.js.map