@venizia/ignis-docs 0.2.0 → 0.2.1-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 +14 -14
- package/content/best-practices/api-usage-examples.md +39 -19
- package/content/best-practices/architectural-patterns.md +22 -11
- package/content/best-practices/architecture-decisions.md +29 -16
- package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
- package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
- package/content/best-practices/code-style-standards/control-flow.md +33 -1
- package/content/best-practices/code-style-standards/documentation.md +28 -8
- package/content/best-practices/code-style-standards/function-patterns.md +15 -4
- package/content/best-practices/code-style-standards/index.md +7 -2
- package/content/best-practices/code-style-standards/naming-conventions.md +26 -2
- package/content/best-practices/code-style-standards/route-definitions.md +9 -5
- package/content/best-practices/code-style-standards/tooling.md +14 -1
- package/content/best-practices/code-style-standards/type-safety.md +39 -6
- package/content/best-practices/common-pitfalls.md +59 -35
- package/content/best-practices/contribution-workflow.md +6 -2
- package/content/best-practices/data-modeling.md +78 -62
- package/content/best-practices/deployment-strategies.md +56 -19
- package/content/best-practices/error-handling.md +182 -93
- package/content/best-practices/index.md +6 -0
- package/content/best-practices/performance-optimization.md +26 -13
- package/content/best-practices/security-guidelines.md +35 -9
- package/content/best-practices/testing-strategies.md +7 -2
- package/content/best-practices/troubleshooting-tips.md +27 -17
- package/content/extensions/components/api-reference.md +107 -322
- package/content/extensions/components/authentication/api.md +454 -603
- package/content/extensions/components/authentication/errors.md +121 -498
- package/content/extensions/components/authentication/index.md +88 -801
- package/content/extensions/components/authentication/usage.md +207 -956
- package/content/extensions/components/authorization/api.md +736 -656
- package/content/extensions/components/authorization/errors.md +168 -206
- package/content/extensions/components/authorization/index.md +82 -797
- package/content/extensions/components/authorization/usage.md +194 -527
- package/content/extensions/components/health-check.md +71 -243
- package/content/extensions/components/mail/api.md +504 -287
- package/content/extensions/components/mail/errors.md +73 -61
- package/content/extensions/components/mail/index.md +96 -467
- package/content/extensions/components/mail/usage.md +130 -172
- package/content/extensions/components/request-tracker.md +66 -173
- package/content/extensions/components/socket-io/api.md +195 -13
- package/content/extensions/components/socket-io/errors.md +3 -3
- package/content/extensions/components/socket-io/index.md +50 -337
- package/content/extensions/components/socket-io/usage.md +143 -26
- package/content/extensions/components/static-asset/api.md +410 -142
- package/content/extensions/components/static-asset/errors.md +110 -53
- package/content/extensions/components/static-asset/index.md +79 -608
- package/content/extensions/components/static-asset/usage.md +180 -300
- package/content/extensions/components/websocket/api.md +275 -399
- package/content/extensions/components/websocket/errors.md +47 -56
- package/content/extensions/components/websocket/index.md +74 -407
- package/content/extensions/components/websocket/usage.md +110 -341
- package/content/extensions/helpers/cron/index.md +51 -160
- package/content/extensions/helpers/crypto/index.md +62 -483
- package/content/extensions/helpers/crypto/reference.md +456 -0
- package/content/extensions/helpers/env/index.md +60 -178
- package/content/extensions/helpers/error/index.md +221 -207
- package/content/extensions/helpers/inversion/index.md +65 -556
- package/content/extensions/helpers/inversion/reference.md +522 -0
- package/content/extensions/helpers/kafka/admin.md +20 -1
- package/content/extensions/helpers/kafka/compile-binary.md +41 -35
- package/content/extensions/helpers/kafka/consumer.md +54 -20
- package/content/extensions/helpers/kafka/examples.md +21 -16
- package/content/extensions/helpers/kafka/index.md +80 -610
- package/content/extensions/helpers/kafka/producer.md +134 -5
- package/content/extensions/helpers/kafka/schema-registry.md +45 -70
- package/content/extensions/helpers/logger/hf-logger.md +193 -0
- package/content/extensions/helpers/logger/index.md +64 -563
- package/content/extensions/helpers/logger/pino.md +85 -0
- package/content/extensions/helpers/logger/reference.md +746 -0
- package/content/extensions/helpers/network/api.md +241 -195
- package/content/extensions/helpers/network/index.md +72 -530
- package/content/extensions/helpers/queue/index.md +72 -900
- package/content/extensions/helpers/queue/reference.md +467 -0
- package/content/extensions/helpers/redis/index.md +73 -645
- package/content/extensions/helpers/redis/reference.md +727 -0
- package/content/extensions/helpers/secrets/index.md +66 -0
- package/content/extensions/helpers/socket-io/api.md +305 -203
- package/content/extensions/helpers/socket-io/index.md +66 -432
- package/content/extensions/helpers/storage/api.md +564 -462
- package/content/extensions/helpers/storage/index.md +77 -573
- package/content/extensions/helpers/types/index.md +66 -499
- package/content/extensions/helpers/types/reference.md +650 -0
- package/content/extensions/helpers/uid/index.md +58 -227
- package/content/extensions/helpers/websocket/api.md +329 -216
- package/content/extensions/helpers/websocket/index.md +65 -503
- package/content/extensions/helpers/worker-thread/index.md +58 -396
- package/content/extensions/helpers/worker-thread/reference.md +428 -0
- package/content/guides/core-concepts/persistent/models.md +1 -1
- package/content/guides/core-concepts/persistent/search-typesense.md +3 -3
- package/content/guides/core-concepts/persistent/transactions.md +1 -1
- package/content/guides/core-concepts/secrets-vault.md +177 -0
- package/content/guides/core-concepts/services.md +1 -1
- package/content/guides/migrations/redis-helpers-migration.md +1 -1
- package/content/guides/migrations/unified-connectors-migration.md +2 -2
- package/content/guides/tutorials/ecommerce-api.md +3 -8
- package/content/references/base/application.md +1 -1
- package/content/references/base/connectors.md +79 -136
- package/content/references/base/datasources-reference.md +599 -0
- package/content/references/base/datasources.md +84 -444
- package/content/references/base/dependency-injection.md +17 -39
- package/content/references/base/filter-system/application-usage.md +69 -121
- package/content/references/base/filter-system/array-operators.md +12 -0
- package/content/references/base/filter-system/comparison-operators.md +12 -0
- package/content/references/base/filter-system/default-filter.md +136 -348
- package/content/references/base/filter-system/fields-order-pagination.md +38 -16
- package/content/references/base/filter-system/index.md +106 -257
- package/content/references/base/filter-system/json-filtering.md +12 -2
- package/content/references/base/filter-system/list-operators.md +16 -2
- package/content/references/base/filter-system/logical-operators.md +13 -0
- package/content/references/base/filter-system/null-operators.md +13 -0
- package/content/references/base/filter-system/pattern-matching.md +12 -0
- package/content/references/base/filter-system/quick-reference.md +11 -2
- package/content/references/base/filter-system/range-operators.md +12 -0
- package/content/references/base/filter-system/tips.md +70 -133
- package/content/references/base/filter-system/use-cases.md +156 -233
- package/content/references/base/middlewares.md +35 -21
- package/content/references/base/models-reference.md +886 -0
- package/content/references/base/models.md +80 -1452
- package/content/references/base/repositories/advanced.md +156 -192
- package/content/references/base/repositories/index.md +77 -650
- package/content/references/base/repositories/mixins.md +22 -18
- package/content/references/base/repositories/relations.md +123 -171
- package/content/references/base/repositories/soft-deletable.md +58 -56
- package/content/references/base/secrets.md +263 -0
- package/content/references/base/services.md +2 -2
- package/content/references/configuration/environment-variables.md +48 -4
- package/content/references/configuration/index.md +49 -31
- package/content/references/quick-reference.md +3 -16
- package/content/references/utilities/crypto.md +35 -76
- package/content/references/utilities/date.md +33 -73
- package/content/references/utilities/index.md +1 -1
- package/content/references/utilities/jsx-reference.md +298 -0
- package/content/references/utilities/jsx.md +82 -525
- package/content/references/utilities/module.md +29 -62
- package/content/references/utilities/parse.md +34 -64
- package/content/references/utilities/performance.md +33 -58
- package/content/references/utilities/promise.md +28 -62
- package/content/references/utilities/request.md +57 -218
- package/content/references/utilities/schema.md +43 -137
- package/content/references/utilities/statuses-reference.md +361 -0
- package/content/references/utilities/statuses.md +63 -667
- package/package.json +8 -8
|
@@ -16,7 +16,9 @@ Control which fields are returned using `fields`:
|
|
|
16
16
|
### Array Format (Recommended)
|
|
17
17
|
|
|
18
18
|
```typescript
|
|
19
|
-
|
|
19
|
+
import { userRepository } from '@/repositories';
|
|
20
|
+
|
|
21
|
+
await userRepository.find({
|
|
20
22
|
filter: {
|
|
21
23
|
where: { status: 'active' },
|
|
22
24
|
fields: ['id', 'email', 'name']
|
|
@@ -29,7 +31,7 @@ await repository.find({
|
|
|
29
31
|
|
|
30
32
|
```typescript
|
|
31
33
|
// Include specific fields (only keys with `true` are selected)
|
|
32
|
-
await
|
|
34
|
+
await userRepository.find({
|
|
33
35
|
filter: {
|
|
34
36
|
fields: { id: true, email: true, name: true }
|
|
35
37
|
}
|
|
@@ -46,17 +48,17 @@ await repository.find({
|
|
|
46
48
|
|
|
47
49
|
```typescript
|
|
48
50
|
// Single column, descending
|
|
49
|
-
await
|
|
51
|
+
await userRepository.find({
|
|
50
52
|
filter: { order: ['createdAt DESC'] }
|
|
51
53
|
});
|
|
52
54
|
|
|
53
55
|
// Multiple columns
|
|
54
|
-
await
|
|
56
|
+
await userRepository.find({
|
|
55
57
|
filter: { order: ['status ASC', 'createdAt DESC'] }
|
|
56
58
|
});
|
|
57
59
|
|
|
58
60
|
// Default direction is ASC
|
|
59
|
-
await
|
|
61
|
+
await userRepository.find({
|
|
60
62
|
filter: { order: ['name'] } // Same as 'name ASC'
|
|
61
63
|
});
|
|
62
64
|
```
|
|
@@ -74,12 +76,12 @@ Error: Invalid direction: 'RANDOM' | Expected: 'ASC' or 'DESC'
|
|
|
74
76
|
Order by nested fields in JSON columns:
|
|
75
77
|
|
|
76
78
|
```typescript
|
|
77
|
-
await
|
|
79
|
+
await userRepository.find({
|
|
78
80
|
filter: { order: ['metadata.priority DESC'] }
|
|
79
81
|
});
|
|
80
82
|
// SQL: ORDER BY "metadata" #> '{priority}' DESC
|
|
81
83
|
|
|
82
|
-
await
|
|
84
|
+
await userRepository.find({
|
|
83
85
|
filter: { order: ['settings.display.theme ASC'] }
|
|
84
86
|
});
|
|
85
87
|
```
|
|
@@ -104,24 +106,24 @@ Both `skip` and `offset` are supported as aliases -- they both map to the SQL `O
|
|
|
104
106
|
|
|
105
107
|
```typescript
|
|
106
108
|
// First 10 results (default limit is 10)
|
|
107
|
-
await
|
|
109
|
+
await userRepository.find({
|
|
108
110
|
filter: { limit: 10 }
|
|
109
111
|
});
|
|
110
112
|
|
|
111
113
|
// Page 2 (skip first 10, get next 10)
|
|
112
|
-
await
|
|
114
|
+
await userRepository.find({
|
|
113
115
|
filter: { limit: 10, skip: 10 }
|
|
114
116
|
});
|
|
115
117
|
|
|
116
118
|
// Using offset (equivalent to skip)
|
|
117
|
-
await
|
|
119
|
+
await userRepository.find({
|
|
118
120
|
filter: { limit: 10, offset: 10 }
|
|
119
121
|
});
|
|
120
122
|
|
|
121
123
|
// Page N formula: skip = (page - 1) * limit
|
|
122
124
|
const page = 3;
|
|
123
125
|
const pageSize = 20;
|
|
124
|
-
await
|
|
126
|
+
await userRepository.find({
|
|
125
127
|
filter: {
|
|
126
128
|
limit: pageSize,
|
|
127
129
|
skip: (page - 1) * pageSize
|
|
@@ -145,11 +147,17 @@ query.limit ?? model settings.defaultLimit ?? DEFAULT_LIMIT (10)
|
|
|
145
147
|
- **`DEFAULT_LIMIT`** - the global fallback, `10`.
|
|
146
148
|
|
|
147
149
|
```typescript
|
|
150
|
+
import { model, BaseEntity } from '@venizia/ignis';
|
|
151
|
+
import { countryTable } from '@/schemas';
|
|
152
|
+
import { countryRepository } from '@/repositories';
|
|
153
|
+
|
|
148
154
|
@model({
|
|
149
155
|
type: 'entity',
|
|
150
156
|
settings: { defaultLimit: 200 }, // Small lookup table - default to 200 rows
|
|
151
157
|
})
|
|
152
|
-
export class Country extends BaseEntity<typeof Country.schema> {
|
|
158
|
+
export class Country extends BaseEntity<typeof Country.schema> {
|
|
159
|
+
static override schema = countryTable;
|
|
160
|
+
}
|
|
153
161
|
|
|
154
162
|
await countryRepository.find({ filter: {} }); // LIMIT 200
|
|
155
163
|
await countryRepository.find({ filter: { limit: 10 } }); // LIMIT 10 (explicit wins)
|
|
@@ -184,7 +192,7 @@ When building paginated APIs, you often need to return the total count alongside
|
|
|
184
192
|
### Basic Usage
|
|
185
193
|
|
|
186
194
|
```typescript
|
|
187
|
-
const result = await
|
|
195
|
+
const result = await userRepository.find({
|
|
188
196
|
filter: { limit: 10, skip: 20 },
|
|
189
197
|
options: { shouldQueryRange: true }
|
|
190
198
|
});
|
|
@@ -205,7 +213,7 @@ const result = await repository.find({
|
|
|
205
213
|
Use the range information to set standard HTTP headers:
|
|
206
214
|
|
|
207
215
|
```typescript
|
|
208
|
-
const { data, range } = await
|
|
216
|
+
const { data, range } = await userRepository.find({
|
|
209
217
|
filter: { limit: 10, skip: 20, where: { status: 'active' } },
|
|
210
218
|
options: { shouldQueryRange: true }
|
|
211
219
|
});
|
|
@@ -246,7 +254,7 @@ When `shouldQueryRange: true`, the repository executes the data query and count
|
|
|
246
254
|
## Combined Example
|
|
247
255
|
|
|
248
256
|
```typescript
|
|
249
|
-
await
|
|
257
|
+
await userRepository.find({
|
|
250
258
|
filter: {
|
|
251
259
|
where: { status: 'active' },
|
|
252
260
|
fields: ['id', 'name', 'price', 'createdAt'],
|
|
@@ -260,7 +268,7 @@ await repository.find({
|
|
|
260
268
|
### With Range Information
|
|
261
269
|
|
|
262
270
|
```typescript
|
|
263
|
-
const { data, range } = await
|
|
271
|
+
const { data, range } = await userRepository.find({
|
|
264
272
|
filter: {
|
|
265
273
|
where: { status: 'active' },
|
|
266
274
|
fields: ['id', 'name', 'price', 'createdAt'],
|
|
@@ -274,3 +282,17 @@ const { data, range } = await repository.find({
|
|
|
274
282
|
console.log(`Showing ${range.start}-${range.end} of ${range.total}`);
|
|
275
283
|
// -> "Showing 0-19 of 150"
|
|
276
284
|
```
|
|
285
|
+
|
|
286
|
+
## See also
|
|
287
|
+
|
|
288
|
+
- [Filter System Overview](./) - the `filter` shape and the full `where` operator table
|
|
289
|
+
- [JSON Filtering](./json-filtering) - JSON path ordering and the JSONB sort-order table
|
|
290
|
+
- [Default Filter](./default-filter) - `settings.defaultFilter`, the sibling of `settings.defaultLimit`
|
|
291
|
+
- [Quick Reference](./quick-reference) - every operator, one line each
|
|
292
|
+
|
|
293
|
+
**Files:**
|
|
294
|
+
|
|
295
|
+
- [`packages/core/src/connectors/postgres/repositories/dialect/filter.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/dialect/filter.ts) - `FilterBuilder`, `toColumns`/`toOrderBy`
|
|
296
|
+
- [`packages/core/src/base/repositories/common/operators.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/repositories/common/operators.ts) - `Sorts` constants
|
|
297
|
+
- [`packages/core/src/base/repositories/common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/repositories/common/constants.ts) - `DEFAULT_LIMIT`
|
|
298
|
+
- [`packages/core/src/base/repositories/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/repositories/common/types.ts) - `TDataRange`, `buildDataRange`
|
|
@@ -1,288 +1,137 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: Filter System
|
|
3
|
-
description:
|
|
2
|
+
title: Filter System
|
|
3
|
+
description: Shape a query - which rows, which fields, what order, how much
|
|
4
4
|
difficulty: intermediate
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Filter System
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
A filter is the object every repository read, update, and delete verb accepts to shape a query - which rows (`where`), which columns (`fields`), what order (`order`), and how much (`limit`/`skip`).
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
> If you're new to IGNIS, start with:
|
|
13
|
-
> - [5-Minute Quickstart](/guides/get-started/5-minute-quickstart) - Get up and running
|
|
14
|
-
> - [Building a CRUD API](/guides/tutorials/building-a-crud-api) - Learn the basics
|
|
15
|
-
> - [Repositories](/references/base/repositories/) - Repository overview
|
|
11
|
+
## In one example
|
|
16
12
|
|
|
17
|
-
|
|
13
|
+
A filter shapes one query - `where` picks rows, `fields` picks columns, `order` sorts, `limit` bounds the result:
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
| [List Operators](./list-operators.md) | IN, NOT IN |
|
|
35
|
-
| [Range Operators](./range-operators.md) | BETWEEN, NOT BETWEEN |
|
|
36
|
-
| [Null Operators](./null-operators.md) | IS NULL, IS NOT NULL |
|
|
37
|
-
| [Array Operators](./array-operators.md) | PostgreSQL array operations |
|
|
38
|
-
| [JSON Filtering](./json-filtering.md) | JSON/JSONB path queries |
|
|
39
|
-
| [Fields, Order, Pagination](./fields-order-pagination.md) | SELECT, ORDER BY, LIMIT |
|
|
40
|
-
| [**Default Filter**](./default-filter.md) | Automatic filter application |
|
|
41
|
-
| [Application Usage](./application-usage.md) | Filter flow in applications |
|
|
42
|
-
| [Tips & Best Practices](./tips.md) | Performance and patterns |
|
|
43
|
-
| [Use Cases](./use-cases.md) | Real-world examples |
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
## Filter Structure
|
|
15
|
+
```typescript
|
|
16
|
+
import { postRepository } from '@/repositories';
|
|
17
|
+
|
|
18
|
+
const posts = await postRepository.find({
|
|
19
|
+
filter: {
|
|
20
|
+
where: {
|
|
21
|
+
status: 'published',
|
|
22
|
+
or: [{ featured: true }, { rating: { gte: 4.5 } }],
|
|
23
|
+
},
|
|
24
|
+
fields: ['id', 'title', 'rating', 'publishedAt'],
|
|
25
|
+
order: ['rating DESC', 'publishedAt DESC'],
|
|
26
|
+
limit: 20,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
```
|
|
47
30
|
|
|
48
|
-
|
|
31
|
+
`postRepository` is a `@repository({ model: Post, dataSource })`-bound repository - `Post`'s schema comes from `@/schemas`. See [Models](/references/base/models) and [Repositories](../repositories/).
|
|
49
32
|
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
offset?: number; // Alias for skip
|
|
58
|
-
include?: TInclusion[]; // Related data (Drizzle relational queries)
|
|
59
|
-
};
|
|
33
|
+
```sql
|
|
34
|
+
-- Equivalent SQL
|
|
35
|
+
SELECT "id", "title", "rating", "published_at"
|
|
36
|
+
FROM "post"
|
|
37
|
+
WHERE "status" = 'published' AND ("featured" = true OR "rating" >= 4.5)
|
|
38
|
+
ORDER BY "rating" DESC, "published_at" DESC
|
|
39
|
+
LIMIT 20
|
|
60
40
|
```
|
|
61
41
|
|
|
42
|
+
## How it works
|
|
62
43
|
|
|
63
|
-
|
|
44
|
+
- **`TFilter` maps straight to SQL.** Every property corresponds to one clause of the generated query - see the table below.
|
|
45
|
+
- **`where` takes a bare value or an operator object.** A bare value is implicit equality (`null` becomes `IS NULL`, an array becomes `IN`); an operator object keys into one of the operator families.
|
|
46
|
+
- **Multiple `where` keys are an implicit AND.** A dot-notation key (`'metadata.path'`) targets a JSON/JSONB column instead of a top-level column and accepts the same operators, with automatic numeric casting when the operand is a number.
|
|
47
|
+
- **A model's `settings.defaultFilter` merges into every query for that model**, narrowing-only - see [Default filter](#default-filter) below.
|
|
64
48
|
|
|
65
|
-
| Filter
|
|
66
|
-
|
|
49
|
+
| Filter property | SQL equivalent | Purpose |
|
|
50
|
+
|---|---|---|
|
|
67
51
|
| `where` | `WHERE` | Filter rows by conditions |
|
|
68
52
|
| `fields` | `SELECT col1, col2` | Select specific columns |
|
|
69
53
|
| `order` | `ORDER BY` | Sort results |
|
|
70
|
-
| `limit` | `LIMIT` | Restrict number of results |
|
|
71
|
-
| `skip` / `offset` | `OFFSET` | Skip rows for pagination |
|
|
72
|
-
| `include` | Separate relational query |
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
## Basic Example
|
|
76
|
-
|
|
77
|
-
```typescript
|
|
78
|
-
// Filter object
|
|
79
|
-
const filter = {
|
|
80
|
-
where: { status: 'active', role: 'admin' },
|
|
81
|
-
fields: ['id', 'name', 'email'],
|
|
82
|
-
order: ['createdAt DESC'],
|
|
83
|
-
limit: 10,
|
|
84
|
-
skip: 0
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
// Equivalent SQL
|
|
88
|
-
// SELECT "id", "name", "email"
|
|
89
|
-
// FROM "users"
|
|
90
|
-
// WHERE "status" = 'active' AND "role" = 'admin'
|
|
91
|
-
// ORDER BY "created_at" DESC
|
|
92
|
-
// LIMIT 10 OFFSET 0
|
|
93
|
-
```
|
|
54
|
+
| `limit` | `LIMIT` | Restrict the number of results |
|
|
55
|
+
| `skip` / `offset` | `OFFSET` | Skip rows for pagination (aliases - `skip` wins if both are given) |
|
|
56
|
+
| `include` | Separate relational query | Eager-load related rows ([Relations & Includes](../repositories/relations)) |
|
|
94
57
|
|
|
58
|
+
### The `where` operator families
|
|
95
59
|
|
|
96
|
-
|
|
60
|
+
| Family | Operators | Example |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| Comparison | `eq`, `ne`/`neq`, `gt`, `gte`, `lt`, `lte` | `{ age: { gte: 18, lte: 65 } }` |
|
|
63
|
+
| Null / presence | `is`, `isn`, `exists`, `notExists` | `{ deletedAt: null }` or `{ verifiedAt: { exists: true } }` |
|
|
64
|
+
| List | `in`/`inq`, `nin` | `{ status: { inq: ['active', 'pending'] } }` |
|
|
65
|
+
| Range | `between`, `notBetween` | `{ score: { between: [40, 60] } }` |
|
|
66
|
+
| Pattern | `like`, `nlike`, `ilike`, `nilike`, `regexp`, `iregexp` | `{ email: { ilike: '%@company.com' } }` |
|
|
67
|
+
| Logical | `and`, `or`, `not` | `{ or: [{ role: 'admin' }, { role: 'moderator' }] }` |
|
|
68
|
+
| Array (PostgreSQL) | `contains`, `containedBy`, `overlaps` | `{ tags: { contains: ['typescript'] } }` |
|
|
69
|
+
| JSON path | comparison, null, list, range, and pattern operators, on a `'column.path'` key | `{ 'metadata.score': { gt: 80 } }` |
|
|
97
70
|
|
|
98
|
-
|
|
99
|
-
|------------|---------------|
|
|
100
|
-
| Equals | `{ field: value }` or `{ field: { eq: value } }` |
|
|
101
|
-
| Not equals | `{ field: { ne: value } }` or `{ field: { neq: value } }` |
|
|
102
|
-
| Greater than | `{ field: { gt: value } }` |
|
|
103
|
-
| Greater or equal | `{ field: { gte: value } }` |
|
|
104
|
-
| Less than | `{ field: { lt: value } }` |
|
|
105
|
-
| Less or equal | `{ field: { lte: value } }` |
|
|
106
|
-
| Is null | `{ field: null }` or `{ field: { is: null } }` |
|
|
107
|
-
| Is not null | `{ field: { isn: null } }` or `{ field: { ne: null } }` |
|
|
108
|
-
| Field present (`IS NOT NULL`) | `{ field: { exists: true } }` (or `{ field: { notExists: false } }`) |
|
|
109
|
-
| Field missing/null (`IS NULL`) | `{ field: { exists: false } }` or `{ field: { notExists: true } }` |
|
|
110
|
-
| Negate a condition | `{ field: { not: value } }` (negates `eq`) or `{ field: { not: { gt: 10 } } }` |
|
|
111
|
-
| In list | `{ field: { in: [a, b, c] } }` or `{ field: { inq: [a, b, c] } }` |
|
|
112
|
-
| Not in list | `{ field: { nin: [a, b, c] } }` |
|
|
113
|
-
| Range | `{ field: { between: [min, max] } }` |
|
|
114
|
-
| Outside range | `{ field: { notBetween: [min, max] } }` |
|
|
115
|
-
| Contains pattern | `{ field: { like: '%pattern%' } }` |
|
|
116
|
-
| Not contains pattern | `{ field: { nlike: '%pattern%' } }` |
|
|
117
|
-
| Case-insensitive | `{ field: { ilike: '%pattern%' } }` |
|
|
118
|
-
| Not case-insensitive | `{ field: { nilike: '%pattern%' } }` |
|
|
119
|
-
| Regex match | `{ field: { regexp: '^pattern$' } }` |
|
|
120
|
-
| Case-insensitive regex | `{ field: { iregexp: '^pattern$' } }` |
|
|
121
|
-
| Array contains all | `{ arrayField: { contains: [a, b] } }` |
|
|
122
|
-
| Array is subset | `{ arrayField: { containedBy: [a, b, c] } }` |
|
|
123
|
-
| Array overlaps | `{ arrayField: { overlaps: [a, b] } }` |
|
|
124
|
-
| JSON nested | `{ 'jsonField.nested.path': value }` |
|
|
125
|
-
| JSON with operator | `{ 'jsonField.path': { gt: 10 } }` |
|
|
126
|
-
| AND conditions | `{ a: 1, b: 2 }` or `{ and: [{a: 1}, {b: 2}] }` |
|
|
127
|
-
| OR conditions | `{ or: [{ a: 1 }, { b: 2 }] }` |
|
|
128
|
-
| Include relation | `{ include: [{ relation: 'name' }] }` |
|
|
129
|
-
| Nested include | `{ include: [{ relation: 'a', scope: { include: [{ relation: 'b' }] } }] }` |
|
|
130
|
-
| Select fields | `{ fields: ['id', 'name'] }` or `{ fields: { id: true, name: true } }` |
|
|
131
|
-
| Order by | `{ order: ['field DESC'] }` |
|
|
132
|
-
| Order by JSON | `{ order: ['jsonField.path DESC'] }` |
|
|
133
|
-
| Paginate | `{ limit: 10, skip: 20 }` or `{ limit: 10, offset: 20 }` |
|
|
71
|
+
### Fields, order, and pagination
|
|
134
72
|
|
|
73
|
+
- **`fields`** selects columns - an array, or a `{ field: true }` object (inclusion-only; `false` is ignored).
|
|
74
|
+
- **`order`** takes `'field ASC'` / `'field DESC'` strings, including JSON paths.
|
|
75
|
+
- **`limit`**, when omitted, resolves through `query.limit ?? model settings.defaultLimit ?? 10`.
|
|
76
|
+
- **`skip` / `offset`** both map to SQL `OFFSET`.
|
|
135
77
|
|
|
136
|
-
|
|
78
|
+
### Default filter
|
|
137
79
|
|
|
138
|
-
|
|
80
|
+
- **Applies automatically.** A model's `settings.defaultFilter` merges into every read, update, and delete for that model.
|
|
81
|
+
- **Narrowing only.** When both the default and the caller's filter constrain the same field, the two conditions are AND-composed rather than one replacing the other - a caller can never widen or drop a scope like soft-delete or multi-tenancy by accident.
|
|
139
82
|
|
|
140
83
|
```typescript
|
|
141
|
-
{
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
]
|
|
151
|
-
}
|
|
84
|
+
import { model, BaseEntity } from '@venizia/ignis';
|
|
85
|
+
import { postTable } from '@/schemas';
|
|
86
|
+
|
|
87
|
+
@model({
|
|
88
|
+
type: 'entity',
|
|
89
|
+
settings: { defaultFilter: { where: { isDeleted: false } } },
|
|
90
|
+
})
|
|
91
|
+
export class Post extends BaseEntity<typeof Post.schema> {
|
|
92
|
+
static override schema = postTable;
|
|
152
93
|
}
|
|
153
|
-
```
|
|
154
94
|
|
|
155
|
-
|
|
95
|
+
await postRepository.find({ filter: { where: { status: 'published' } } });
|
|
96
|
+
// WHERE "isDeleted" = false AND "status" = 'published' - both conditions apply
|
|
156
97
|
|
|
157
|
-
|
|
158
|
-
{
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
{ email: { ilike: '%john%' } },
|
|
163
|
-
{ username: { ilike: '%john%' } }
|
|
164
|
-
]
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
### Date Range
|
|
170
|
-
|
|
171
|
-
```typescript
|
|
172
|
-
{
|
|
173
|
-
where: {
|
|
174
|
-
createdAt: {
|
|
175
|
-
gte: new Date('2024-01-01'),
|
|
176
|
-
lt: new Date('2024-02-01')
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
### Exclude Soft Deleted
|
|
183
|
-
|
|
184
|
-
```typescript
|
|
185
|
-
{
|
|
186
|
-
where: {
|
|
187
|
-
and: [
|
|
188
|
-
{ isDeleted: false },
|
|
189
|
-
{ status: 'active' }
|
|
190
|
-
]
|
|
191
|
-
}
|
|
192
|
-
}
|
|
98
|
+
await postRepository.find({
|
|
99
|
+
filter: { where: { status: 'published' } },
|
|
100
|
+
options: { shouldSkipDefaultFilter: true },
|
|
101
|
+
});
|
|
102
|
+
// WHERE "status" = 'published' - default filter skipped
|
|
193
103
|
```
|
|
194
104
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
]
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
## Performance Tips
|
|
235
|
-
|
|
236
|
-
1. **Index frequently filtered columns:**
|
|
237
|
-
```sql
|
|
238
|
-
CREATE INDEX idx_users_status ON users(status);
|
|
239
|
-
CREATE INDEX idx_posts_created_at ON posts(created_at DESC);
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
2. **Use `eq` instead of `like` when possible:**
|
|
243
|
-
```typescript
|
|
244
|
-
// Fast: Uses index
|
|
245
|
-
{ status: { eq: 'active' } }
|
|
246
|
-
|
|
247
|
-
// Slower: Full table scan
|
|
248
|
-
{ status: { like: 'active' } }
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
3. **Limit array contains operations:**
|
|
252
|
-
```typescript
|
|
253
|
-
// Better performance with smaller arrays
|
|
254
|
-
{ tags: { contains: ['typescript'] } } // Good
|
|
255
|
-
{ tags: { contains: ['tag1', 'tag2', /* ... 100 tags */] } } // Slow
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
4. **Use pagination for large result sets:**
|
|
259
|
-
```typescript
|
|
260
|
-
{
|
|
261
|
-
where: { isActive: true },
|
|
262
|
-
limit: 100,
|
|
263
|
-
skip: 0,
|
|
264
|
-
order: ['id ASC']
|
|
265
|
-
}
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
## See Also
|
|
270
|
-
|
|
271
|
-
- **Detailed Guides:**
|
|
272
|
-
- [Comparison Operators](./comparison-operators.md)
|
|
273
|
-
- [Logical Operators](./logical-operators.md)
|
|
274
|
-
- [Pattern Matching](./pattern-matching.md)
|
|
275
|
-
- [JSON Filtering](./json-filtering.md)
|
|
276
|
-
- [Array Operators](./array-operators.md)
|
|
277
|
-
|
|
278
|
-
- **Related References:**
|
|
279
|
-
- [Repositories](../repositories/) - Using filters in repository queries
|
|
280
|
-
- [Models](../models.md) - Defining model schemas
|
|
281
|
-
|
|
282
|
-
- **Usage Guides:**
|
|
283
|
-
- [Application Usage](./application-usage.md) - Filters in the full stack
|
|
284
|
-
- [Use Case Gallery](./use-cases.md) - Real-world examples
|
|
285
|
-
- [Pro Tips & Edge Cases](./tips.md) - Advanced patterns
|
|
286
|
-
|
|
287
|
-
- **Quick Reference:**
|
|
288
|
-
- [Main Quick Reference](/references/quick-reference.md) - All IGNIS APIs
|
|
105
|
+
## Operators
|
|
106
|
+
|
|
107
|
+
Each operator family and every long-form topic has its own page:
|
|
108
|
+
|
|
109
|
+
| Page | Covers |
|
|
110
|
+
|---|---|
|
|
111
|
+
| [Quick Reference](./quick-reference) | Every operator, one line each - the fast lookup |
|
|
112
|
+
| [Comparison Operators](./comparison-operators) | `eq`, `ne`/`neq`, `gt`, `gte`, `lt`, `lte` |
|
|
113
|
+
| [Null Operators](./null-operators) | `is`, `isn`, direct `null`, `exists`/`notExists` |
|
|
114
|
+
| [List Operators](./list-operators) | `in`/`inq`, `nin` |
|
|
115
|
+
| [Range Operators](./range-operators) | `between`, `notBetween` |
|
|
116
|
+
| [Pattern Matching](./pattern-matching) | `like`, `nlike`, `ilike`, `nilike`, `regexp`, `iregexp` |
|
|
117
|
+
| [Logical Operators](./logical-operators) | Implicit/explicit `and`, `or`, `not`, empty-group semantics |
|
|
118
|
+
| [Array Operators](./array-operators) | `contains`, `containedBy`, `overlaps` (PostgreSQL array columns) |
|
|
119
|
+
| [JSON Filtering](./json-filtering) | Dot-path queries into JSON/JSONB columns |
|
|
120
|
+
| [Fields, Order & Pagination](./fields-order-pagination) | `fields`, `order`, `limit`/`skip`/`offset`, `defaultLimit` |
|
|
121
|
+
| [Default Filter](./default-filter) | `settings.defaultFilter`, merge semantics, `shouldSkipDefaultFilter` |
|
|
122
|
+
| [Application Usage](./application-usage) | How a filter flows controller -> service -> repository |
|
|
123
|
+
| [Use Case Gallery](./use-cases) | Real-world filters with the SQL they produce |
|
|
124
|
+
| [Tips & Edge Cases](./tips) | Performance notes and common gotchas |
|
|
125
|
+
|
|
126
|
+
## See also
|
|
127
|
+
|
|
128
|
+
- [Repositories](../repositories/) - the `find`/`count`/`updateAll`/`deleteAll` verbs that take a `filter`
|
|
129
|
+
- [Models](/references/base/models) - `settings.defaultFilter` and `settings.hiddenProperties`
|
|
130
|
+
- [Building a CRUD API](/guides/tutorials/building-a-crud-api) - filters in a real endpoint
|
|
131
|
+
- [Quick Reference Card](/references/quick-reference.md) - all IGNIS APIs on one page
|
|
132
|
+
|
|
133
|
+
**Files:**
|
|
134
|
+
|
|
135
|
+
- [`packages/core/src/connectors/postgres/repositories/dialect/filter.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/dialect/filter.ts) - `FilterBuilder`, translates `TFilter` to Drizzle/SQL
|
|
136
|
+
- [`packages/core/src/base/repositories/query-schemas/filter.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/repositories/query-schemas/filter.ts) - `TFilter`/`TInclusion` types
|
|
137
|
+
- [`packages/core/src/base/repositories/common/operators.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/repositories/common/operators.ts) - `QueryOperators`/`Sorts` constants
|
|
@@ -246,6 +246,16 @@ CREATE INDEX idx_metadata_gin ON "Product" USING GIN ("metadata");
|
|
|
246
246
|
```
|
|
247
247
|
|
|
248
248
|
|
|
249
|
-
## See
|
|
249
|
+
## See also
|
|
250
250
|
|
|
251
|
-
- [
|
|
251
|
+
- [Filter System Overview](./) - the `filter` shape and the full `where` operator table
|
|
252
|
+
- [Fields, Order & Pagination](./fields-order-pagination) - JSON path ordering (`#>`, sorted by native JSONB type)
|
|
253
|
+
- [Pattern Matching](./pattern-matching) - `like`/`ilike`/`regexp` also work on a JSON path, with no numeric casting
|
|
254
|
+
- [Nested JSON Updates](../repositories/advanced.md#nested-json-updates) - writing to JSON paths
|
|
255
|
+
- [Quick Reference](./quick-reference) - every operator, one line each
|
|
256
|
+
|
|
257
|
+
**Files:**
|
|
258
|
+
|
|
259
|
+
- [`packages/core/src/connectors/postgres/repositories/dialect/filter.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/dialect/filter.ts) - `FilterBuilder`, `buildJsonWhereCondition`/`buildJsonOperatorConditions`/`buildJsonOrderBy`
|
|
260
|
+
- [`packages/core/src/connectors/postgres/repositories/dialect/internal/json-utils.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/dialect/internal/json-utils.ts) - `isJsonPath`, `parseJsonPath`, path validation regex
|
|
261
|
+
- [`packages/core/src/base/repositories/common/operators.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/repositories/common/operators.ts) - `QueryOperators` constants
|
|
@@ -57,16 +57,30 @@ Matches records where field value is in the provided array. `in` and `inq` are a
|
|
|
57
57
|
## Performance Tip
|
|
58
58
|
|
|
59
59
|
```typescript
|
|
60
|
+
import { userRepository } from '@/repositories';
|
|
61
|
+
|
|
60
62
|
// For very large arrays (1000+ items), consider chunking
|
|
61
|
-
const allIds =
|
|
63
|
+
const allIds: number[] = [ /* 5000 ids */ ];
|
|
62
64
|
|
|
63
65
|
const chunkSize = 500;
|
|
64
66
|
const results = [];
|
|
65
67
|
for (let i = 0; i < allIds.length; i += chunkSize) {
|
|
66
68
|
const chunk = allIds.slice(i, i + chunkSize);
|
|
67
|
-
const chunkResults = await
|
|
69
|
+
const chunkResults = await userRepository.find({
|
|
68
70
|
filter: { where: { id: { in: chunk } } }
|
|
69
71
|
});
|
|
70
72
|
results.push(...chunkResults);
|
|
71
73
|
}
|
|
72
74
|
```
|
|
75
|
+
|
|
76
|
+
## See also
|
|
77
|
+
|
|
78
|
+
- [Filter System Overview](./) - the `filter` shape and the full `where` operator table
|
|
79
|
+
- [Array Operators](./array-operators) - `contains`/`containedBy`/`overlaps` match against array COLUMNS, not to be confused with `in`/`nin`
|
|
80
|
+
- [Quick Reference](./quick-reference) - every operator, one line each
|
|
81
|
+
|
|
82
|
+
**Files:**
|
|
83
|
+
|
|
84
|
+
- [`packages/core/src/connectors/postgres/repositories/dialect/query.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/dialect/query.ts) - `PostgresQueryOperators.FNS`, per-operator SQL builders
|
|
85
|
+
- [`packages/core/src/connectors/postgres/repositories/dialect/filter.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/dialect/filter.ts) - `FilterBuilder`, translates `TFilter` to Drizzle/SQL
|
|
86
|
+
- [`packages/core/src/base/repositories/common/operators.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/repositories/common/operators.ts) - `QueryOperators` constants
|
|
@@ -191,3 +191,16 @@ The dedicated negation operators remain available and are often clearer for a si
|
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
193
|
```
|
|
194
|
+
|
|
195
|
+
## See also
|
|
196
|
+
|
|
197
|
+
- [Filter System Overview](./) - the `filter` shape and the full `where` operator table
|
|
198
|
+
- [Null Operators](./null-operators) - `isn`, one of the dedicated negation operators referenced above
|
|
199
|
+
- [Comparison Operators](./comparison-operators) - `ne`/`neq`, the other dedicated negation operators
|
|
200
|
+
- [Quick Reference](./quick-reference) - every operator, one line each
|
|
201
|
+
|
|
202
|
+
**Files:**
|
|
203
|
+
|
|
204
|
+
- [`packages/core/src/connectors/postgres/repositories/dialect/filter.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/dialect/filter.ts) - `FilterBuilder`, `buildLogicalGroupCondition`/`buildNotCondition`
|
|
205
|
+
- [`packages/core/src/connectors/postgres/repositories/dialect/query.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/dialect/query.ts) - `PostgresQueryOperators.FNS`, per-operator SQL builders
|
|
206
|
+
- [`packages/core/src/base/repositories/common/operators.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/repositories/common/operators.ts) - `QueryOperators` constants
|
|
@@ -135,3 +135,16 @@ All the IS NULL / IS NOT NULL syntaxes above are equivalent -- use whichever rea
|
|
|
135
135
|
// Find unverified users
|
|
136
136
|
{ where: { emailVerifiedAt: { is: null } } }
|
|
137
137
|
```
|
|
138
|
+
|
|
139
|
+
## See also
|
|
140
|
+
|
|
141
|
+
- [Filter System Overview](./) - the `filter` shape and the full `where` operator table
|
|
142
|
+
- [Logical Operators](./logical-operators) - `not`, the general-purpose negation operator
|
|
143
|
+
- [JSON Filtering](./json-filtering) - `exists` also works over a `'column.path'` key
|
|
144
|
+
- [Quick Reference](./quick-reference) - every operator, one line each
|
|
145
|
+
|
|
146
|
+
**Files:**
|
|
147
|
+
|
|
148
|
+
- [`packages/core/src/connectors/postgres/repositories/dialect/query.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/dialect/query.ts) - `PostgresQueryOperators.FNS`, per-operator SQL builders
|
|
149
|
+
- [`packages/core/src/connectors/postgres/repositories/dialect/filter.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/dialect/filter.ts) - `FilterBuilder`, translates `TFilter` to Drizzle/SQL
|
|
150
|
+
- [`packages/core/src/base/repositories/common/operators.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/repositories/common/operators.ts) - `QueryOperators` constants
|