bigal 15.10.2 → 15.11.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/.devcontainer/devcontainer.json +1 -3
- package/.oxfmtrc.json +38 -0
- package/.oxlintrc.json +214 -0
- package/CHANGELOG.md +6 -0
- package/README.md +111 -0
- package/dist/index.cjs +102 -0
- package/dist/index.d.cts +7 -2
- package/dist/index.d.mts +7 -2
- package/dist/index.d.ts +7 -2
- package/dist/index.mjs +102 -0
- package/package.json +18 -15
- package/.prettierrc.cjs +0 -34
- package/eslint.config.mjs +0 -14
package/.oxfmtrc.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/nicolo-ribaudo/oxc/oxfmt-config-schema/npm/oxfmt/configuration_schema.json",
|
|
3
|
+
"printWidth": 200,
|
|
4
|
+
"semi": true,
|
|
5
|
+
"singleQuote": true,
|
|
6
|
+
"bracketSpacing": true,
|
|
7
|
+
"arrowParens": "always",
|
|
8
|
+
"quoteProps": "as-needed",
|
|
9
|
+
"trailingComma": "all",
|
|
10
|
+
"useTabs": false,
|
|
11
|
+
"tabWidth": 2,
|
|
12
|
+
"endOfLine": "lf",
|
|
13
|
+
"arrayWrap": { "minElementsToWrap": 3 },
|
|
14
|
+
"sortImports": {
|
|
15
|
+
"newlinesBetween": true,
|
|
16
|
+
"customGroups": [
|
|
17
|
+
{
|
|
18
|
+
"groupName": "path-alias",
|
|
19
|
+
"elementNamePattern": ["@/**"]
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"groups": [
|
|
23
|
+
["value-builtin", "type-builtin"],
|
|
24
|
+
{ "newlinesBetween": true },
|
|
25
|
+
["value-external", "type-external"],
|
|
26
|
+
{ "newlinesBetween": true },
|
|
27
|
+
["value-internal", "type-internal"],
|
|
28
|
+
{ "newlinesBetween": true },
|
|
29
|
+
["value-parent", "type-parent"],
|
|
30
|
+
{ "newlinesBetween": true },
|
|
31
|
+
["value-sibling", "type-sibling", "value-index", "type-index"],
|
|
32
|
+
{ "newlinesBetween": true },
|
|
33
|
+
"path-alias"
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
"sortPackageJson": false,
|
|
37
|
+
"ignorePatterns": ["**/node_modules/**", "**/dist/**"]
|
|
38
|
+
}
|
package/.oxlintrc.json
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/nicolo-ribaudo/oxc-project.github.io/compiled-schema/oxlint.schema.json",
|
|
3
|
+
"options": {
|
|
4
|
+
"typeAware": true
|
|
5
|
+
},
|
|
6
|
+
"plugins": ["node", "jsx-a11y", "jsdoc", "vitest", "import", "promise"],
|
|
7
|
+
"categories": {
|
|
8
|
+
"correctness": "error",
|
|
9
|
+
"suspicious": "error",
|
|
10
|
+
"pedantic": "off",
|
|
11
|
+
"style": "off",
|
|
12
|
+
"restriction": "off",
|
|
13
|
+
"nursery": "off"
|
|
14
|
+
},
|
|
15
|
+
"rules": {
|
|
16
|
+
"eqeqeq": ["error", "smart"],
|
|
17
|
+
"no-console": "error",
|
|
18
|
+
"no-nested-ternary": "error",
|
|
19
|
+
"no-await-in-loop": "error",
|
|
20
|
+
"id-length": ["error", { "min": 2, "exceptions": ["e", "i", "j", "q", "x", "y", "_", "A", "D", "K", "P", "T", "U"] }],
|
|
21
|
+
"no-void": "off",
|
|
22
|
+
"no-bitwise": "off",
|
|
23
|
+
|
|
24
|
+
"curly": ["error", "all"],
|
|
25
|
+
"no-var": "error",
|
|
26
|
+
"prefer-const": ["error", { "destructuring": "any", "ignoreReadBeforeAssign": true }],
|
|
27
|
+
"prefer-template": "error",
|
|
28
|
+
"no-else-return": ["error", { "allowElseIf": false }],
|
|
29
|
+
"no-return-assign": ["error", "always"],
|
|
30
|
+
"yoda": "error",
|
|
31
|
+
"no-lonely-if": "error",
|
|
32
|
+
"no-negated-condition": "error",
|
|
33
|
+
"no-self-compare": "error",
|
|
34
|
+
"no-useless-return": "error",
|
|
35
|
+
"no-promise-executor-return": "error",
|
|
36
|
+
"no-constructor-return": "error",
|
|
37
|
+
"no-new-wrappers": "error",
|
|
38
|
+
"no-template-curly-in-string": "error",
|
|
39
|
+
"no-useless-computed-key": "error",
|
|
40
|
+
"no-sequences": "error",
|
|
41
|
+
"no-proto": "error",
|
|
42
|
+
"no-multi-assign": "error",
|
|
43
|
+
"no-multi-str": "error",
|
|
44
|
+
"no-lone-blocks": "error",
|
|
45
|
+
"no-labels": ["error", { "allowLoop": false, "allowSwitch": false }],
|
|
46
|
+
"no-label-var": "error",
|
|
47
|
+
"no-extra-label": "error",
|
|
48
|
+
"no-new-func": "error",
|
|
49
|
+
"no-script-url": "error",
|
|
50
|
+
"max-classes-per-file": ["error", 1],
|
|
51
|
+
"array-callback-return": ["error", { "allowImplicit": true }],
|
|
52
|
+
"symbol-description": "error",
|
|
53
|
+
"default-case": ["error", { "commentPattern": "^no default$" }],
|
|
54
|
+
"default-case-last": "error",
|
|
55
|
+
"operator-assignment": ["error", "always"],
|
|
56
|
+
"prefer-numeric-literals": "error",
|
|
57
|
+
"prefer-object-spread": "error",
|
|
58
|
+
"prefer-promise-reject-errors": ["error", { "allowEmptyReject": true }],
|
|
59
|
+
"vars-on-top": "error",
|
|
60
|
+
"func-names": "error",
|
|
61
|
+
"func-style": ["error", "declaration"],
|
|
62
|
+
"grouped-accessor-pairs": "error",
|
|
63
|
+
"guard-for-in": "error",
|
|
64
|
+
"sort-imports": "off",
|
|
65
|
+
"no-array-constructor": "error",
|
|
66
|
+
"no-restricted-globals": ["error", { "name": "isFinite", "message": "Use Number.isFinite instead" }, { "name": "isNaN", "message": "Use Number.isNaN instead" }],
|
|
67
|
+
"no-use-before-define": ["error", { "functions": false, "classes": true, "variables": true }],
|
|
68
|
+
"no-empty-function": ["error", { "allow": ["arrowFunctions", "functions", "methods"] }],
|
|
69
|
+
"no-case-declarations": "error",
|
|
70
|
+
"no-empty": "error",
|
|
71
|
+
"no-fallthrough": "error",
|
|
72
|
+
"no-prototype-builtins": "error",
|
|
73
|
+
"no-redeclare": "error",
|
|
74
|
+
"no-regex-spaces": "error",
|
|
75
|
+
"no-unreachable": "error",
|
|
76
|
+
"prefer-rest-params": "error",
|
|
77
|
+
"prefer-spread": "error",
|
|
78
|
+
"getter-return": ["error", { "allowImplicit": true }],
|
|
79
|
+
"no-undef": "off",
|
|
80
|
+
|
|
81
|
+
"no-unneeded-ternary": ["error", { "defaultAssignment": false }],
|
|
82
|
+
|
|
83
|
+
"typescript/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
|
|
84
|
+
"typescript/consistent-type-imports": ["error", { "prefer": "type-imports" }],
|
|
85
|
+
"typescript/array-type": ["error", { "default": "array" }],
|
|
86
|
+
"typescript/explicit-function-return-type": "off",
|
|
87
|
+
"typescript/prefer-nullish-coalescing": "off",
|
|
88
|
+
"typescript/no-unsafe-argument": "off",
|
|
89
|
+
"typescript/no-unsafe-assignment": "off",
|
|
90
|
+
"typescript/no-unsafe-call": "off",
|
|
91
|
+
"typescript/no-unsafe-member-access": "off",
|
|
92
|
+
"typescript/no-unsafe-return": "off",
|
|
93
|
+
"typescript/no-misused-promises": "off",
|
|
94
|
+
"typescript/ban-ts-comment": ["error", { "minimumDescriptionLength": 10 }],
|
|
95
|
+
"typescript/only-throw-error": "error",
|
|
96
|
+
"typescript/return-await": "error",
|
|
97
|
+
"typescript/no-empty-interface": "error",
|
|
98
|
+
"typescript/parameter-properties": ["error", { "allow": ["readonly"] }],
|
|
99
|
+
"typescript/no-deprecated": "off",
|
|
100
|
+
"typescript/no-dynamic-delete": "error",
|
|
101
|
+
"typescript/no-empty-object-type": "error",
|
|
102
|
+
"typescript/no-extraneous-class": "error",
|
|
103
|
+
"typescript/no-invalid-void-type": "error",
|
|
104
|
+
"typescript/no-mixed-enums": "error",
|
|
105
|
+
"typescript/no-namespace": "error",
|
|
106
|
+
"typescript/no-non-null-asserted-nullish-coalescing": "error",
|
|
107
|
+
"typescript/no-require-imports": "error",
|
|
108
|
+
"typescript/no-unnecessary-condition": "error",
|
|
109
|
+
"typescript/no-unnecessary-template-expression": "error",
|
|
110
|
+
"typescript/no-unnecessary-type-arguments": "error",
|
|
111
|
+
"typescript/no-unnecessary-type-assertion": "error",
|
|
112
|
+
"typescript/no-unnecessary-type-constraint": "error",
|
|
113
|
+
"typescript/no-unnecessary-type-parameters": "error",
|
|
114
|
+
"typescript/no-unsafe-enum-comparison": "error",
|
|
115
|
+
"typescript/no-unsafe-function-type": "error",
|
|
116
|
+
"typescript/no-useless-default-assignment": "error",
|
|
117
|
+
"typescript/prefer-literal-enum-member": "error",
|
|
118
|
+
"typescript/prefer-promise-reject-errors": "error",
|
|
119
|
+
"typescript/prefer-reduce-type-parameter": "error",
|
|
120
|
+
"typescript/prefer-return-this-type": "error",
|
|
121
|
+
"typescript/related-getter-setter-pairs": "error",
|
|
122
|
+
"typescript/require-await": "error",
|
|
123
|
+
"typescript/restrict-plus-operands": "error",
|
|
124
|
+
"typescript/unified-signatures": "error",
|
|
125
|
+
"typescript/adjacent-overload-signatures": "error",
|
|
126
|
+
"typescript/ban-tslint-comment": "error",
|
|
127
|
+
"typescript/class-literal-property-style": "error",
|
|
128
|
+
"typescript/consistent-generic-constructors": "error",
|
|
129
|
+
"typescript/consistent-indexed-object-style": "error",
|
|
130
|
+
"typescript/consistent-type-assertions": "error",
|
|
131
|
+
"typescript/consistent-type-definitions": "error",
|
|
132
|
+
"typescript/no-confusing-non-null-assertion": "error",
|
|
133
|
+
"typescript/no-inferrable-types": "error",
|
|
134
|
+
"typescript/non-nullable-type-assertion-style": "error",
|
|
135
|
+
"typescript/prefer-find": "error",
|
|
136
|
+
"typescript/prefer-for-of": "error",
|
|
137
|
+
"typescript/prefer-function-type": "error",
|
|
138
|
+
"typescript/prefer-includes": "error",
|
|
139
|
+
"typescript/prefer-optional-chain": "error",
|
|
140
|
+
"typescript/prefer-regexp-exec": "error",
|
|
141
|
+
"typescript/prefer-string-starts-ends-with": "error",
|
|
142
|
+
"typescript/dot-notation": "error",
|
|
143
|
+
"typescript/no-unnecessary-boolean-literal-compare": "error",
|
|
144
|
+
|
|
145
|
+
"react/react-in-jsx-scope": "off",
|
|
146
|
+
"react/exhaustive-deps": "error",
|
|
147
|
+
"react/rules-of-hooks": "error",
|
|
148
|
+
"react/display-name": ["error", { "ignoreTranspilerName": false }],
|
|
149
|
+
"react/jsx-no-target-blank": "error",
|
|
150
|
+
"react/jsx-no-useless-fragment": "error",
|
|
151
|
+
"react/jsx-fragments": "error",
|
|
152
|
+
"react/jsx-pascal-case": ["error", { "allowAllCaps": true }],
|
|
153
|
+
"react/no-redundant-should-component-update": "error",
|
|
154
|
+
"react/self-closing-comp": "error",
|
|
155
|
+
"react/require-render-return": "error",
|
|
156
|
+
"react/jsx-no-script-url": "error",
|
|
157
|
+
"react/no-namespace": "error",
|
|
158
|
+
"react/style-prop-object": "error",
|
|
159
|
+
"react/iframe-missing-sandbox": "error",
|
|
160
|
+
|
|
161
|
+
"import/no-named-as-default": "off",
|
|
162
|
+
"import/no-named-as-default-member": "off",
|
|
163
|
+
"import/no-unassigned-import": "off",
|
|
164
|
+
"import/consistent-type-specifier-style": "off",
|
|
165
|
+
"import/first": "error",
|
|
166
|
+
"import/no-duplicates": "error",
|
|
167
|
+
|
|
168
|
+
"jsx-a11y/prefer-tag-over-role": "off",
|
|
169
|
+
"jsx-a11y/no-static-element-interactions": "off",
|
|
170
|
+
|
|
171
|
+
"unicorn/no-array-method-this-argument": "off",
|
|
172
|
+
"unicorn/prefer-array-find": "error",
|
|
173
|
+
"unicorn/prefer-set-has": "error",
|
|
174
|
+
"unicorn/prefer-node-protocol": "error",
|
|
175
|
+
"unicorn/prefer-object-from-entries": "error",
|
|
176
|
+
|
|
177
|
+
"promise/catch-or-return": ["error", { "allowThen": true }],
|
|
178
|
+
"promise/param-names": "error",
|
|
179
|
+
"promise/always-return": "error",
|
|
180
|
+
|
|
181
|
+
"node/no-new-require": "error",
|
|
182
|
+
"node/no-path-concat": "error",
|
|
183
|
+
"node/global-require": "error",
|
|
184
|
+
|
|
185
|
+
"jsdoc/require-param": ["error", { "ignoreWhenAllParamsMissing": true }],
|
|
186
|
+
"jsdoc/require-param-name": "error",
|
|
187
|
+
"jsdoc/require-param-type": "error",
|
|
188
|
+
|
|
189
|
+
"vitest/warn-todo": "off",
|
|
190
|
+
"vitest/consistent-test-it": ["error", { "fn": "it" }],
|
|
191
|
+
"vitest/no-duplicate-hooks": "error",
|
|
192
|
+
"vitest/no-identical-title": "error",
|
|
193
|
+
"vitest/no-import-node-test": "error",
|
|
194
|
+
"vitest/no-test-prefixes": "error",
|
|
195
|
+
"vitest/prefer-comparison-matcher": "error",
|
|
196
|
+
"vitest/prefer-equality-matcher": "error",
|
|
197
|
+
"vitest/prefer-hooks-in-order": "error",
|
|
198
|
+
"vitest/prefer-hooks-on-top": "error",
|
|
199
|
+
"vitest/prefer-lowercase-title": ["error", { "ignore": ["describe"] }],
|
|
200
|
+
"vitest/prefer-mock-promise-shorthand": "error",
|
|
201
|
+
"vitest/prefer-spy-on": "error",
|
|
202
|
+
"vitest/prefer-strict-equal": "error",
|
|
203
|
+
"vitest/require-top-level-describe": "error"
|
|
204
|
+
},
|
|
205
|
+
"overrides": [
|
|
206
|
+
{
|
|
207
|
+
"files": ["**/*.test.ts", "**/*.test.tsx"],
|
|
208
|
+
"rules": {
|
|
209
|
+
"id-length": "off"
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
],
|
|
213
|
+
"ignorePatterns": ["dist/**", "node_modules/**"]
|
|
214
|
+
}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
# [15.11.0](https://github.com/bigalorm/bigal/compare/v15.10.2...v15.11.0) (2026-03-10)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
- Add JSONB property querying in where clauses ([#335](https://github.com/bigalorm/bigal/issues/335)) ([c03c23e](https://github.com/bigalorm/bigal/commit/c03c23ea9e676ee5bfe5edc03506f90605849c85))
|
|
6
|
+
|
|
1
7
|
## [15.10.2](https://github.com/bigalorm/bigal/compare/v15.10.1...v15.10.2) (2026-02-27)
|
|
2
8
|
|
|
3
9
|
## [15.10.1](https://github.com/bigalorm/bigal/compare/v15.10.0...v15.10.1) (2026-02-07)
|
package/README.md
CHANGED
|
@@ -432,6 +432,117 @@ const items = await ProductRepository.find().where({
|
|
|
432
432
|
// SQL: SELECT ... FROM product WHERE deleted_at IS NOT NULL
|
|
433
433
|
```
|
|
434
434
|
|
|
435
|
+
#### JSON/JSONB column queries
|
|
436
|
+
|
|
437
|
+
BigAl supports querying individual properties within JSON/JSONB columns using PostgreSQL's `->>`
|
|
438
|
+
(text extraction) operator. Numeric and boolean values are automatically cast using `::numeric` and
|
|
439
|
+
`::boolean`.
|
|
440
|
+
|
|
441
|
+
**Property equality:**
|
|
442
|
+
|
|
443
|
+
```ts
|
|
444
|
+
const items = await SimpleWithJsonRepository.find().where({
|
|
445
|
+
bar: { theme: 'dark' },
|
|
446
|
+
});
|
|
447
|
+
// SQL: WHERE "bar"->>'theme'=$1
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
**Comparison operators on JSON properties:**
|
|
451
|
+
|
|
452
|
+
```ts
|
|
453
|
+
const items = await SimpleWithJsonRepository.find().where({
|
|
454
|
+
bar: { retryCount: { '>=': 3 } },
|
|
455
|
+
});
|
|
456
|
+
// SQL: WHERE ("bar"->>'retryCount')::numeric>=$1
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
**Boolean values:**
|
|
460
|
+
|
|
461
|
+
```ts
|
|
462
|
+
const items = await SimpleWithJsonRepository.find().where({
|
|
463
|
+
bar: { active: true },
|
|
464
|
+
});
|
|
465
|
+
// SQL: WHERE ("bar"->>'active')::boolean=$1
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
**Null checks** — a missing JSON property returns `NULL` from `->>`, so this matches both explicit
|
|
469
|
+
`null` values and absent properties:
|
|
470
|
+
|
|
471
|
+
```ts
|
|
472
|
+
const items = await SimpleWithJsonRepository.find().where({
|
|
473
|
+
bar: { deletedAt: null },
|
|
474
|
+
});
|
|
475
|
+
// SQL: WHERE "bar"->>'deletedAt' IS NULL
|
|
476
|
+
|
|
477
|
+
const items = await SimpleWithJsonRepository.find().where({
|
|
478
|
+
bar: { deletedAt: { '!': null } },
|
|
479
|
+
});
|
|
480
|
+
// SQL: WHERE "bar"->>'deletedAt' IS NOT NULL
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
**Negation:**
|
|
484
|
+
|
|
485
|
+
```ts
|
|
486
|
+
// Inline negation
|
|
487
|
+
const items = await SimpleWithJsonRepository.find().where({
|
|
488
|
+
bar: { status: { '!': 'archived' } },
|
|
489
|
+
});
|
|
490
|
+
// SQL: WHERE "bar"->>'status'<>$1
|
|
491
|
+
|
|
492
|
+
// Outer negation wrapping multiple properties
|
|
493
|
+
const items = await SimpleWithJsonRepository.find().where({
|
|
494
|
+
bar: { '!': { retryCount: { '>=': 5 } } },
|
|
495
|
+
});
|
|
496
|
+
// SQL: WHERE ("bar"->>'retryCount')::numeric<$1
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
**Array values (IN):**
|
|
500
|
+
|
|
501
|
+
```ts
|
|
502
|
+
const items = await SimpleWithJsonRepository.find().where({
|
|
503
|
+
bar: { stage: ['transcription', 'summarization'] },
|
|
504
|
+
});
|
|
505
|
+
// SQL: WHERE "bar"->>'stage'=ANY($1)
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
**Multiple properties (AND):**
|
|
509
|
+
|
|
510
|
+
```ts
|
|
511
|
+
const items = await SimpleWithJsonRepository.find().where({
|
|
512
|
+
bar: { retryCount: { '<': 3 }, stage: 'transcription' },
|
|
513
|
+
});
|
|
514
|
+
// SQL: WHERE ("bar"->>'retryCount')::numeric<$1 AND "bar"->>'stage'=$2
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
**Nested objects for deep path access** — intermediate segments use `->` and the final segment uses
|
|
518
|
+
`->>`:
|
|
519
|
+
|
|
520
|
+
```ts
|
|
521
|
+
const items = await SimpleWithJsonRepository.find().where({
|
|
522
|
+
bar: { failure: { stage: 'transcription' } },
|
|
523
|
+
});
|
|
524
|
+
// SQL: WHERE "bar"->'failure'->>'stage'=$1
|
|
525
|
+
|
|
526
|
+
const items = await SimpleWithJsonRepository.find().where({
|
|
527
|
+
bar: { a: { b: { c: 'value' } } },
|
|
528
|
+
});
|
|
529
|
+
// SQL: WHERE "bar"->'a'->'b'->>'c'=$1
|
|
530
|
+
|
|
531
|
+
const items = await SimpleWithJsonRepository.find().where({
|
|
532
|
+
bar: { stats: { retryCount: { '>=': 3 } } },
|
|
533
|
+
});
|
|
534
|
+
// SQL: WHERE ("bar"->'stats'->>'retryCount')::numeric>=$1
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
**Combined `contains` and property access:**
|
|
538
|
+
|
|
539
|
+
```ts
|
|
540
|
+
const items = await SimpleWithJsonRepository.find().where({
|
|
541
|
+
bar: { contains: { type: 'recovery' }, retryCount: { '<': 3 } },
|
|
542
|
+
});
|
|
543
|
+
// SQL: WHERE "bar"@>$1::jsonb AND ("bar"->>'retryCount')::numeric<$2
|
|
544
|
+
```
|
|
545
|
+
|
|
435
546
|
#### Example of an AND statement
|
|
436
547
|
|
|
437
548
|
```ts
|
package/dist/index.cjs
CHANGED
|
@@ -459,6 +459,7 @@ class SubqueryBuilder {
|
|
|
459
459
|
}
|
|
460
460
|
/**
|
|
461
461
|
* Group the subquery results by one or more columns.
|
|
462
|
+
* @param {(string & keyof T)[]} columns - Columns to group by
|
|
462
463
|
* @returns New SubqueryBuilder with the groupBy applied
|
|
463
464
|
* @example
|
|
464
465
|
* subquery(ProductRepository)
|
|
@@ -472,6 +473,7 @@ class SubqueryBuilder {
|
|
|
472
473
|
}
|
|
473
474
|
/**
|
|
474
475
|
* Filter groups based on aggregate values (used with groupBy).
|
|
476
|
+
* @param {HavingCondition} condition - Having condition to apply
|
|
475
477
|
* @returns New SubqueryBuilder with the having condition applied
|
|
476
478
|
* @example
|
|
477
479
|
* subquery(ProductRepository)
|
|
@@ -1737,6 +1739,21 @@ function buildWhere({
|
|
|
1737
1739
|
let subQueryComparer;
|
|
1738
1740
|
if (isComparer(key)) {
|
|
1739
1741
|
subQueryComparer = key;
|
|
1742
|
+
} else if (propertyName) {
|
|
1743
|
+
const parentColumn = model.columnsByPropertyName[propertyName];
|
|
1744
|
+
if (parentColumn?.type?.toLowerCase() === "json") {
|
|
1745
|
+
andValues.push(
|
|
1746
|
+
buildJsonPropertyClause({
|
|
1747
|
+
columnName: parentColumn.name,
|
|
1748
|
+
path: [key],
|
|
1749
|
+
isNegated,
|
|
1750
|
+
constraint: where,
|
|
1751
|
+
params
|
|
1752
|
+
})
|
|
1753
|
+
);
|
|
1754
|
+
continue;
|
|
1755
|
+
}
|
|
1756
|
+
propertyName = key;
|
|
1740
1757
|
} else {
|
|
1741
1758
|
propertyName = key;
|
|
1742
1759
|
}
|
|
@@ -2101,6 +2118,91 @@ function buildJsonContainmentStatement({ repositoriesByModelNameLowered, model,
|
|
|
2101
2118
|
}
|
|
2102
2119
|
});
|
|
2103
2120
|
}
|
|
2121
|
+
function getTypeCastSuffix(value) {
|
|
2122
|
+
if (typeof value === "number") {
|
|
2123
|
+
return "::numeric";
|
|
2124
|
+
}
|
|
2125
|
+
if (typeof value === "boolean") {
|
|
2126
|
+
return "::boolean";
|
|
2127
|
+
}
|
|
2128
|
+
return "";
|
|
2129
|
+
}
|
|
2130
|
+
function buildJsonAccessor(columnName, path) {
|
|
2131
|
+
let result = `"${columnName}"`;
|
|
2132
|
+
for (let i = 0; i < path.length; i++) {
|
|
2133
|
+
const arrow = i === path.length - 1 ? "->>" : "->";
|
|
2134
|
+
result += `${arrow}'${path[i]}'`;
|
|
2135
|
+
}
|
|
2136
|
+
return result;
|
|
2137
|
+
}
|
|
2138
|
+
function isJsonConstraintOperator(key) {
|
|
2139
|
+
return key === "!" || key === "<" || key === "<=" || key === ">" || key === ">=";
|
|
2140
|
+
}
|
|
2141
|
+
function buildJsonPropertyClause({
|
|
2142
|
+
columnName,
|
|
2143
|
+
path,
|
|
2144
|
+
isNegated,
|
|
2145
|
+
constraint,
|
|
2146
|
+
params
|
|
2147
|
+
}) {
|
|
2148
|
+
for (const segment of path) {
|
|
2149
|
+
assertValidSqlIdentifier(segment, `JSON property name "${segment}"`);
|
|
2150
|
+
}
|
|
2151
|
+
if (constraint === null) {
|
|
2152
|
+
return `${buildJsonAccessor(columnName, path)} ${isNegated ? "IS NOT" : "IS"} NULL`;
|
|
2153
|
+
}
|
|
2154
|
+
if (Array.isArray(constraint)) {
|
|
2155
|
+
const accessor2 = buildJsonAccessor(columnName, path);
|
|
2156
|
+
params.push(constraint);
|
|
2157
|
+
return `${accessor2}${isNegated ? "<>ALL" : "=ANY"}($${params.length})`;
|
|
2158
|
+
}
|
|
2159
|
+
if (typeof constraint === "object") {
|
|
2160
|
+
const entries = Object.entries(constraint);
|
|
2161
|
+
const firstKey = entries[0]?.[0];
|
|
2162
|
+
if (firstKey && isJsonConstraintOperator(firstKey)) {
|
|
2163
|
+
const accessor2 = buildJsonAccessor(columnName, path);
|
|
2164
|
+
const negatedComparisonOperators = { "<": ">=", "<=": ">", ">": "<=", ">=": "<" };
|
|
2165
|
+
const clauses2 = [];
|
|
2166
|
+
for (const [operator, operatorValue] of entries) {
|
|
2167
|
+
if (operator === "!") {
|
|
2168
|
+
if (operatorValue === null) {
|
|
2169
|
+
clauses2.push(`${accessor2} ${isNegated ? "IS" : "IS NOT"} NULL`);
|
|
2170
|
+
} else {
|
|
2171
|
+
params.push(operatorValue);
|
|
2172
|
+
const castSuffix2 = getTypeCastSuffix(operatorValue);
|
|
2173
|
+
const castAccessor2 = castSuffix2 ? `(${accessor2})${castSuffix2}` : accessor2;
|
|
2174
|
+
clauses2.push(`${castAccessor2}${isNegated ? "=" : "<>"}$${params.length}`);
|
|
2175
|
+
}
|
|
2176
|
+
} else if (operator === "<" || operator === "<=" || operator === ">" || operator === ">=") {
|
|
2177
|
+
params.push(operatorValue);
|
|
2178
|
+
const castSuffix2 = getTypeCastSuffix(operatorValue);
|
|
2179
|
+
const castAccessor2 = castSuffix2 ? `(${accessor2})${castSuffix2}` : accessor2;
|
|
2180
|
+
const effectiveOperator = isNegated ? negatedComparisonOperators[operator] : operator;
|
|
2181
|
+
clauses2.push(`${castAccessor2}${effectiveOperator}$${params.length}`);
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
return clauses2.join(" AND ");
|
|
2185
|
+
}
|
|
2186
|
+
const clauses = [];
|
|
2187
|
+
for (const [nestedKey, nestedValue] of entries) {
|
|
2188
|
+
clauses.push(
|
|
2189
|
+
buildJsonPropertyClause({
|
|
2190
|
+
columnName,
|
|
2191
|
+
path: [...path, nestedKey],
|
|
2192
|
+
isNegated,
|
|
2193
|
+
constraint: nestedValue,
|
|
2194
|
+
params
|
|
2195
|
+
})
|
|
2196
|
+
);
|
|
2197
|
+
}
|
|
2198
|
+
return clauses.join(" AND ");
|
|
2199
|
+
}
|
|
2200
|
+
const accessor = buildJsonAccessor(columnName, path);
|
|
2201
|
+
params.push(constraint);
|
|
2202
|
+
const castSuffix = getTypeCastSuffix(constraint);
|
|
2203
|
+
const castAccessor = castSuffix ? `(${accessor})${castSuffix}` : accessor;
|
|
2204
|
+
return `${castAccessor}${isNegated ? "<>" : "="}$${params.length}`;
|
|
2205
|
+
}
|
|
2104
2206
|
function buildLikeOperatorStatement({ repositoriesByModelNameLowered, model, propertyName, isNegated, value, params, joins }) {
|
|
2105
2207
|
return buildArrayOrSingleStatement({
|
|
2106
2208
|
repositoriesByModelNameLowered,
|
package/dist/index.d.cts
CHANGED
|
@@ -630,6 +630,7 @@ declare class SubqueryBuilder<T extends Entity, TColumns extends string = never>
|
|
|
630
630
|
select(columns: SelectItem<T>[]): SubqueryBuilder<T>;
|
|
631
631
|
/**
|
|
632
632
|
* Group the subquery results by one or more columns.
|
|
633
|
+
* @param {(string & keyof T)[]} columns - Columns to group by
|
|
633
634
|
* @returns New SubqueryBuilder with the groupBy applied
|
|
634
635
|
* @example
|
|
635
636
|
* subquery(ProductRepository)
|
|
@@ -639,6 +640,7 @@ declare class SubqueryBuilder<T extends Entity, TColumns extends string = never>
|
|
|
639
640
|
groupBy(columns: (string & keyof T)[]): SubqueryBuilder<T, TColumns>;
|
|
640
641
|
/**
|
|
641
642
|
* Filter groups based on aggregate values (used with groupBy).
|
|
643
|
+
* @param {HavingCondition} condition - Having condition to apply
|
|
642
644
|
* @returns New SubqueryBuilder with the having condition applied
|
|
643
645
|
* @example
|
|
644
646
|
* subquery(ProductRepository)
|
|
@@ -1153,7 +1155,10 @@ type ExcludeUndefined<T> = Exclude<T, undefined>;
|
|
|
1153
1155
|
type LiteralValues<TValue> = (TValue | null)[] | TValue | null;
|
|
1154
1156
|
type WhereClauseValue<TValue> = TValue extends NotEntityBrand | undefined ? Exclude<TValue, NotEntityBrand | undefined> : Extract<TValue, Entity> extends undefined ? LiteralValues<ExcludeUndefined<TValue>> : (ExcludeUndefined<Exclude<TValue, Entity>> | null)[] | (Pick<Extract<ExcludeUndefined<TValue>, Entity>, 'id'> | null)[] | ExcludeUndefined<Exclude<TValue, Entity>> | Pick<Extract<ExcludeUndefined<TValue>, Entity>, 'id'> | null;
|
|
1155
1157
|
type StringConstraint<TValue extends string> = Partial<Record<'contains' | 'endsWith' | 'like' | 'startsWith', LiteralValues<ExcludeUndefined<TValue>>>>;
|
|
1156
|
-
type
|
|
1158
|
+
type JsonPropertyValue = boolean | number | string | null;
|
|
1159
|
+
type JsonConstraint<TValue> = Partial<Record<'contains', ExcludeUndefined<TValue> | LiteralValues<ExcludeUndefined<TValue>>>> & {
|
|
1160
|
+
[key: string]: JsonPropertyValue | JsonPropertyValue[] | Partial<Record<'!' | '<' | '<=' | '>' | '>=', JsonPropertyValue>> | undefined;
|
|
1161
|
+
};
|
|
1157
1162
|
type NumberOrDateConstraint<TValue extends Date | number> = Partial<Record<'<' | '<=' | '>' | '>=', LiteralValues<ExcludeUndefined<TValue>>>>;
|
|
1158
1163
|
interface SubqueryInConstraint {
|
|
1159
1164
|
in: SubqueryBuilderLike;
|
|
@@ -1470,4 +1475,4 @@ interface InitializeOptions extends IConnection {
|
|
|
1470
1475
|
declare function initialize({ models, pool, readonlyPool, connections, expose }: InitializeOptions): Record<string, IReadonlyRepository<Entity> | IRepository<Entity>>;
|
|
1471
1476
|
|
|
1472
1477
|
export { ColumnBaseMetadata, ColumnCollectionMetadata, ColumnModelMetadata, ColumnTypeMetadata, Entity, ModelMetadata, QueryError, ReadonlyRepository, Repository, ScalarSubquery, SelectBuilder, SubqueryBuilder, column, createDateColumn, getMetadataStorage, initialize, isSubqueryJoin, primaryColumn, subquery, table, updateDateColumn, versionColumn };
|
|
1473
|
-
export type { AggregateBuilder, AggregateCallback, AnyJoinInfo, ClassLike, ColumnBaseMetadataOptions, ColumnCollectionMetadataOptions, ColumnMetadata, ColumnModelMetadataOptions, ColumnModifierMetadata, ColumnTypeMetadataOptions, Comparer, CountResult, CreateResult, CreateResultArray, CreateResultArrayJSON, CreateResultJSON, CreateUpdateOptions, CreateUpdateParams, DeleteOptions, DestroyResult, DestroyResultJSON, DestroyResultWithRecords, DoNotReturnRecords, EntityFieldValue, EntityPrimitiveOrId, EntityStatic, ExcludeEntityCollections, ExcludeFunctions, FindArgs, FindOneArgs, FindOneResult, FindOneResultJSON, FindQueryWithCount, FindQueryWithCountJSON, FindResult, FindResultJSON, FindWithCountResult, GetValueType, HavingComparer, HavingCondition, IConnection, IReadonlyRepository, IRepository, IRepositoryOptions, IncludeFunctions, InitializeOptions, IsValueOfType, JoinDefinition, JoinInfo, JoinType, JoinedSort, JoinedWhereQuery, JsonConstraint, LiteralValues, ModelJoinDefinition, ModelMetadataOptions, ModelRelationshipKeys, MultipleSortString, NegatableConstraint, NotEntity, NotEntityBrand, NumberOrDateConstraint, NumberOrDateConstraintWithSubquery, OmitEntityCollections, OmitFunctions, OrderBy, PaginateOptions, PickAsType, PickByValueType, PickFunctions, PlainObject, PoolLike, PoolQueryResult, PopulateArgs, Populated, QueryResult, QueryResultOptionalPopulated, QueryResultPopulated, QueryResultRow, ReturnSelect$1 as ReturnSelect, ScalarSubqueryConstraint, SelectAggregateExpression, SelectItem, Sort, SortObject, SortObjectValue, SortString, StringConstraint, SubqueryBuilderLike, SubqueryInConstraint, SubqueryJoinDefinition, SubqueryJoinInfo, SubqueryJoinOnCondition, ThroughArgs, TypedAggregateExpression, TypedSelectItem, TypedSubqueryBuilder, UpdateResult, UpdateResultJSON, WhereClauseValue, WhereQuery, WhereQueryStatement };
|
|
1478
|
+
export type { AggregateBuilder, AggregateCallback, AnyJoinInfo, ClassLike, ColumnBaseMetadataOptions, ColumnCollectionMetadataOptions, ColumnMetadata, ColumnModelMetadataOptions, ColumnModifierMetadata, ColumnTypeMetadataOptions, Comparer, CountResult, CreateResult, CreateResultArray, CreateResultArrayJSON, CreateResultJSON, CreateUpdateOptions, CreateUpdateParams, DeleteOptions, DestroyResult, DestroyResultJSON, DestroyResultWithRecords, DoNotReturnRecords, EntityFieldValue, EntityPrimitiveOrId, EntityStatic, ExcludeEntityCollections, ExcludeFunctions, FindArgs, FindOneArgs, FindOneResult, FindOneResultJSON, FindQueryWithCount, FindQueryWithCountJSON, FindResult, FindResultJSON, FindWithCountResult, GetValueType, HavingComparer, HavingCondition, IConnection, IReadonlyRepository, IRepository, IRepositoryOptions, IncludeFunctions, InitializeOptions, IsValueOfType, JoinDefinition, JoinInfo, JoinType, JoinedSort, JoinedWhereQuery, JsonConstraint, JsonPropertyValue, LiteralValues, ModelJoinDefinition, ModelMetadataOptions, ModelRelationshipKeys, MultipleSortString, NegatableConstraint, NotEntity, NotEntityBrand, NumberOrDateConstraint, NumberOrDateConstraintWithSubquery, OmitEntityCollections, OmitFunctions, OrderBy, PaginateOptions, PickAsType, PickByValueType, PickFunctions, PlainObject, PoolLike, PoolQueryResult, PopulateArgs, Populated, QueryResult, QueryResultOptionalPopulated, QueryResultPopulated, QueryResultRow, ReturnSelect$1 as ReturnSelect, ScalarSubqueryConstraint, SelectAggregateExpression, SelectItem, Sort, SortObject, SortObjectValue, SortString, StringConstraint, SubqueryBuilderLike, SubqueryInConstraint, SubqueryJoinDefinition, SubqueryJoinInfo, SubqueryJoinOnCondition, ThroughArgs, TypedAggregateExpression, TypedSelectItem, TypedSubqueryBuilder, UpdateResult, UpdateResultJSON, WhereClauseValue, WhereQuery, WhereQueryStatement };
|
package/dist/index.d.mts
CHANGED
|
@@ -630,6 +630,7 @@ declare class SubqueryBuilder<T extends Entity, TColumns extends string = never>
|
|
|
630
630
|
select(columns: SelectItem<T>[]): SubqueryBuilder<T>;
|
|
631
631
|
/**
|
|
632
632
|
* Group the subquery results by one or more columns.
|
|
633
|
+
* @param {(string & keyof T)[]} columns - Columns to group by
|
|
633
634
|
* @returns New SubqueryBuilder with the groupBy applied
|
|
634
635
|
* @example
|
|
635
636
|
* subquery(ProductRepository)
|
|
@@ -639,6 +640,7 @@ declare class SubqueryBuilder<T extends Entity, TColumns extends string = never>
|
|
|
639
640
|
groupBy(columns: (string & keyof T)[]): SubqueryBuilder<T, TColumns>;
|
|
640
641
|
/**
|
|
641
642
|
* Filter groups based on aggregate values (used with groupBy).
|
|
643
|
+
* @param {HavingCondition} condition - Having condition to apply
|
|
642
644
|
* @returns New SubqueryBuilder with the having condition applied
|
|
643
645
|
* @example
|
|
644
646
|
* subquery(ProductRepository)
|
|
@@ -1153,7 +1155,10 @@ type ExcludeUndefined<T> = Exclude<T, undefined>;
|
|
|
1153
1155
|
type LiteralValues<TValue> = (TValue | null)[] | TValue | null;
|
|
1154
1156
|
type WhereClauseValue<TValue> = TValue extends NotEntityBrand | undefined ? Exclude<TValue, NotEntityBrand | undefined> : Extract<TValue, Entity> extends undefined ? LiteralValues<ExcludeUndefined<TValue>> : (ExcludeUndefined<Exclude<TValue, Entity>> | null)[] | (Pick<Extract<ExcludeUndefined<TValue>, Entity>, 'id'> | null)[] | ExcludeUndefined<Exclude<TValue, Entity>> | Pick<Extract<ExcludeUndefined<TValue>, Entity>, 'id'> | null;
|
|
1155
1157
|
type StringConstraint<TValue extends string> = Partial<Record<'contains' | 'endsWith' | 'like' | 'startsWith', LiteralValues<ExcludeUndefined<TValue>>>>;
|
|
1156
|
-
type
|
|
1158
|
+
type JsonPropertyValue = boolean | number | string | null;
|
|
1159
|
+
type JsonConstraint<TValue> = Partial<Record<'contains', ExcludeUndefined<TValue> | LiteralValues<ExcludeUndefined<TValue>>>> & {
|
|
1160
|
+
[key: string]: JsonPropertyValue | JsonPropertyValue[] | Partial<Record<'!' | '<' | '<=' | '>' | '>=', JsonPropertyValue>> | undefined;
|
|
1161
|
+
};
|
|
1157
1162
|
type NumberOrDateConstraint<TValue extends Date | number> = Partial<Record<'<' | '<=' | '>' | '>=', LiteralValues<ExcludeUndefined<TValue>>>>;
|
|
1158
1163
|
interface SubqueryInConstraint {
|
|
1159
1164
|
in: SubqueryBuilderLike;
|
|
@@ -1470,4 +1475,4 @@ interface InitializeOptions extends IConnection {
|
|
|
1470
1475
|
declare function initialize({ models, pool, readonlyPool, connections, expose }: InitializeOptions): Record<string, IReadonlyRepository<Entity> | IRepository<Entity>>;
|
|
1471
1476
|
|
|
1472
1477
|
export { ColumnBaseMetadata, ColumnCollectionMetadata, ColumnModelMetadata, ColumnTypeMetadata, Entity, ModelMetadata, QueryError, ReadonlyRepository, Repository, ScalarSubquery, SelectBuilder, SubqueryBuilder, column, createDateColumn, getMetadataStorage, initialize, isSubqueryJoin, primaryColumn, subquery, table, updateDateColumn, versionColumn };
|
|
1473
|
-
export type { AggregateBuilder, AggregateCallback, AnyJoinInfo, ClassLike, ColumnBaseMetadataOptions, ColumnCollectionMetadataOptions, ColumnMetadata, ColumnModelMetadataOptions, ColumnModifierMetadata, ColumnTypeMetadataOptions, Comparer, CountResult, CreateResult, CreateResultArray, CreateResultArrayJSON, CreateResultJSON, CreateUpdateOptions, CreateUpdateParams, DeleteOptions, DestroyResult, DestroyResultJSON, DestroyResultWithRecords, DoNotReturnRecords, EntityFieldValue, EntityPrimitiveOrId, EntityStatic, ExcludeEntityCollections, ExcludeFunctions, FindArgs, FindOneArgs, FindOneResult, FindOneResultJSON, FindQueryWithCount, FindQueryWithCountJSON, FindResult, FindResultJSON, FindWithCountResult, GetValueType, HavingComparer, HavingCondition, IConnection, IReadonlyRepository, IRepository, IRepositoryOptions, IncludeFunctions, InitializeOptions, IsValueOfType, JoinDefinition, JoinInfo, JoinType, JoinedSort, JoinedWhereQuery, JsonConstraint, LiteralValues, ModelJoinDefinition, ModelMetadataOptions, ModelRelationshipKeys, MultipleSortString, NegatableConstraint, NotEntity, NotEntityBrand, NumberOrDateConstraint, NumberOrDateConstraintWithSubquery, OmitEntityCollections, OmitFunctions, OrderBy, PaginateOptions, PickAsType, PickByValueType, PickFunctions, PlainObject, PoolLike, PoolQueryResult, PopulateArgs, Populated, QueryResult, QueryResultOptionalPopulated, QueryResultPopulated, QueryResultRow, ReturnSelect$1 as ReturnSelect, ScalarSubqueryConstraint, SelectAggregateExpression, SelectItem, Sort, SortObject, SortObjectValue, SortString, StringConstraint, SubqueryBuilderLike, SubqueryInConstraint, SubqueryJoinDefinition, SubqueryJoinInfo, SubqueryJoinOnCondition, ThroughArgs, TypedAggregateExpression, TypedSelectItem, TypedSubqueryBuilder, UpdateResult, UpdateResultJSON, WhereClauseValue, WhereQuery, WhereQueryStatement };
|
|
1478
|
+
export type { AggregateBuilder, AggregateCallback, AnyJoinInfo, ClassLike, ColumnBaseMetadataOptions, ColumnCollectionMetadataOptions, ColumnMetadata, ColumnModelMetadataOptions, ColumnModifierMetadata, ColumnTypeMetadataOptions, Comparer, CountResult, CreateResult, CreateResultArray, CreateResultArrayJSON, CreateResultJSON, CreateUpdateOptions, CreateUpdateParams, DeleteOptions, DestroyResult, DestroyResultJSON, DestroyResultWithRecords, DoNotReturnRecords, EntityFieldValue, EntityPrimitiveOrId, EntityStatic, ExcludeEntityCollections, ExcludeFunctions, FindArgs, FindOneArgs, FindOneResult, FindOneResultJSON, FindQueryWithCount, FindQueryWithCountJSON, FindResult, FindResultJSON, FindWithCountResult, GetValueType, HavingComparer, HavingCondition, IConnection, IReadonlyRepository, IRepository, IRepositoryOptions, IncludeFunctions, InitializeOptions, IsValueOfType, JoinDefinition, JoinInfo, JoinType, JoinedSort, JoinedWhereQuery, JsonConstraint, JsonPropertyValue, LiteralValues, ModelJoinDefinition, ModelMetadataOptions, ModelRelationshipKeys, MultipleSortString, NegatableConstraint, NotEntity, NotEntityBrand, NumberOrDateConstraint, NumberOrDateConstraintWithSubquery, OmitEntityCollections, OmitFunctions, OrderBy, PaginateOptions, PickAsType, PickByValueType, PickFunctions, PlainObject, PoolLike, PoolQueryResult, PopulateArgs, Populated, QueryResult, QueryResultOptionalPopulated, QueryResultPopulated, QueryResultRow, ReturnSelect$1 as ReturnSelect, ScalarSubqueryConstraint, SelectAggregateExpression, SelectItem, Sort, SortObject, SortObjectValue, SortString, StringConstraint, SubqueryBuilderLike, SubqueryInConstraint, SubqueryJoinDefinition, SubqueryJoinInfo, SubqueryJoinOnCondition, ThroughArgs, TypedAggregateExpression, TypedSelectItem, TypedSubqueryBuilder, UpdateResult, UpdateResultJSON, WhereClauseValue, WhereQuery, WhereQueryStatement };
|
package/dist/index.d.ts
CHANGED
|
@@ -630,6 +630,7 @@ declare class SubqueryBuilder<T extends Entity, TColumns extends string = never>
|
|
|
630
630
|
select(columns: SelectItem<T>[]): SubqueryBuilder<T>;
|
|
631
631
|
/**
|
|
632
632
|
* Group the subquery results by one or more columns.
|
|
633
|
+
* @param {(string & keyof T)[]} columns - Columns to group by
|
|
633
634
|
* @returns New SubqueryBuilder with the groupBy applied
|
|
634
635
|
* @example
|
|
635
636
|
* subquery(ProductRepository)
|
|
@@ -639,6 +640,7 @@ declare class SubqueryBuilder<T extends Entity, TColumns extends string = never>
|
|
|
639
640
|
groupBy(columns: (string & keyof T)[]): SubqueryBuilder<T, TColumns>;
|
|
640
641
|
/**
|
|
641
642
|
* Filter groups based on aggregate values (used with groupBy).
|
|
643
|
+
* @param {HavingCondition} condition - Having condition to apply
|
|
642
644
|
* @returns New SubqueryBuilder with the having condition applied
|
|
643
645
|
* @example
|
|
644
646
|
* subquery(ProductRepository)
|
|
@@ -1153,7 +1155,10 @@ type ExcludeUndefined<T> = Exclude<T, undefined>;
|
|
|
1153
1155
|
type LiteralValues<TValue> = (TValue | null)[] | TValue | null;
|
|
1154
1156
|
type WhereClauseValue<TValue> = TValue extends NotEntityBrand | undefined ? Exclude<TValue, NotEntityBrand | undefined> : Extract<TValue, Entity> extends undefined ? LiteralValues<ExcludeUndefined<TValue>> : (ExcludeUndefined<Exclude<TValue, Entity>> | null)[] | (Pick<Extract<ExcludeUndefined<TValue>, Entity>, 'id'> | null)[] | ExcludeUndefined<Exclude<TValue, Entity>> | Pick<Extract<ExcludeUndefined<TValue>, Entity>, 'id'> | null;
|
|
1155
1157
|
type StringConstraint<TValue extends string> = Partial<Record<'contains' | 'endsWith' | 'like' | 'startsWith', LiteralValues<ExcludeUndefined<TValue>>>>;
|
|
1156
|
-
type
|
|
1158
|
+
type JsonPropertyValue = boolean | number | string | null;
|
|
1159
|
+
type JsonConstraint<TValue> = Partial<Record<'contains', ExcludeUndefined<TValue> | LiteralValues<ExcludeUndefined<TValue>>>> & {
|
|
1160
|
+
[key: string]: JsonPropertyValue | JsonPropertyValue[] | Partial<Record<'!' | '<' | '<=' | '>' | '>=', JsonPropertyValue>> | undefined;
|
|
1161
|
+
};
|
|
1157
1162
|
type NumberOrDateConstraint<TValue extends Date | number> = Partial<Record<'<' | '<=' | '>' | '>=', LiteralValues<ExcludeUndefined<TValue>>>>;
|
|
1158
1163
|
interface SubqueryInConstraint {
|
|
1159
1164
|
in: SubqueryBuilderLike;
|
|
@@ -1470,4 +1475,4 @@ interface InitializeOptions extends IConnection {
|
|
|
1470
1475
|
declare function initialize({ models, pool, readonlyPool, connections, expose }: InitializeOptions): Record<string, IReadonlyRepository<Entity> | IRepository<Entity>>;
|
|
1471
1476
|
|
|
1472
1477
|
export { ColumnBaseMetadata, ColumnCollectionMetadata, ColumnModelMetadata, ColumnTypeMetadata, Entity, ModelMetadata, QueryError, ReadonlyRepository, Repository, ScalarSubquery, SelectBuilder, SubqueryBuilder, column, createDateColumn, getMetadataStorage, initialize, isSubqueryJoin, primaryColumn, subquery, table, updateDateColumn, versionColumn };
|
|
1473
|
-
export type { AggregateBuilder, AggregateCallback, AnyJoinInfo, ClassLike, ColumnBaseMetadataOptions, ColumnCollectionMetadataOptions, ColumnMetadata, ColumnModelMetadataOptions, ColumnModifierMetadata, ColumnTypeMetadataOptions, Comparer, CountResult, CreateResult, CreateResultArray, CreateResultArrayJSON, CreateResultJSON, CreateUpdateOptions, CreateUpdateParams, DeleteOptions, DestroyResult, DestroyResultJSON, DestroyResultWithRecords, DoNotReturnRecords, EntityFieldValue, EntityPrimitiveOrId, EntityStatic, ExcludeEntityCollections, ExcludeFunctions, FindArgs, FindOneArgs, FindOneResult, FindOneResultJSON, FindQueryWithCount, FindQueryWithCountJSON, FindResult, FindResultJSON, FindWithCountResult, GetValueType, HavingComparer, HavingCondition, IConnection, IReadonlyRepository, IRepository, IRepositoryOptions, IncludeFunctions, InitializeOptions, IsValueOfType, JoinDefinition, JoinInfo, JoinType, JoinedSort, JoinedWhereQuery, JsonConstraint, LiteralValues, ModelJoinDefinition, ModelMetadataOptions, ModelRelationshipKeys, MultipleSortString, NegatableConstraint, NotEntity, NotEntityBrand, NumberOrDateConstraint, NumberOrDateConstraintWithSubquery, OmitEntityCollections, OmitFunctions, OrderBy, PaginateOptions, PickAsType, PickByValueType, PickFunctions, PlainObject, PoolLike, PoolQueryResult, PopulateArgs, Populated, QueryResult, QueryResultOptionalPopulated, QueryResultPopulated, QueryResultRow, ReturnSelect$1 as ReturnSelect, ScalarSubqueryConstraint, SelectAggregateExpression, SelectItem, Sort, SortObject, SortObjectValue, SortString, StringConstraint, SubqueryBuilderLike, SubqueryInConstraint, SubqueryJoinDefinition, SubqueryJoinInfo, SubqueryJoinOnCondition, ThroughArgs, TypedAggregateExpression, TypedSelectItem, TypedSubqueryBuilder, UpdateResult, UpdateResultJSON, WhereClauseValue, WhereQuery, WhereQueryStatement };
|
|
1478
|
+
export type { AggregateBuilder, AggregateCallback, AnyJoinInfo, ClassLike, ColumnBaseMetadataOptions, ColumnCollectionMetadataOptions, ColumnMetadata, ColumnModelMetadataOptions, ColumnModifierMetadata, ColumnTypeMetadataOptions, Comparer, CountResult, CreateResult, CreateResultArray, CreateResultArrayJSON, CreateResultJSON, CreateUpdateOptions, CreateUpdateParams, DeleteOptions, DestroyResult, DestroyResultJSON, DestroyResultWithRecords, DoNotReturnRecords, EntityFieldValue, EntityPrimitiveOrId, EntityStatic, ExcludeEntityCollections, ExcludeFunctions, FindArgs, FindOneArgs, FindOneResult, FindOneResultJSON, FindQueryWithCount, FindQueryWithCountJSON, FindResult, FindResultJSON, FindWithCountResult, GetValueType, HavingComparer, HavingCondition, IConnection, IReadonlyRepository, IRepository, IRepositoryOptions, IncludeFunctions, InitializeOptions, IsValueOfType, JoinDefinition, JoinInfo, JoinType, JoinedSort, JoinedWhereQuery, JsonConstraint, JsonPropertyValue, LiteralValues, ModelJoinDefinition, ModelMetadataOptions, ModelRelationshipKeys, MultipleSortString, NegatableConstraint, NotEntity, NotEntityBrand, NumberOrDateConstraint, NumberOrDateConstraintWithSubquery, OmitEntityCollections, OmitFunctions, OrderBy, PaginateOptions, PickAsType, PickByValueType, PickFunctions, PlainObject, PoolLike, PoolQueryResult, PopulateArgs, Populated, QueryResult, QueryResultOptionalPopulated, QueryResultPopulated, QueryResultRow, ReturnSelect$1 as ReturnSelect, ScalarSubqueryConstraint, SelectAggregateExpression, SelectItem, Sort, SortObject, SortObjectValue, SortString, StringConstraint, SubqueryBuilderLike, SubqueryInConstraint, SubqueryJoinDefinition, SubqueryJoinInfo, SubqueryJoinOnCondition, ThroughArgs, TypedAggregateExpression, TypedSelectItem, TypedSubqueryBuilder, UpdateResult, UpdateResultJSON, WhereClauseValue, WhereQuery, WhereQueryStatement };
|
package/dist/index.mjs
CHANGED
|
@@ -457,6 +457,7 @@ class SubqueryBuilder {
|
|
|
457
457
|
}
|
|
458
458
|
/**
|
|
459
459
|
* Group the subquery results by one or more columns.
|
|
460
|
+
* @param {(string & keyof T)[]} columns - Columns to group by
|
|
460
461
|
* @returns New SubqueryBuilder with the groupBy applied
|
|
461
462
|
* @example
|
|
462
463
|
* subquery(ProductRepository)
|
|
@@ -470,6 +471,7 @@ class SubqueryBuilder {
|
|
|
470
471
|
}
|
|
471
472
|
/**
|
|
472
473
|
* Filter groups based on aggregate values (used with groupBy).
|
|
474
|
+
* @param {HavingCondition} condition - Having condition to apply
|
|
473
475
|
* @returns New SubqueryBuilder with the having condition applied
|
|
474
476
|
* @example
|
|
475
477
|
* subquery(ProductRepository)
|
|
@@ -1735,6 +1737,21 @@ function buildWhere({
|
|
|
1735
1737
|
let subQueryComparer;
|
|
1736
1738
|
if (isComparer(key)) {
|
|
1737
1739
|
subQueryComparer = key;
|
|
1740
|
+
} else if (propertyName) {
|
|
1741
|
+
const parentColumn = model.columnsByPropertyName[propertyName];
|
|
1742
|
+
if (parentColumn?.type?.toLowerCase() === "json") {
|
|
1743
|
+
andValues.push(
|
|
1744
|
+
buildJsonPropertyClause({
|
|
1745
|
+
columnName: parentColumn.name,
|
|
1746
|
+
path: [key],
|
|
1747
|
+
isNegated,
|
|
1748
|
+
constraint: where,
|
|
1749
|
+
params
|
|
1750
|
+
})
|
|
1751
|
+
);
|
|
1752
|
+
continue;
|
|
1753
|
+
}
|
|
1754
|
+
propertyName = key;
|
|
1738
1755
|
} else {
|
|
1739
1756
|
propertyName = key;
|
|
1740
1757
|
}
|
|
@@ -2099,6 +2116,91 @@ function buildJsonContainmentStatement({ repositoriesByModelNameLowered, model,
|
|
|
2099
2116
|
}
|
|
2100
2117
|
});
|
|
2101
2118
|
}
|
|
2119
|
+
function getTypeCastSuffix(value) {
|
|
2120
|
+
if (typeof value === "number") {
|
|
2121
|
+
return "::numeric";
|
|
2122
|
+
}
|
|
2123
|
+
if (typeof value === "boolean") {
|
|
2124
|
+
return "::boolean";
|
|
2125
|
+
}
|
|
2126
|
+
return "";
|
|
2127
|
+
}
|
|
2128
|
+
function buildJsonAccessor(columnName, path) {
|
|
2129
|
+
let result = `"${columnName}"`;
|
|
2130
|
+
for (let i = 0; i < path.length; i++) {
|
|
2131
|
+
const arrow = i === path.length - 1 ? "->>" : "->";
|
|
2132
|
+
result += `${arrow}'${path[i]}'`;
|
|
2133
|
+
}
|
|
2134
|
+
return result;
|
|
2135
|
+
}
|
|
2136
|
+
function isJsonConstraintOperator(key) {
|
|
2137
|
+
return key === "!" || key === "<" || key === "<=" || key === ">" || key === ">=";
|
|
2138
|
+
}
|
|
2139
|
+
function buildJsonPropertyClause({
|
|
2140
|
+
columnName,
|
|
2141
|
+
path,
|
|
2142
|
+
isNegated,
|
|
2143
|
+
constraint,
|
|
2144
|
+
params
|
|
2145
|
+
}) {
|
|
2146
|
+
for (const segment of path) {
|
|
2147
|
+
assertValidSqlIdentifier(segment, `JSON property name "${segment}"`);
|
|
2148
|
+
}
|
|
2149
|
+
if (constraint === null) {
|
|
2150
|
+
return `${buildJsonAccessor(columnName, path)} ${isNegated ? "IS NOT" : "IS"} NULL`;
|
|
2151
|
+
}
|
|
2152
|
+
if (Array.isArray(constraint)) {
|
|
2153
|
+
const accessor2 = buildJsonAccessor(columnName, path);
|
|
2154
|
+
params.push(constraint);
|
|
2155
|
+
return `${accessor2}${isNegated ? "<>ALL" : "=ANY"}($${params.length})`;
|
|
2156
|
+
}
|
|
2157
|
+
if (typeof constraint === "object") {
|
|
2158
|
+
const entries = Object.entries(constraint);
|
|
2159
|
+
const firstKey = entries[0]?.[0];
|
|
2160
|
+
if (firstKey && isJsonConstraintOperator(firstKey)) {
|
|
2161
|
+
const accessor2 = buildJsonAccessor(columnName, path);
|
|
2162
|
+
const negatedComparisonOperators = { "<": ">=", "<=": ">", ">": "<=", ">=": "<" };
|
|
2163
|
+
const clauses2 = [];
|
|
2164
|
+
for (const [operator, operatorValue] of entries) {
|
|
2165
|
+
if (operator === "!") {
|
|
2166
|
+
if (operatorValue === null) {
|
|
2167
|
+
clauses2.push(`${accessor2} ${isNegated ? "IS" : "IS NOT"} NULL`);
|
|
2168
|
+
} else {
|
|
2169
|
+
params.push(operatorValue);
|
|
2170
|
+
const castSuffix2 = getTypeCastSuffix(operatorValue);
|
|
2171
|
+
const castAccessor2 = castSuffix2 ? `(${accessor2})${castSuffix2}` : accessor2;
|
|
2172
|
+
clauses2.push(`${castAccessor2}${isNegated ? "=" : "<>"}$${params.length}`);
|
|
2173
|
+
}
|
|
2174
|
+
} else if (operator === "<" || operator === "<=" || operator === ">" || operator === ">=") {
|
|
2175
|
+
params.push(operatorValue);
|
|
2176
|
+
const castSuffix2 = getTypeCastSuffix(operatorValue);
|
|
2177
|
+
const castAccessor2 = castSuffix2 ? `(${accessor2})${castSuffix2}` : accessor2;
|
|
2178
|
+
const effectiveOperator = isNegated ? negatedComparisonOperators[operator] : operator;
|
|
2179
|
+
clauses2.push(`${castAccessor2}${effectiveOperator}$${params.length}`);
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
return clauses2.join(" AND ");
|
|
2183
|
+
}
|
|
2184
|
+
const clauses = [];
|
|
2185
|
+
for (const [nestedKey, nestedValue] of entries) {
|
|
2186
|
+
clauses.push(
|
|
2187
|
+
buildJsonPropertyClause({
|
|
2188
|
+
columnName,
|
|
2189
|
+
path: [...path, nestedKey],
|
|
2190
|
+
isNegated,
|
|
2191
|
+
constraint: nestedValue,
|
|
2192
|
+
params
|
|
2193
|
+
})
|
|
2194
|
+
);
|
|
2195
|
+
}
|
|
2196
|
+
return clauses.join(" AND ");
|
|
2197
|
+
}
|
|
2198
|
+
const accessor = buildJsonAccessor(columnName, path);
|
|
2199
|
+
params.push(constraint);
|
|
2200
|
+
const castSuffix = getTypeCastSuffix(constraint);
|
|
2201
|
+
const castAccessor = castSuffix ? `(${accessor})${castSuffix}` : accessor;
|
|
2202
|
+
return `${castAccessor}${isNegated ? "<>" : "="}$${params.length}`;
|
|
2203
|
+
}
|
|
2102
2204
|
function buildLikeOperatorStatement({ repositoriesByModelNameLowered, model, propertyName, isNegated, value, params, joins }) {
|
|
2103
2205
|
return buildArrayOrSingleStatement({
|
|
2104
2206
|
repositoriesByModelNameLowered,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bigal",
|
|
3
|
-
"version": "15.
|
|
3
|
+
"version": "15.11.0",
|
|
4
4
|
"description": "A fast and lightweight orm for postgres and node.js, written in typescript.",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -38,31 +38,33 @@
|
|
|
38
38
|
"@semantic-release/commit-analyzer": "13.0.1",
|
|
39
39
|
"@semantic-release/git": "10.0.1",
|
|
40
40
|
"@semantic-release/github": "12.0.6",
|
|
41
|
-
"@semantic-release/npm": "13.1.
|
|
41
|
+
"@semantic-release/npm": "13.1.5",
|
|
42
42
|
"@semantic-release/release-notes-generator": "14.1.0",
|
|
43
43
|
"@types/node": ">=22",
|
|
44
|
-
"
|
|
45
|
-
"eslint-config-decent": "4.1.0",
|
|
44
|
+
"@typescript/native-preview": "7.0.0-dev.20260310.1",
|
|
46
45
|
"husky": "9.1.7",
|
|
47
|
-
"lint-staged": "16.
|
|
48
|
-
"markdownlint-cli": "0.
|
|
46
|
+
"lint-staged": "16.3.3",
|
|
47
|
+
"markdownlint-cli": "0.48.0",
|
|
49
48
|
"npm-run-all2": "8.0.4",
|
|
49
|
+
"oxfmt": "0.37.0",
|
|
50
|
+
"oxlint": "1.52.0",
|
|
51
|
+
"oxlint-tsgolint": "0.16.0",
|
|
50
52
|
"pinst": "3.0.0",
|
|
51
|
-
"
|
|
53
|
+
"postgres-pool": "11.0.4",
|
|
52
54
|
"semantic-release": "25.0.3",
|
|
53
55
|
"strict-event-emitter-types": "2.0.0",
|
|
54
|
-
"postgres-pool": "11.0.3",
|
|
55
56
|
"typescript": "5.9.3",
|
|
56
57
|
"unbuild": "3.6.1",
|
|
57
58
|
"vitest": "4.0.18"
|
|
58
59
|
},
|
|
59
60
|
"scripts": {
|
|
60
61
|
"build": "unbuild",
|
|
61
|
-
"check:types": "
|
|
62
|
+
"check:types": "tsgo --noEmit --skipLibCheck",
|
|
62
63
|
"test": "npm run check:types && vitest run",
|
|
63
|
-
"lint:markdown": "
|
|
64
|
-
"lint:code": "
|
|
65
|
-
"lint": "run-p lint:*",
|
|
64
|
+
"lint:markdown": "markdownlint '**/*.md' --ignore '**/node_modules/**' --ignore '**/dist/**' --config=.github/linters/.markdown-lint.yml --fix",
|
|
65
|
+
"lint:code": "oxlint --fix --deny-warnings",
|
|
66
|
+
"lint": "npm run format && run-p lint:*",
|
|
67
|
+
"format": "oxfmt --write .",
|
|
66
68
|
"lint-staged": "lint-staged",
|
|
67
69
|
"beta": "npm publish --tag beta",
|
|
68
70
|
"prepublishOnly": "pinst --disable",
|
|
@@ -71,14 +73,15 @@
|
|
|
71
73
|
},
|
|
72
74
|
"lint-staged": {
|
|
73
75
|
"*.md": [
|
|
74
|
-
"
|
|
76
|
+
"oxfmt --write",
|
|
75
77
|
"markdownlint --config=.github/linters/.markdown-lint.yml --fix"
|
|
76
78
|
],
|
|
77
79
|
"*.{js,cjs,mjs,ts}": [
|
|
78
|
-
"
|
|
80
|
+
"oxfmt --write",
|
|
81
|
+
"oxlint --fix --deny-warnings"
|
|
79
82
|
],
|
|
80
83
|
"*.{json5,yml}": [
|
|
81
|
-
"
|
|
84
|
+
"oxfmt --write"
|
|
82
85
|
]
|
|
83
86
|
},
|
|
84
87
|
"repository": {
|
package/.prettierrc.cjs
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
module.exports = {
|
|
4
|
-
arrowParens: 'always',
|
|
5
|
-
bracketSpacing: true,
|
|
6
|
-
printWidth: 200,
|
|
7
|
-
quoteProps: 'as-needed',
|
|
8
|
-
semi: true,
|
|
9
|
-
singleQuote: true,
|
|
10
|
-
useTabs: false,
|
|
11
|
-
tabWidth: 2,
|
|
12
|
-
trailingComma: 'all',
|
|
13
|
-
|
|
14
|
-
overrides: [
|
|
15
|
-
{
|
|
16
|
-
files: ['*.js', '*.cjs', '*.mjs'],
|
|
17
|
-
options: {
|
|
18
|
-
parser: 'espree',
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
files: '*.json',
|
|
23
|
-
options: {
|
|
24
|
-
parser: 'json',
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
files: '*.ts',
|
|
29
|
-
options: {
|
|
30
|
-
parser: 'typescript',
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
],
|
|
34
|
-
};
|
package/eslint.config.mjs
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { config } from 'eslint-config-decent';
|
|
2
|
-
|
|
3
|
-
export default [
|
|
4
|
-
...config({
|
|
5
|
-
tsconfigRootDir: import.meta.dirname,
|
|
6
|
-
enableTestingLibrary: false,
|
|
7
|
-
}),
|
|
8
|
-
{
|
|
9
|
-
files: ['**/*.ts'],
|
|
10
|
-
rules: {
|
|
11
|
-
'@typescript-eslint/no-invalid-void-type': 'off',
|
|
12
|
-
},
|
|
13
|
-
},
|
|
14
|
-
];
|