@simtlix/simfinity-js 2.6.3 → 2.6.4

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.
Files changed (3) hide show
  1. package/README.md +23 -3
  2. package/package.json +1 -1
  3. package/src/index.js +21 -1
package/README.md CHANGED
@@ -352,6 +352,25 @@ query {
352
352
  }
353
353
  ```
354
354
 
355
+ As an equivalent shorthand, you can pass the full dotted path in `field` and omit `path`:
356
+
357
+ ```graphql
358
+ query {
359
+ books(
360
+ OR: [
361
+ { conditions: [{ field: "author.name", operator: LIKE, value: "Adams" }] }
362
+ { conditions: [{ field: "author.name", operator: LIKE, value: "Pratchett" }] }
363
+ ]
364
+ ) {
365
+ id
366
+ title
367
+ author { name }
368
+ }
369
+ }
370
+ ```
371
+
372
+ Both forms produce the same query. Multi-segment paths work too — e.g. `field: "author.country.code"` is equivalent to `field: "author", path: "country.code"`. Supplying both a dotted `field` and a separate `path` is rejected as ambiguous.
373
+
355
374
  #### Mixing Flat Filters with AND/OR
356
375
 
357
376
  You can freely combine the existing flat filter syntax with AND/OR groups. Flat filters and AND groups are all ANDed together at the top level:
@@ -439,7 +458,8 @@ query {
439
458
  AND: [
440
459
  { conditions: [{ field: "year", operator: GTE, value: 2022 }] }
441
460
  { conditions: [
442
- { field: "episodes", path: "name", operator: LIKE, value: "Final" }
461
+ # Equivalent to: { field: "episodes", path: "name", ... }
462
+ { field: "episodes.name", operator: LIKE, value: "Final" }
443
463
  ] }
444
464
  ]
445
465
  }
@@ -462,8 +482,8 @@ This translates to: `number > 1 AND (year = 2020 OR (year >= 2022 AND episodes.n
462
482
  | `QLFilterGroup` | `AND: [QLFilterGroup]`, `OR: [QLFilterGroup]`, `conditions: [QLFilterCondition]` | Recursive logical group |
463
483
  | `QLFilterCondition` | `field: String!`, `operator: QLOperator`, `value: QLValue`, `path: String` | Individual filter condition |
464
484
 
465
- - `field` identifies the entity field by name (e.g., `"title"`, `"author"`)
466
- - `path` is required for object/relationship fields (e.g., `"name"`, `"country.name"`)
485
+ - `field` identifies the entity field by name (e.g., `"title"`, `"author"`). For object/relationship fields you can also pass the full dotted path here as a shorthand (e.g., `"author.name"`, `"author.country.name"`)
486
+ - `path` is required for object/relationship fields when `field` is just the top-level name (e.g., `"name"`, `"country.name"`). Omit `path` if you used the dotted-`field` shorthand. Supplying both is an error.
467
487
  - Multiple `conditions` in the same group are combined with AND
468
488
  - Maximum nesting depth: 5 levels
469
489
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simtlix/simfinity-js",
3
- "version": "2.6.3",
3
+ "version": "2.6.4",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
package/src/index.js CHANGED
@@ -316,6 +316,25 @@ const assertValidFilterPath = (path) => {
316
316
  }
317
317
  };
318
318
 
319
+ const normalizeFilterCondition = (condition) => {
320
+ const { field } = condition;
321
+ if (typeof field !== 'string') return condition;
322
+ const dotIdx = field.indexOf('.');
323
+ if (dotIdx < 0) return condition;
324
+ if (condition.path != null && condition.path !== '') {
325
+ throw new SimfinityError(
326
+ 'Filter condition cannot use both dotted field syntax and a separate path',
327
+ 'AMBIGUOUS_FILTER_FIELD',
328
+ 400,
329
+ );
330
+ }
331
+ return {
332
+ ...condition,
333
+ field: field.slice(0, dotIdx),
334
+ path: field.slice(dotIdx + 1),
335
+ };
336
+ };
337
+
319
338
  const OP_TO_MONGO = {
320
339
  LT: (v) => ({ $lt: v }),
321
340
  GT: (v) => ({ $gt: v }),
@@ -1397,7 +1416,8 @@ const buildFilterGroupMatch = async (filterGroup, gqltype, aggregateClauses, agg
1397
1416
  const fields = gqltype.getFields();
1398
1417
 
1399
1418
  if (filterGroup.conditions?.length > 0) {
1400
- for (const condition of filterGroup.conditions) {
1419
+ for (const rawCondition of filterGroup.conditions) {
1420
+ const condition = normalizeFilterCondition(rawCondition);
1401
1421
  const qlField = fields[condition.field];
1402
1422
  if (!qlField) {
1403
1423
  throw new SimfinityError(`Unknown filter field: ${condition.field}`, 'INVALID_FILTER_FIELD', 400);