@simtlix/simfinity-js 2.4.6 → 2.6.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/.claude/worktrees/agitated-kepler/.claude/settings.local.json +23 -0
- package/.claude/worktrees/agitated-kepler/AGGREGATION_CHANGES_SUMMARY.md +235 -0
- package/.claude/worktrees/agitated-kepler/AGGREGATION_EXAMPLE.md +567 -0
- package/.claude/worktrees/agitated-kepler/LICENSE +201 -0
- package/.claude/worktrees/agitated-kepler/README.md +3941 -0
- package/.claude/worktrees/agitated-kepler/eslint.config.mjs +71 -0
- package/.claude/worktrees/agitated-kepler/package-lock.json +4740 -0
- package/.claude/worktrees/agitated-kepler/package.json +41 -0
- package/.claude/worktrees/agitated-kepler/src/auth/errors.js +44 -0
- package/.claude/worktrees/agitated-kepler/src/auth/expressions.js +273 -0
- package/.claude/worktrees/agitated-kepler/src/auth/index.js +391 -0
- package/.claude/worktrees/agitated-kepler/src/auth/rules.js +274 -0
- package/.claude/worktrees/agitated-kepler/src/const/QLOperator.js +39 -0
- package/.claude/worktrees/agitated-kepler/src/const/QLSort.js +28 -0
- package/.claude/worktrees/agitated-kepler/src/const/QLValue.js +39 -0
- package/.claude/worktrees/agitated-kepler/src/errors/internal-server.error.js +11 -0
- package/.claude/worktrees/agitated-kepler/src/errors/simfinity.error.js +15 -0
- package/.claude/worktrees/agitated-kepler/src/index.js +2412 -0
- package/.claude/worktrees/agitated-kepler/src/plugins.js +53 -0
- package/.claude/worktrees/agitated-kepler/src/scalars.js +188 -0
- package/.claude/worktrees/agitated-kepler/src/validators.js +250 -0
- package/.claude/worktrees/agitated-kepler/yarn.lock +1154 -0
- package/.claude/worktrees/distracted-swirles-4601fe/.claude/settings.local.json +10 -0
- package/.claude/worktrees/distracted-swirles-4601fe/AGGREGATION_CHANGES_SUMMARY.md +235 -0
- package/.claude/worktrees/distracted-swirles-4601fe/AGGREGATION_EXAMPLE.md +567 -0
- package/.claude/worktrees/distracted-swirles-4601fe/LICENSE +201 -0
- package/.claude/worktrees/distracted-swirles-4601fe/README.md +3941 -0
- package/.claude/worktrees/distracted-swirles-4601fe/eslint.config.mjs +71 -0
- package/.claude/worktrees/distracted-swirles-4601fe/package-lock.json +4740 -0
- package/.claude/worktrees/distracted-swirles-4601fe/package.json +41 -0
- package/.claude/worktrees/distracted-swirles-4601fe/src/auth/errors.js +44 -0
- package/.claude/worktrees/distracted-swirles-4601fe/src/auth/expressions.js +273 -0
- package/.claude/worktrees/distracted-swirles-4601fe/src/auth/index.js +391 -0
- package/.claude/worktrees/distracted-swirles-4601fe/src/auth/rules.js +274 -0
- package/.claude/worktrees/distracted-swirles-4601fe/src/const/QLOperator.js +39 -0
- package/.claude/worktrees/distracted-swirles-4601fe/src/const/QLSort.js +28 -0
- package/.claude/worktrees/distracted-swirles-4601fe/src/const/QLValue.js +39 -0
- package/.claude/worktrees/distracted-swirles-4601fe/src/errors/internal-server.error.js +11 -0
- package/.claude/worktrees/distracted-swirles-4601fe/src/errors/simfinity.error.js +15 -0
- package/.claude/worktrees/distracted-swirles-4601fe/src/index.js +1925 -0
- package/.claude/worktrees/distracted-swirles-4601fe/src/plugins.js +53 -0
- package/.claude/worktrees/distracted-swirles-4601fe/src/scalars.js +188 -0
- package/.claude/worktrees/distracted-swirles-4601fe/src/validators.js +250 -0
- package/.claude/worktrees/distracted-swirles-4601fe/yarn.lock +1154 -0
- package/.cursor/rules/simfinity-core-functions.mdc +3 -1
- package/README.md +202 -0
- package/package.json +1 -1
- package/src/index.js +623 -896
- package/git-report.js +0 -224
|
@@ -55,11 +55,13 @@ Handles: scalars, enums, relation fields (`IdInputType` for refs, nested input f
|
|
|
55
55
|
`buildQuery(args, gqltype)` translates GraphQL filter arguments into a MongoDB aggregation pipeline:
|
|
56
56
|
|
|
57
57
|
- `buildQueryTerms()` -- converts scalar/enum filters to `$match`, object/relation filters to `$lookup` + `$unwind` + `$match`
|
|
58
|
+
- `buildFilterGroupMatch()` -- recursively processes `QLFilterGroup` (AND/OR) into nested MongoDB `$and`/`$or` match expressions. Reuses `buildQueryTerms()` for each leaf condition. Deduplicates `$lookup` stages via shared `aggregationsIncluded` dict. Max depth: 5.
|
|
58
59
|
- `buildAggregationsForSort()` -- adds `$lookup`/`$unwind` for sort on related fields
|
|
59
60
|
- Pagination: `$skip` + `$limit` from `QLPagination` input
|
|
60
61
|
- Sort: `$sort` from `QLSortExpression` input
|
|
62
|
+
- AND/OR: optional `AND`/`OR` arguments containing `QLFilterGroup` trees. Flat field filters and scope-injected filters are always ANDed at the top level (cannot be bypassed by user OR).
|
|
61
63
|
|
|
62
|
-
`buildAggregationQuery()` extends this with `$group` for aggregation operations (SUM, COUNT, AVG, MIN, MAX).
|
|
64
|
+
`buildAggregationQuery()` extends this with `$group` for aggregation operations (SUM, COUNT, AVG, MIN, MAX). Supports the same AND/OR filter pattern.
|
|
63
65
|
|
|
64
66
|
## Mutation Pipeline
|
|
65
67
|
|
package/README.md
CHANGED
|
@@ -265,6 +265,208 @@ query {
|
|
|
265
265
|
- `NIN` - Not in array
|
|
266
266
|
- `BTW` - Between two values
|
|
267
267
|
|
|
268
|
+
### Logical Filters (AND / OR)
|
|
269
|
+
|
|
270
|
+
By default, all field-level filters are combined with implicit AND logic. For complex conditions requiring OR logic or nested combinations, use the `AND` and `OR` query arguments.
|
|
271
|
+
|
|
272
|
+
#### Simple OR
|
|
273
|
+
|
|
274
|
+
Return books in either the Sci-Fi or Fantasy category:
|
|
275
|
+
|
|
276
|
+
```graphql
|
|
277
|
+
query {
|
|
278
|
+
books(
|
|
279
|
+
OR: [
|
|
280
|
+
{ conditions: [{ field: "category", operator: EQ, value: "Sci-Fi" }] }
|
|
281
|
+
{ conditions: [{ field: "category", operator: EQ, value: "Fantasy" }] }
|
|
282
|
+
]
|
|
283
|
+
) {
|
|
284
|
+
id
|
|
285
|
+
title
|
|
286
|
+
category
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
#### Flat Filters Combined with OR
|
|
292
|
+
|
|
293
|
+
Flat field filters are always ANDed at the top level, making them ideal for scope/security conditions that cannot be bypassed by user OR logic:
|
|
294
|
+
|
|
295
|
+
```graphql
|
|
296
|
+
query {
|
|
297
|
+
books(
|
|
298
|
+
rating: { operator: GTE, value: 7.0 }
|
|
299
|
+
OR: [
|
|
300
|
+
{ conditions: [{ field: "category", operator: EQ, value: "Sci-Fi" }] }
|
|
301
|
+
{ conditions: [{ field: "category", operator: EQ, value: "Fantasy" }] }
|
|
302
|
+
]
|
|
303
|
+
) {
|
|
304
|
+
id
|
|
305
|
+
title
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
This translates to: `rating >= 7.0 AND (category = "Sci-Fi" OR category = "Fantasy")`.
|
|
311
|
+
|
|
312
|
+
#### Nested AND inside OR
|
|
313
|
+
|
|
314
|
+
```graphql
|
|
315
|
+
query {
|
|
316
|
+
books(
|
|
317
|
+
OR: [
|
|
318
|
+
{ AND: [
|
|
319
|
+
{ conditions: [{ field: "rating", operator: GTE, value: 9.0 }] }
|
|
320
|
+
{ conditions: [{ field: "category", operator: EQ, value: "Sci-Fi" }] }
|
|
321
|
+
]}
|
|
322
|
+
{ AND: [
|
|
323
|
+
{ conditions: [{ field: "rating", operator: GTE, value: 8.0 }] }
|
|
324
|
+
{ conditions: [{ field: "category", operator: EQ, value: "Fantasy" }] }
|
|
325
|
+
]}
|
|
326
|
+
]
|
|
327
|
+
) {
|
|
328
|
+
id
|
|
329
|
+
title
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
This translates to: `(rating >= 9.0 AND category = "Sci-Fi") OR (rating >= 8.0 AND category = "Fantasy")`.
|
|
335
|
+
|
|
336
|
+
#### Filtering on Relationships within AND/OR
|
|
337
|
+
|
|
338
|
+
Use the `path` parameter to filter on related entity fields:
|
|
339
|
+
|
|
340
|
+
```graphql
|
|
341
|
+
query {
|
|
342
|
+
books(
|
|
343
|
+
OR: [
|
|
344
|
+
{ conditions: [{ field: "author", path: "name", operator: LIKE, value: "Adams" }] }
|
|
345
|
+
{ conditions: [{ field: "author", path: "name", operator: LIKE, value: "Pratchett" }] }
|
|
346
|
+
]
|
|
347
|
+
) {
|
|
348
|
+
id
|
|
349
|
+
title
|
|
350
|
+
author { name }
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
#### Mixing Flat Filters with AND/OR
|
|
356
|
+
|
|
357
|
+
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:
|
|
358
|
+
|
|
359
|
+
```graphql
|
|
360
|
+
query {
|
|
361
|
+
books(
|
|
362
|
+
rating: { operator: GTE, value: 7.0 }
|
|
363
|
+
author: { terms: [{ path: "country", operator: EQ, value: "UK" }] }
|
|
364
|
+
OR: [
|
|
365
|
+
{ conditions: [{ field: "category", operator: EQ, value: "Sci-Fi" }] }
|
|
366
|
+
{ conditions: [{ field: "category", operator: EQ, value: "Fantasy" }] }
|
|
367
|
+
]
|
|
368
|
+
) {
|
|
369
|
+
id
|
|
370
|
+
title
|
|
371
|
+
rating
|
|
372
|
+
category
|
|
373
|
+
author { name country }
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
This translates to: `rating >= 7.0 AND author.country = "UK" AND (category = "Sci-Fi" OR category = "Fantasy")`. The flat field filters (`rating`, `author`) use the existing syntax while the OR group uses the new `QLFilterGroup` syntax.
|
|
379
|
+
|
|
380
|
+
You can also combine flat filters with explicit AND groups for more complex logic:
|
|
381
|
+
|
|
382
|
+
```graphql
|
|
383
|
+
query {
|
|
384
|
+
books(
|
|
385
|
+
rating: { operator: GTE, value: 5.0 }
|
|
386
|
+
AND: [
|
|
387
|
+
{
|
|
388
|
+
OR: [
|
|
389
|
+
{ conditions: [{ field: "category", operator: EQ, value: "Sci-Fi" }] }
|
|
390
|
+
{ conditions: [{ field: "category", operator: EQ, value: "Fantasy" }] }
|
|
391
|
+
]
|
|
392
|
+
}
|
|
393
|
+
{
|
|
394
|
+
OR: [
|
|
395
|
+
{ conditions: [{ field: "author", path: "country", operator: EQ, value: "UK" }] }
|
|
396
|
+
{ conditions: [{ field: "author", path: "country", operator: EQ, value: "US" }] }
|
|
397
|
+
]
|
|
398
|
+
}
|
|
399
|
+
]
|
|
400
|
+
) {
|
|
401
|
+
id
|
|
402
|
+
title
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
This translates to: `rating >= 5.0 AND (category = "Sci-Fi" OR category = "Fantasy") AND (author.country = "UK" OR author.country = "US")`.
|
|
408
|
+
|
|
409
|
+
#### Collection Filtering with AND/OR
|
|
410
|
+
|
|
411
|
+
AND/OR filters are also available on collection fields (one-to-many relationships). The auto-generated resolvers for collection fields support the same `AND` and `OR` arguments:
|
|
412
|
+
|
|
413
|
+
```graphql
|
|
414
|
+
query {
|
|
415
|
+
series {
|
|
416
|
+
seasons(
|
|
417
|
+
OR: [
|
|
418
|
+
{ conditions: [{ field: "year", operator: EQ, value: 2020 }] }
|
|
419
|
+
{ conditions: [{ field: "year", operator: EQ, value: 2021 }] }
|
|
420
|
+
]
|
|
421
|
+
) {
|
|
422
|
+
number
|
|
423
|
+
year
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
You can mix flat collection filters with AND/OR:
|
|
430
|
+
|
|
431
|
+
```graphql
|
|
432
|
+
query {
|
|
433
|
+
series {
|
|
434
|
+
seasons(
|
|
435
|
+
number: { operator: GT, value: 1 }
|
|
436
|
+
OR: [
|
|
437
|
+
{ conditions: [{ field: "year", operator: EQ, value: 2020 }] }
|
|
438
|
+
{
|
|
439
|
+
AND: [
|
|
440
|
+
{ conditions: [{ field: "year", operator: GTE, value: 2022 }] }
|
|
441
|
+
{ conditions: [
|
|
442
|
+
{ field: "episodes", path: "name", operator: LIKE, value: "Final" }
|
|
443
|
+
] }
|
|
444
|
+
]
|
|
445
|
+
}
|
|
446
|
+
]
|
|
447
|
+
) {
|
|
448
|
+
number
|
|
449
|
+
year
|
|
450
|
+
episodes { name }
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
This translates to: `number > 1 AND (year = 2020 OR (year >= 2022 AND episodes.name LIKE "Final"))`.
|
|
457
|
+
|
|
458
|
+
#### Filter Types Reference
|
|
459
|
+
|
|
460
|
+
| Type | Fields | Description |
|
|
461
|
+
|------|--------|-------------|
|
|
462
|
+
| `QLFilterGroup` | `AND: [QLFilterGroup]`, `OR: [QLFilterGroup]`, `conditions: [QLFilterCondition]` | Recursive logical group |
|
|
463
|
+
| `QLFilterCondition` | `field: String!`, `operator: QLOperator`, `value: QLValue`, `path: String` | Individual filter condition |
|
|
464
|
+
|
|
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"`)
|
|
467
|
+
- Multiple `conditions` in the same group are combined with AND
|
|
468
|
+
- Maximum nesting depth: 5 levels
|
|
469
|
+
|
|
268
470
|
### Collection Field Filtering
|
|
269
471
|
|
|
270
472
|
Simfinity.js now supports filtering collection fields (one-to-many relationships) using the same powerful query format. This allows you to filter related objects directly within your GraphQL queries.
|