@cheetah.js/orm
Version:
A simple ORM for Cheetah.js
189 lines (188 loc) • 7.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SqlConditionBuilder = void 0;
const value_object_1 = require("../common/value-object");
const utils_1 = require("../utils");
class SqlConditionBuilder {
constructor(entityStorage, applyJoinCallback, statements) {
this.entityStorage = entityStorage;
this.applyJoinCallback = applyJoinCallback;
this.statements = statements;
this.OPERATORS = ['$eq', '$ne', '$in', '$nin', '$like', '$gt', '$gte', '$lt', '$lte', '$and', '$or'];
this.lastKeyNotOperator = '';
}
build(condition, alias, model) {
const sqlParts = this.processConditions(condition, alias, model);
if (sqlParts.length === 0) {
return '';
}
return this.wrapWithLogicalOperator(sqlParts, 'AND');
}
processConditions(condition, alias, model) {
const sqlParts = [];
for (let [key, value] of Object.entries(condition)) {
const extractedValue = this.extractValueFromValueObject(value);
const conditionSql = this.processEntry(key, extractedValue, alias, model);
if (conditionSql) {
sqlParts.push(conditionSql);
}
}
return sqlParts;
}
processEntry(key, value, alias, model) {
this.trackLastNonOperatorKey(key, model);
const relationship = this.findRelationship(key, model);
if (relationship) {
return this.handleRelationship(relationship, value, alias);
}
if (this.isScalarValue(value)) {
return this.handleScalarValue(key, value, alias, model);
}
if (this.isArrayValue(key, value)) {
return this.buildInCondition(key, value, alias, model);
}
return this.handleObjectValue(key, value, alias, model);
}
handleRelationship(relationship, value, alias) {
const sql = this.applyJoinCallback(relationship, value, alias);
if (this.statements.strategy === 'joined') {
return sql;
}
return '';
}
handleScalarValue(key, value, alias, model) {
if (key === '$eq') {
return this.buildSimpleCondition(this.lastKeyNotOperator, value, alias, '=', model);
}
return this.buildSimpleCondition(key, value, alias, '=', model);
}
handleObjectValue(key, value, alias, model) {
if (this.isLogicalOperator(key)) {
return this.buildLogicalOperatorCondition(key, value, alias, model);
}
return this.buildOperatorConditions(key, value, alias, model);
}
buildLogicalOperatorCondition(key, value, alias, model) {
const conditions = value.map((cond) => this.build(cond, alias, model));
const operator = this.extractLogicalOperator(key);
return this.wrapWithLogicalOperator(conditions, operator);
}
buildOperatorConditions(key, value, alias, model) {
const parts = [];
for (const operator of this.OPERATORS) {
if (operator in value) {
const condition = this.buildOperatorCondition(key, operator, value[operator], alias, model);
parts.push(condition);
}
}
return parts.join(' AND ');
}
buildOperatorCondition(key, operator, value, alias, model) {
switch (operator) {
case '$eq':
return this.buildSimpleCondition(key, value, alias, '=', model);
case '$ne':
return this.buildSimpleCondition(key, value, alias, '!=', model);
case '$in':
return this.buildInCondition(key, value, alias, model);
case '$nin':
return this.buildNotInCondition(key, value, alias, model);
case '$like':
return this.buildLikeCondition(key, value, alias, model);
case '$gt':
return this.buildComparisonCondition(key, value, alias, '>', model);
case '$gte':
return this.buildComparisonCondition(key, value, alias, '>=', model);
case '$lt':
return this.buildComparisonCondition(key, value, alias, '<', model);
case '$lte':
return this.buildComparisonCondition(key, value, alias, '<=', model);
case '$and':
case '$or':
return this.buildNestedLogicalCondition(operator, value, alias, model);
default:
return '';
}
}
buildSimpleCondition(key, value, alias, operator, model) {
const column = this.resolveColumnName(key, model);
const formattedValue = this.formatValue(value);
return `${alias}.${column} ${operator} ${formattedValue}`;
}
buildInCondition(key, values, alias, model) {
const column = this.resolveColumnName(key, model);
const formattedValues = values.map(val => this.formatValue(val)).join(', ');
return `${alias}.${column} IN (${formattedValues})`;
}
buildNotInCondition(key, values, alias, model) {
const column = this.resolveColumnName(key, model);
const formattedValues = values.map(val => this.formatValue(val)).join(', ');
return `${alias}.${column} NOT IN (${formattedValues})`;
}
buildLikeCondition(key, value, alias, model) {
const column = this.resolveColumnName(key, model);
return `${alias}.${column} LIKE '${value}'`;
}
buildComparisonCondition(key, value, alias, operator, model) {
const column = this.resolveColumnName(key, model);
return `${alias}.${column} ${operator} ${value}`;
}
buildNestedLogicalCondition(operator, value, alias, model) {
const conditions = value.map((cond) => this.build(cond, alias, model));
const logicalOp = this.extractLogicalOperator(operator);
return this.wrapWithLogicalOperator(conditions, logicalOp);
}
wrapWithLogicalOperator(conditions, operator) {
return `(${conditions.join(` ${operator} `)})`;
}
extractValueFromValueObject(value) {
if ((0, utils_1.extendsFrom)(value_object_1.ValueObject, value?.constructor?.prototype)) {
return value.getValue();
}
return value;
}
formatValue(value) {
return (typeof value === 'string') ? `'${value}'` : value;
}
findRelationship(key, model) {
const entity = this.entityStorage.get(model);
return entity?.relations?.find(rel => rel.propertyKey === key);
}
isScalarValue(value) {
return typeof value !== 'object' || value === null;
}
isArrayValue(key, value) {
return !this.OPERATORS.includes(key) && Array.isArray(value);
}
isLogicalOperator(key) {
return ['$or', '$and'].includes(key);
}
extractLogicalOperator(key) {
return key.toUpperCase().replace('$', '');
}
trackLastNonOperatorKey(key, model) {
if (!this.OPERATORS.includes(key)) {
this.lastKeyNotOperator = key;
}
}
resolveColumnName(property, model) {
if (property.startsWith('$')) {
return property;
}
const entity = this.entityStorage.get(model);
if (!entity) {
return property;
}
const column = entity.properties?.[property]?.options.columnName;
if (column) {
return column;
}
return this.resolveRelationColumn(property, entity) ?? property;
}
resolveRelationColumn(property, entity) {
const relation = entity.relations?.find(rel => rel.propertyKey === property);
const column = relation?.columnName;
return typeof column === 'string' ? column : undefined;
}
}
exports.SqlConditionBuilder = SqlConditionBuilder;