@inixiative/json-rules 3.7.0 → 3.9.0
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 +54 -9
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -5
- package/dist/index.d.ts +14 -5
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -703,7 +703,7 @@ engine's `sql.dialect` — `SqlDialect.postgres` by default, `SqlDialect.mysql`
|
|
|
703
703
|
are not a second `toSql`: `toSql` and `toPrisma` are unchanged.
|
|
704
704
|
|
|
705
705
|
```ts
|
|
706
|
-
import { engineGlobals, Operator, SqlDialect, toLensWhereSql, toWhereSql } from '@inixiative/json-rules';
|
|
706
|
+
import { ArrayOperator, engineGlobals, Operator, SqlDialect, toLensWhereSql, toWhereSql } from '@inixiative/json-rules';
|
|
707
707
|
|
|
708
708
|
engineGlobals.set('sql.dialect', SqlDialect.mysql);
|
|
709
709
|
|
|
@@ -713,17 +713,48 @@ toWhereSql(
|
|
|
713
713
|
);
|
|
714
714
|
// => "${fan_user.affiliation} IN ('advocate', 'consultant')"
|
|
715
715
|
|
|
716
|
-
|
|
716
|
+
toWhereSql(
|
|
717
|
+
{
|
|
718
|
+
field: 'affiliations',
|
|
719
|
+
arrayOperator: ArrayOperator.any,
|
|
720
|
+
condition: { field: 'affiliation', operator: Operator.equals, value: 'advocate' },
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
columns: {
|
|
724
|
+
uuid: '${fan_users.uuid}',
|
|
725
|
+
'affiliations.fanUserUuid': 'a.fanUserUuid',
|
|
726
|
+
'affiliations.affiliation': 'a.affiliation',
|
|
727
|
+
},
|
|
728
|
+
tables: { affiliations: 'FanUserAffiliation AS a' },
|
|
729
|
+
map,
|
|
730
|
+
mapName: 'prisma',
|
|
731
|
+
model: 'FanUser',
|
|
732
|
+
},
|
|
733
|
+
);
|
|
734
|
+
// => "EXISTS (SELECT 1 FROM FanUserAffiliation AS a WHERE a.fanUserUuid = ${fan_users.uuid} AND (a.affiliation = 'advocate'))"
|
|
735
|
+
|
|
736
|
+
toLensWhereSql(lens, { columns, tables });
|
|
717
737
|
// => { root: '…' | null, relations: {}, mapDefaults: { prisma: { models: { BrandReferenceRequestFanUser: "${brand_reference_request_fan_user.affiliation} = 'advocate'" } } } }
|
|
718
738
|
```
|
|
719
739
|
|
|
720
740
|
- **Operators:** `equals`, `notEquals`, `in`, `notIn`, `exists`, `notExists`, inside `all` / `any`
|
|
721
|
-
(and `true` / `false`)
|
|
722
|
-
|
|
723
|
-
`
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
741
|
+
(and `true` / `false`), and `any` / `all` / `none` / `empty` / `notEmpty` over a to-many relation
|
|
742
|
+
(below). Anything else — another operator, a date rule, a count or windowed array rule, an
|
|
743
|
+
aggregate, `if` / `then` / `else` — throws, naming it. So do binds, `path` (column-to-column),
|
|
744
|
+
offsets, `coerceType`, case-insensitivity, a path across a bridge, a field rule through a to-many
|
|
745
|
+
relation, anything Json (a stored JSON null is a value to SQL and null to `check()`), and a
|
|
746
|
+
DateTime or list *value*. A DateTime's or list's existence compiles: `deletedAt notExists`, a
|
|
747
|
+
lens's soft-delete `where`, is `… IS NULL`. A rule with no `value` throws, as on every rail.
|
|
748
|
+
- **To-many relations:** an array rule on a to-many relation of the model is one subquery on the
|
|
749
|
+
relation's table, keyed to the row: `any` / `notEmpty` is `EXISTS`, `none` / `empty` is
|
|
750
|
+
`NOT EXISTS`, and `all` is `NOT EXISTS` of a row whose condition `IS NOT TRUE` — a NULL result
|
|
751
|
+
fails, as in `check()`. The map gives the keys (`relationKeys`; a composite key ANDs every pair):
|
|
752
|
+
the row's side reads `columns['<key>']`, the related side `columns['<relation>.<key>']`.
|
|
753
|
+
`tables['<relation>']` is the table, written after `FROM` as given, with the alias its columns
|
|
754
|
+
read. The condition compiles as above and reads `columns['<relation>.<field>']`; a to-many
|
|
755
|
+
relation of the related rows nests (`tables['<relation>.<relation>']`). A relation behind
|
|
756
|
+
another one (`profile.affiliations`), a relation missing from `tables`, `atLeast` / `atMost` /
|
|
757
|
+
`exactly`, a window (`filter` / `orderBy` / `take` / `skip`) and a fieldless array rule throw.
|
|
727
758
|
- **Map:** `map` / `mapName` / `model` are required. A value is checked against its column's
|
|
728
759
|
kind (`leafFitViolations`, as `validateNarrowing` checks it) and an undeclared path is refused —
|
|
729
760
|
MySQL casts a mistyped literal instead of erroring, so `score = TRUE` would match 1 where
|
|
@@ -743,7 +774,7 @@ toLensWhereSql(lens, { columns });
|
|
|
743
774
|
exactly, so a `_ci` column (MySQL's default) also matches `'Advocate'` for `'advocate'`; compare a
|
|
744
775
|
binary-collated column where that matters.
|
|
745
776
|
|
|
746
|
-
`toLensWhereSql(lens, { columns })` mirrors the lens: the root path's `where`s under `root` (null
|
|
777
|
+
`toLensWhereSql(lens, { columns, tables? })` mirrors the lens: the root path's `where`s under `root` (null
|
|
747
778
|
when it has none), each relation path's own under `relations`, keyed by the path, and each model
|
|
748
779
|
default's (`mapDefaults[map].models[model].where`) under `mapDefaults`, keyed the same way — one
|
|
749
780
|
predicate per model however many paths reach it. Each is the `where` as its layer wrote it: a
|
|
@@ -755,6 +786,20 @@ reports every map's defaults; a caller writing one database reads its own map's.
|
|
|
755
786
|
(`BrandReferenceRequestFanUser.affiliation`). It does not compile through `toSql`'s `{ lens }`
|
|
756
787
|
path, which would fold the lens's `where`s into the predicate a second time.
|
|
757
788
|
|
|
789
|
+
An array rule on a to-many relation has no join to put that relation's clamps on, so its subquery
|
|
790
|
+
carries them: the relation path's own `where` and the related model's default, ANDed beside the
|
|
791
|
+
key (outside the `IS NOT TRUE` of an `all`, as `check()` filters the rows before it tests them).
|
|
792
|
+
It carries only the clamps of the layers above the layer that wrote the `where`, which is what
|
|
793
|
+
`check()` reads it through: a layer's clamps narrow the layers after it, never its own conditions.
|
|
794
|
+
A clamp in the same layer or a later one stays out, so a later layer can't widen what a `none` or
|
|
795
|
+
an `empty` admitted. Each clamp reads its own relations through the layers above its own. The
|
|
796
|
+
clamps are still reported at their own path and model. `tables` reads like `columns`: by the
|
|
797
|
+
relation's path from the lens model in a path `where` (`affiliations`), by
|
|
798
|
+
`'<Model>.<relation>'` in a model default (`FanUser.affiliations`). An array rule on a relation
|
|
799
|
+
the lens hides throws, and its condition's fields are checked from the relation, not from the
|
|
800
|
+
model it sits on. A model default compiles to one predicate for every path that reaches its
|
|
801
|
+
model, so it throws when a to-many relation it reads carries different clamps on two of them.
|
|
802
|
+
|
|
758
803
|
**Absent, not hidden.** A row a relation's `where` filters out is absent from the SQL join, so a
|
|
759
804
|
negation through that relation holds for it; `check()` under the lens treats the row as hidden.
|
|
760
805
|
The lens is a projection: the difference is accepted and documented.
|