@cheetah.js/orm 0.1.143 → 0.1.144

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -180,6 +180,32 @@ export class User {
180
180
  }
181
181
  ```
182
182
 
183
+ You can also use the ORM filter syntax in `where` (with `$in`, `$or`, `$nor`, etc.):
184
+
185
+ ```javascript
186
+ @Entity()
187
+ @Index<{ User }>({
188
+ properties: ['email'],
189
+ where: {
190
+ isActive: true,
191
+ status: { $in: ['active', 'pending'] },
192
+ },
193
+ })
194
+ export class User {
195
+ @PrimaryKey()
196
+ id: number;
197
+
198
+ @Property()
199
+ email: string;
200
+
201
+ @Property()
202
+ isActive: boolean;
203
+
204
+ @Property()
205
+ status: string;
206
+ }
207
+ ```
208
+
183
209
  Note: MySQL does not support partial indexes; using `where` with the MySQL driver will throw.
184
210
 
185
211
  #### Property options
@@ -1,15 +1,17 @@
1
+ import { FilterQuery } from "../driver/driver.interface";
1
2
  export type IndexColumnMap<T> = {
2
3
  [K in keyof T as K extends symbol ? never : K]: string;
3
4
  };
4
5
  export type IndexPredicate<T> = string | ((columns: IndexColumnMap<T>) => string);
6
+ export type IndexWhere<T> = IndexPredicate<T> | FilterQuery<T>;
5
7
  export type IndexDefinition = {
6
8
  name: string;
7
9
  properties: string[];
8
- where?: IndexPredicate<any>;
10
+ where?: IndexWhere<any>;
9
11
  };
10
12
  type IndexOptions<T> = {
11
13
  properties: (keyof T)[];
12
- where?: IndexPredicate<T>;
14
+ where?: IndexWhere<T>;
13
15
  } | (keyof T)[] | undefined;
14
16
  export declare function Index<T>(options?: IndexOptions<T>): ClassDecorator & PropertyDecorator;
15
17
  export {};
@@ -14,6 +14,7 @@ exports.EntityStorage = void 0;
14
14
  const core_1 = require("@cheetah.js/core");
15
15
  const orm_session_context_1 = require("../orm-session-context");
16
16
  const utils_1 = require("../utils");
17
+ const index_condition_builder_1 = require("../query/index-condition-builder");
17
18
  function buildIndexColumnMap(properties, relations) {
18
19
  const map = mapPropertyColumns(properties);
19
20
  addRelationColumns(map, relations);
@@ -67,7 +68,14 @@ function resolveIndexWhere(where, columnMap) {
67
68
  if (typeof where === "string") {
68
69
  return where;
69
70
  }
70
- return where(columnMap);
71
+ if (typeof where === "function") {
72
+ return where(columnMap);
73
+ }
74
+ return buildIndexWhere(where, columnMap);
75
+ }
76
+ function buildIndexWhere(where, columnMap) {
77
+ const builder = new index_condition_builder_1.IndexConditionBuilder(columnMap);
78
+ return builder.build(where);
71
79
  }
72
80
  let EntityStorage = EntityStorage_1 = class EntityStorage {
73
81
  constructor() {
@@ -0,0 +1,41 @@
1
+ import { FilterQuery } from '../driver/driver.interface';
2
+ export declare class IndexConditionBuilder<T> {
3
+ private columnMap;
4
+ private readonly OPERATORS;
5
+ private lastKeyNotOperator;
6
+ constructor(columnMap: Record<string, string>);
7
+ build(condition: FilterQuery<T>): string;
8
+ private processConditions;
9
+ private getEntries;
10
+ private processEntries;
11
+ private processEntry;
12
+ private handleScalarValue;
13
+ private handleObjectValue;
14
+ private buildLogicalOperatorCondition;
15
+ private buildOperatorConditions;
16
+ private buildOperatorCondition;
17
+ private buildSimpleCondition;
18
+ private buildInCondition;
19
+ private buildNotInCondition;
20
+ private buildLikeCondition;
21
+ private buildComparisonCondition;
22
+ private buildNestedLogicalCondition;
23
+ private buildNorCondition;
24
+ private wrapWithLogicalOperator;
25
+ private extractValueFromValueObject;
26
+ private formatValue;
27
+ private formatDate;
28
+ private isNullish;
29
+ private isPrimitive;
30
+ private formatPrimitive;
31
+ private formatJson;
32
+ private escapeString;
33
+ private isScalarValue;
34
+ private isArrayValue;
35
+ private isLogicalOperator;
36
+ private isNorOperator;
37
+ private extractLogicalOperator;
38
+ private trackLastNonOperatorKey;
39
+ private resolveColumnName;
40
+ private buildNullCondition;
41
+ }
@@ -0,0 +1,235 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IndexConditionBuilder = void 0;
4
+ const value_object_1 = require("../common/value-object");
5
+ const utils_1 = require("../utils");
6
+ class IndexConditionBuilder {
7
+ constructor(columnMap) {
8
+ this.columnMap = columnMap;
9
+ this.OPERATORS = [
10
+ '$eq',
11
+ '$ne',
12
+ '$in',
13
+ '$nin',
14
+ '$like',
15
+ '$gt',
16
+ '$gte',
17
+ '$lt',
18
+ '$lte',
19
+ '$and',
20
+ '$or',
21
+ '$nor',
22
+ ];
23
+ this.lastKeyNotOperator = '';
24
+ }
25
+ build(condition) {
26
+ const sqlParts = this.processConditions(condition);
27
+ if (sqlParts.length === 0) {
28
+ return '';
29
+ }
30
+ return this.wrapWithLogicalOperator(sqlParts, 'AND');
31
+ }
32
+ processConditions(condition) {
33
+ const entries = this.getEntries(condition);
34
+ return this.processEntries(entries);
35
+ }
36
+ getEntries(condition) {
37
+ if (!condition || typeof condition !== 'object') {
38
+ return [];
39
+ }
40
+ return Object.entries(condition);
41
+ }
42
+ processEntries(entries) {
43
+ const sqlParts = [];
44
+ for (const [key, value] of entries) {
45
+ const extractedValue = this.extractValueFromValueObject(value);
46
+ const conditionSql = this.processEntry(key, extractedValue);
47
+ if (conditionSql) {
48
+ sqlParts.push(conditionSql);
49
+ }
50
+ }
51
+ return sqlParts;
52
+ }
53
+ processEntry(key, value) {
54
+ this.trackLastNonOperatorKey(key);
55
+ if (this.isScalarValue(value)) {
56
+ return this.handleScalarValue(key, value);
57
+ }
58
+ if (this.isArrayValue(key, value)) {
59
+ return this.buildInCondition(key, value);
60
+ }
61
+ return this.handleObjectValue(key, value);
62
+ }
63
+ handleScalarValue(key, value) {
64
+ if (key === '$eq') {
65
+ return this.buildSimpleCondition(this.lastKeyNotOperator, value, '=');
66
+ }
67
+ return this.buildSimpleCondition(key, value, '=');
68
+ }
69
+ handleObjectValue(key, value) {
70
+ if (this.isLogicalOperator(key)) {
71
+ return this.buildLogicalOperatorCondition(key, value);
72
+ }
73
+ if (this.isNorOperator(key)) {
74
+ return this.buildNorCondition(value);
75
+ }
76
+ return this.buildOperatorConditions(key, value);
77
+ }
78
+ buildLogicalOperatorCondition(key, value) {
79
+ const conditions = value.map((cond) => this.build(cond));
80
+ const operator = this.extractLogicalOperator(key);
81
+ return this.wrapWithLogicalOperator(conditions, operator);
82
+ }
83
+ buildOperatorConditions(key, value) {
84
+ const parts = [];
85
+ for (const operator of this.OPERATORS) {
86
+ if (operator in value) {
87
+ const condition = this.buildOperatorCondition(key, operator, value[operator]);
88
+ parts.push(condition);
89
+ }
90
+ }
91
+ return parts.join(' AND ');
92
+ }
93
+ buildOperatorCondition(key, operator, value) {
94
+ switch (operator) {
95
+ case '$eq':
96
+ return this.buildSimpleCondition(key, value, '=');
97
+ case '$ne':
98
+ return this.buildSimpleCondition(key, value, '!=');
99
+ case '$in':
100
+ return this.buildInCondition(key, value);
101
+ case '$nin':
102
+ return this.buildNotInCondition(key, value);
103
+ case '$like':
104
+ return this.buildLikeCondition(key, value);
105
+ case '$gt':
106
+ return this.buildComparisonCondition(key, value, '>');
107
+ case '$gte':
108
+ return this.buildComparisonCondition(key, value, '>=');
109
+ case '$lt':
110
+ return this.buildComparisonCondition(key, value, '<');
111
+ case '$lte':
112
+ return this.buildComparisonCondition(key, value, '<=');
113
+ case '$and':
114
+ case '$or':
115
+ return this.buildNestedLogicalCondition(operator, value);
116
+ case '$nor':
117
+ return this.buildNorCondition(value);
118
+ default:
119
+ return '';
120
+ }
121
+ }
122
+ buildSimpleCondition(key, value, operator) {
123
+ const column = this.resolveColumnName(key);
124
+ if (this.isNullish(value))
125
+ return this.buildNullCondition(column, operator);
126
+ const formattedValue = this.formatValue(value);
127
+ return `${column} ${operator} ${formattedValue}`;
128
+ }
129
+ buildInCondition(key, values) {
130
+ const column = this.resolveColumnName(key);
131
+ const formattedValues = values.map((val) => this.formatValue(val)).join(', ');
132
+ return `${column} IN (${formattedValues})`;
133
+ }
134
+ buildNotInCondition(key, values) {
135
+ const column = this.resolveColumnName(key);
136
+ const formattedValues = values.map((val) => this.formatValue(val)).join(', ');
137
+ return `${column} NOT IN (${formattedValues})`;
138
+ }
139
+ buildLikeCondition(key, value) {
140
+ const column = this.resolveColumnName(key);
141
+ return `${column} LIKE '${value}'`;
142
+ }
143
+ buildComparisonCondition(key, value, operator) {
144
+ const column = this.resolveColumnName(key);
145
+ if (this.isNullish(value))
146
+ return this.buildNullCondition(column, operator);
147
+ const formattedValue = this.formatValue(value);
148
+ return `${column} ${operator} ${formattedValue}`;
149
+ }
150
+ buildNestedLogicalCondition(operator, value) {
151
+ const conditions = value.map((cond) => this.build(cond));
152
+ const logicalOp = this.extractLogicalOperator(operator);
153
+ return this.wrapWithLogicalOperator(conditions, logicalOp);
154
+ }
155
+ buildNorCondition(value) {
156
+ const conditions = value.map((cond) => this.build(cond));
157
+ const wrapped = this.wrapWithLogicalOperator(conditions, 'OR');
158
+ return `NOT ${wrapped}`;
159
+ }
160
+ wrapWithLogicalOperator(conditions, operator) {
161
+ return `(${conditions.join(` ${operator} `)})`;
162
+ }
163
+ extractValueFromValueObject(value) {
164
+ if ((0, utils_1.extendsFrom)(value_object_1.ValueObject, value?.constructor?.prototype)) {
165
+ return value.getValue();
166
+ }
167
+ return value;
168
+ }
169
+ formatValue(value) {
170
+ if (value instanceof Date)
171
+ return this.formatDate(value);
172
+ if (this.isNullish(value))
173
+ return 'NULL';
174
+ if (this.isPrimitive(value))
175
+ return this.formatPrimitive(value);
176
+ return this.formatJson(value);
177
+ }
178
+ formatDate(value) {
179
+ return `'${value.toISOString()}'`;
180
+ }
181
+ isNullish(value) {
182
+ return value === null || value === undefined;
183
+ }
184
+ isPrimitive(value) {
185
+ return ['string', 'number', 'boolean', 'bigint'].includes(typeof value);
186
+ }
187
+ formatPrimitive(value) {
188
+ if (typeof value === 'string')
189
+ return `'${this.escapeString(value)}'`;
190
+ return `${value}`;
191
+ }
192
+ formatJson(value) {
193
+ return `'${this.escapeString(JSON.stringify(value))}'`;
194
+ }
195
+ escapeString(value) {
196
+ return value.replace(/'/g, "''");
197
+ }
198
+ isScalarValue(value) {
199
+ const isDate = value instanceof Date;
200
+ return typeof value !== 'object' || value === null || isDate;
201
+ }
202
+ isArrayValue(key, value) {
203
+ return !this.OPERATORS.includes(key) && Array.isArray(value);
204
+ }
205
+ isLogicalOperator(key) {
206
+ return ['$or', '$and'].includes(key);
207
+ }
208
+ isNorOperator(key) {
209
+ return key === '$nor';
210
+ }
211
+ extractLogicalOperator(key) {
212
+ return key.toUpperCase().replace('$', '');
213
+ }
214
+ trackLastNonOperatorKey(key) {
215
+ if (!this.OPERATORS.includes(key)) {
216
+ this.lastKeyNotOperator = key;
217
+ }
218
+ }
219
+ resolveColumnName(property) {
220
+ if (property.startsWith('$')) {
221
+ return property;
222
+ }
223
+ const column = this.columnMap[property];
224
+ if (column) {
225
+ return column;
226
+ }
227
+ return (0, utils_1.toSnakeCase)(property);
228
+ }
229
+ buildNullCondition(column, operator) {
230
+ if (operator === '!=' || operator === '<>')
231
+ return `${column} IS NOT NULL`;
232
+ return `${column} IS NULL`;
233
+ }
234
+ }
235
+ exports.IndexConditionBuilder = IndexConditionBuilder;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cheetah.js/orm",
3
- "version": "0.1.143",
3
+ "version": "0.1.144",
4
4
  "description": "A simple ORM for Cheetah.js.",
5
5
  "type": "commonjs",
6
6
  "main": "dist/index.js",
@@ -55,5 +55,5 @@
55
55
  "bun",
56
56
  "value-object"
57
57
  ],
58
- "gitHead": "698043086053a80b0e1fe53adb89ac494ef92f9f"
58
+ "gitHead": "73bccb890079ef872f5dcb9f9df1a1a9dfad5912"
59
59
  }