@decaf-ts/core
Version:
Core persistence module for the decaf framework
202 lines • 9.03 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RamStatement = void 0;
const query_1 = require("./../query/index.cjs");
const db_decorators_1 = require("@decaf-ts/db-decorators");
const Statement_1 = require("./../query/Statement.cjs");
const decoration_1 = require("@decaf-ts/decoration");
const constants_1 = require("./../repository/constants.cjs");
/**
* @description RAM-specific query statement builder
* @summary Extends the base Statement class to provide query building functionality for the RAM adapter.
* This class translates high-level query operations into predicates that can filter and sort
* in-memory data structures.
* @template M - The model type being queried
* @template R - The result type returned by the query
* @param {RamAdapter} adapter - The RAM adapter instance to use for executing queries
* @class RamStatement
* @category Ram
* @example
* ```typescript
* // Create a statement for querying User models
* const statement = new RamStatement<User, User>(ramAdapter);
*
* // Build a query to find active users with age > 18
* const results = await statement
* .from(User)
* .where(Condition.and(
* Condition.eq('active', true),
* Condition.gt('age', 18)
* ))
* .orderBy('lastName', 'asc')
* .limit(10)
* .execute();
* ```
*/
class RamStatement extends Statement_1.Statement {
constructor(adapter, overrides) {
super(adapter, overrides);
}
/**
* @description Creates a sort comparator function
* @summary Generates a function that compares two model instances based on the orderBy criteria.
* This method handles different data types (string, number, date) and sort directions (asc, desc).
* @return {function(Model, Model): number} A comparator function for sorting model instances
*/
getSort() {
return (el1, el2) => {
if (!this.orderBySelector)
throw new db_decorators_1.InternalError("orderBySelector not set. Should be impossible");
const selector = this.orderBySelector;
const [key, direction] = selector;
const normalizedDirection = String(direction).toLowerCase();
const directionFactor = normalizedDirection === constants_1.OrderDirection.ASC ? 1 : -1;
const value1 = el1[key];
const value2 = el2[key];
if (value1 === value2)
return 0;
if (value1 == null || value2 == null)
return directionFactor * (value1 == null ? 1 : -1);
const { designType: type } = decoration_1.Metadata.getPropDesignTypes(el1.constructor, key);
const resolvedType = (type && type.name && type.name.toLowerCase()) || typeof value1;
switch (resolvedType) {
case "string":
return (directionFactor *
this.compareStrings(value1, value2));
case "number":
return (directionFactor *
this.compareNumbers(value1, value2));
case "bigint":
return (directionFactor *
this.compareBigInts(value1, value2));
case "boolean":
return (directionFactor *
this.compareBooleans(value1, value2));
case "date":
case "object":
if (value1 instanceof Date && value2 instanceof Date) {
return (directionFactor *
this.compareDates(value1, value2));
}
break;
default:
break;
}
throw new query_1.QueryError(`sorting not supported for type ${resolvedType}`);
};
}
compareBooleans(a, b) {
return a === b ? 0 : a ? 1 : -1;
}
compareNumbers(a, b) {
return a - b;
}
compareBigInts(a, b) {
if (a === b)
return 0;
return a > b ? 1 : -1;
}
compareStrings(a, b) {
return a.localeCompare(b);
}
compareDates(a, b) {
return a.valueOf() - b.valueOf();
}
/**
* @description Builds a RAM query from the statement
* @summary Converts the statement's selectors and conditions into a RawRamQuery object
* that can be executed by the RAM adapter. This method assembles all query components
* (select, from, where, limit, offset, sort) into the final query structure.
* @return {RawRamQuery<M>} The constructed RAM query object
*/
build() {
const result = {
select: this.selectSelector,
from: this.fromSelector,
where: this.whereCondition
? this.parseCondition(this.whereCondition).where
: // eslint-disable-next-line @typescript-eslint/no-unused-vars
(el) => {
return true;
},
limit: this.limitSelector,
skip: this.offsetSelector,
};
if (this.orderBySelector)
result.sort = this.getSort();
return result;
}
/**
* @description Parses a condition into a RAM query predicate
* @summary Converts a Condition object into a predicate function that can be used
* to filter model instances in memory. This method handles both simple conditions
* (equals, greater than, etc.) and complex conditions with logical operators (AND, OR).
* @template M - The model type for the condition
* @param {Condition<M>} condition - The condition to parse
* @return {RawRamQuery<M>} A RAM query object with a where predicate function
* @mermaid
* sequenceDiagram
* participant Caller
* participant RamStatement
* participant SimpleCondition
* participant ComplexCondition
*
* Caller->>RamStatement: parseCondition(condition)
* alt Simple condition (eq, gt, lt, etc.)
* RamStatement->>SimpleCondition: Extract attr1, operator, comparison
* SimpleCondition-->>RamStatement: Return predicate function
* else Logical operator (AND, OR)
* RamStatement->>ComplexCondition: Extract nested conditions
* RamStatement->>RamStatement: parseCondition(leftCondition)
* RamStatement->>RamStatement: parseCondition(rightCondition)
* ComplexCondition-->>RamStatement: Combine predicates with logical operator
* end
* RamStatement-->>Caller: Return query with where predicate
*/
parseCondition(condition) {
return {
where: (m) => {
const { attr1, operator, comparison } = condition;
if ([query_1.GroupOperator.AND, query_1.GroupOperator.OR, query_1.Operator.NOT].indexOf(operator) === -1) {
switch (operator) {
case query_1.Operator.BIGGER:
return m[attr1] > comparison;
case query_1.Operator.BIGGER_EQ:
return m[attr1] >= comparison;
case query_1.Operator.DIFFERENT:
return m[attr1] !== comparison;
case query_1.Operator.EQUAL:
return m[attr1] === comparison;
case query_1.Operator.REGEXP:
if (typeof m[attr1] !== "string")
throw new query_1.QueryError(`Invalid regexp comparison on a non string attribute: ${m[attr1]}`);
return !!m[attr1].match(new RegExp(comparison, "g"));
case query_1.Operator.SMALLER:
return m[attr1] < comparison;
case query_1.Operator.SMALLER_EQ:
return m[attr1] <= comparison;
default:
throw new db_decorators_1.InternalError(`Invalid operator for standard comparisons: ${operator}`);
}
}
else if (operator === query_1.Operator.NOT) {
throw new db_decorators_1.InternalError("Not implemented");
}
else {
const op1 = this.parseCondition(attr1);
const op2 = this.parseCondition(comparison);
switch (operator) {
case query_1.GroupOperator.AND:
return op1.where(m) && op2.where(m);
case query_1.GroupOperator.OR:
return op1.where(m) || op2.where(m);
default:
throw new db_decorators_1.InternalError(`Invalid operator for And/Or comparisons: ${operator}`);
}
}
},
};
}
}
exports.RamStatement = RamStatement;
//# sourceMappingURL=RamStatement.js.map