@dwtechs/antity-pgsql 0.10.0 → 0.11.1
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 +55 -1
- package/dist/antity-pgsql.d.ts +2 -2
- package/dist/antity-pgsql.js +51 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -160,10 +160,14 @@ type MatchMode =
|
|
|
160
160
|
"st_dwithin";
|
|
161
161
|
|
|
162
162
|
|
|
163
|
+
type Filters = {
|
|
164
|
+
[key: string]: Filter | Filter[]; // Supports both simple (object) and complex (array) formats
|
|
165
|
+
}
|
|
166
|
+
|
|
163
167
|
type Filter = {
|
|
164
168
|
value: string | number | boolean | Date | number[];
|
|
165
|
-
subProps?: string[];
|
|
166
169
|
matchMode?: MatchMode;
|
|
170
|
+
operator?: string; // 'and' | 'or' - Used when multiple filters apply to the same property
|
|
167
171
|
}
|
|
168
172
|
|
|
169
173
|
type ExpressMiddleware = (req: Request, res: Response, next: NextFunction) => void;
|
|
@@ -278,6 +282,56 @@ Using substacks simplifies your route definitions and ensures consistent data pr
|
|
|
278
282
|
- **deleteArchive()**: Deletes archived rows that were archived before a specific date using a PostgreSQL SECURITY DEFINER function. Expects `req.body.date` to be a Date object.
|
|
279
283
|
- **getHistory()**: Retrieves modification history for rows from the `log.history` table. Expects `req.body.rows` to be an array of objects with `id` property. Returns all historical records for the specified entity IDs.
|
|
280
284
|
|
|
285
|
+
### Filters
|
|
286
|
+
|
|
287
|
+
Filters support two formats for maximum flexibility:
|
|
288
|
+
|
|
289
|
+
#### Simple Format (Single Filter per Property)
|
|
290
|
+
|
|
291
|
+
Backward-compatible format using a single filter object:
|
|
292
|
+
|
|
293
|
+
```javascript
|
|
294
|
+
const filters = {
|
|
295
|
+
name: { value: 'John', matchMode: 'contains' },
|
|
296
|
+
age: { value: 30, matchMode: 'equals' },
|
|
297
|
+
archived: { value: false, matchMode: 'equals' }
|
|
298
|
+
};
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
#### Complex Format (Multiple Filters per Property)
|
|
302
|
+
|
|
303
|
+
Array-based format supporting multiple filters with logical operators:
|
|
304
|
+
|
|
305
|
+
```javascript
|
|
306
|
+
const filters = {
|
|
307
|
+
// Multiple filters on the same property with OR operator
|
|
308
|
+
name: [
|
|
309
|
+
{ value: 'John', matchMode: 'contains', operator: 'or' },
|
|
310
|
+
{ value: 'Jane', matchMode: 'contains', operator: 'or' }
|
|
311
|
+
],
|
|
312
|
+
// Age range with AND operator
|
|
313
|
+
age: [
|
|
314
|
+
{ value: 18, matchMode: 'gte', operator: 'and' },
|
|
315
|
+
{ value: 65, matchMode: 'lte', operator: 'and' }
|
|
316
|
+
],
|
|
317
|
+
// Single filter in array format
|
|
318
|
+
archived: [{ value: false, matchMode: 'equals' }]
|
|
319
|
+
};
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
This generates SQL like:
|
|
323
|
+
```sql
|
|
324
|
+
WHERE (name LIKE '%John%' OR name LIKE '%Jane%')
|
|
325
|
+
AND (age >= 18 AND age <= 65)
|
|
326
|
+
AND archived = false
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
**Notes:**
|
|
330
|
+
- Both formats can be mixed in the same filters object
|
|
331
|
+
- When using arrays with a single filter, the operator is optional
|
|
332
|
+
- Default operator is 'AND' if not specified
|
|
333
|
+
- The operator field is case-insensitive
|
|
334
|
+
|
|
281
335
|
|
|
282
336
|
## Match modes
|
|
283
337
|
|
package/dist/antity-pgsql.d.ts
CHANGED
|
@@ -36,13 +36,13 @@ export type Filters = {
|
|
|
36
36
|
};
|
|
37
37
|
export type Filter = {
|
|
38
38
|
value: string | number | boolean | Date | number[];
|
|
39
|
-
subProps?: string[];
|
|
40
39
|
matchMode?: MatchMode;
|
|
40
|
+
operator?: string;
|
|
41
41
|
};
|
|
42
42
|
export type { Type };
|
|
43
43
|
|
|
44
44
|
export declare class Property extends BaseProperty {
|
|
45
|
-
|
|
45
|
+
isFilterable: boolean;
|
|
46
46
|
operations: Operation[];
|
|
47
47
|
constructor(
|
|
48
48
|
key: string,
|
package/dist/antity-pgsql.js
CHANGED
|
@@ -24,7 +24,7 @@ SOFTWARE.
|
|
|
24
24
|
https://github.com/DWTechs/Antity-pgsql.js
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
-
import { isArray,
|
|
27
|
+
import { isArray, isString, isIn } from '@dwtechs/checkard';
|
|
28
28
|
import { deleteProps, chunk, flatten } from '@dwtechs/sparray';
|
|
29
29
|
import { log } from '@dwtechs/winstan';
|
|
30
30
|
import { Entity } from '@dwtechs/antity';
|
|
@@ -107,14 +107,6 @@ function quoteIfUppercase(word) {
|
|
|
107
107
|
function index(index, matchMode) {
|
|
108
108
|
const i = index.map((i) => `$${i}`);
|
|
109
109
|
switch (matchMode) {
|
|
110
|
-
case "startsWith":
|
|
111
|
-
return `${i}%`;
|
|
112
|
-
case "endsWith":
|
|
113
|
-
return `%${i}`;
|
|
114
|
-
case "contains":
|
|
115
|
-
return `%${i}%`;
|
|
116
|
-
case "notContains":
|
|
117
|
-
return `%${i}%`;
|
|
118
110
|
case "in":
|
|
119
111
|
return `(${i})`;
|
|
120
112
|
default:
|
|
@@ -159,21 +151,49 @@ function comparator(matchMode) {
|
|
|
159
151
|
}
|
|
160
152
|
}
|
|
161
153
|
|
|
154
|
+
function formatValue(value, matchMode) {
|
|
155
|
+
if (!isString(value))
|
|
156
|
+
return value;
|
|
157
|
+
switch (matchMode) {
|
|
158
|
+
case "startsWith":
|
|
159
|
+
return `${value}%`;
|
|
160
|
+
case "endsWith":
|
|
161
|
+
return `%${value}`;
|
|
162
|
+
case "contains":
|
|
163
|
+
case "notContains":
|
|
164
|
+
return `%${value}%`;
|
|
165
|
+
default:
|
|
166
|
+
return value;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
162
169
|
function add(filters) {
|
|
170
|
+
var _a, _b;
|
|
163
171
|
const conditions = [];
|
|
164
172
|
const args = [];
|
|
165
173
|
if (filters) {
|
|
166
174
|
let i = 1;
|
|
167
175
|
for (const k in filters) {
|
|
168
|
-
const
|
|
169
|
-
const
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
176
|
+
const filterValue = filters[k];
|
|
177
|
+
const filterArray = isArray(filterValue) ? filterValue : [filterValue];
|
|
178
|
+
const groupConditions = [];
|
|
179
|
+
for (const filter of filterArray) {
|
|
180
|
+
const { value, matchMode } = filter;
|
|
181
|
+
const indexes = isArray(value) ? value.map(() => i++) : [i++];
|
|
182
|
+
const cond = addOne(k, indexes, matchMode);
|
|
183
|
+
if (cond) {
|
|
184
|
+
groupConditions.push(cond);
|
|
185
|
+
if (isArray(value))
|
|
186
|
+
args.push(...value.map((v) => formatValue(v, matchMode)));
|
|
187
|
+
else
|
|
188
|
+
args.push(formatValue(value, matchMode));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (groupConditions.length > 0) {
|
|
192
|
+
const operator = ((_b = (_a = filterArray[0]) === null || _a === void 0 ? void 0 : _a.operator) === null || _b === void 0 ? void 0 : _b.toUpperCase()) || 'AND';
|
|
193
|
+
const combined = groupConditions.length > 1
|
|
194
|
+
? `(${groupConditions.join(` ${operator} `)})`
|
|
195
|
+
: groupConditions[0];
|
|
196
|
+
conditions.push(combined);
|
|
177
197
|
}
|
|
178
198
|
}
|
|
179
199
|
}
|
|
@@ -465,12 +485,20 @@ function cleanFilters(filters, properties) {
|
|
|
465
485
|
continue;
|
|
466
486
|
}
|
|
467
487
|
const type$1 = type(prop.type);
|
|
468
|
-
const
|
|
469
|
-
|
|
470
|
-
|
|
488
|
+
const filterValue = filters[k];
|
|
489
|
+
const filterArray = isArray(filterValue) ? filterValue : [filterValue];
|
|
490
|
+
const validFilters = filterArray.filter((f) => {
|
|
491
|
+
const { matchMode: matchMode$1 } = f;
|
|
492
|
+
if (!matchMode$1 || !matchMode(type$1, matchMode$1)) {
|
|
493
|
+
log.warn(`${LOGS_PREFIX}Filters: skipping invalid match mode: "${matchMode$1}" for type: "${type$1}" at property: "${k}"`);
|
|
494
|
+
return false;
|
|
495
|
+
}
|
|
496
|
+
return true;
|
|
497
|
+
});
|
|
498
|
+
if (!validFilters.length)
|
|
471
499
|
delete filters[k];
|
|
472
|
-
|
|
473
|
-
|
|
500
|
+
else
|
|
501
|
+
filters[k] = validFilters;
|
|
474
502
|
}
|
|
475
503
|
}
|
|
476
504
|
return filters;
|