@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.
Files changed (142) hide show
  1. package/README.md +14 -14
  2. package/content/best-practices/api-usage-examples.md +39 -19
  3. package/content/best-practices/architectural-patterns.md +22 -11
  4. package/content/best-practices/architecture-decisions.md +29 -16
  5. package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
  6. package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
  7. package/content/best-practices/code-style-standards/control-flow.md +33 -1
  8. package/content/best-practices/code-style-standards/documentation.md +28 -8
  9. package/content/best-practices/code-style-standards/function-patterns.md +15 -4
  10. package/content/best-practices/code-style-standards/index.md +7 -2
  11. package/content/best-practices/code-style-standards/naming-conventions.md +26 -2
  12. package/content/best-practices/code-style-standards/route-definitions.md +9 -5
  13. package/content/best-practices/code-style-standards/tooling.md +14 -1
  14. package/content/best-practices/code-style-standards/type-safety.md +39 -6
  15. package/content/best-practices/common-pitfalls.md +59 -35
  16. package/content/best-practices/contribution-workflow.md +6 -2
  17. package/content/best-practices/data-modeling.md +78 -62
  18. package/content/best-practices/deployment-strategies.md +56 -19
  19. package/content/best-practices/error-handling.md +182 -93
  20. package/content/best-practices/index.md +6 -0
  21. package/content/best-practices/performance-optimization.md +26 -13
  22. package/content/best-practices/security-guidelines.md +35 -9
  23. package/content/best-practices/testing-strategies.md +7 -2
  24. package/content/best-practices/troubleshooting-tips.md +27 -17
  25. package/content/extensions/components/api-reference.md +107 -322
  26. package/content/extensions/components/authentication/api.md +454 -603
  27. package/content/extensions/components/authentication/errors.md +121 -498
  28. package/content/extensions/components/authentication/index.md +88 -801
  29. package/content/extensions/components/authentication/usage.md +207 -956
  30. package/content/extensions/components/authorization/api.md +736 -656
  31. package/content/extensions/components/authorization/errors.md +168 -206
  32. package/content/extensions/components/authorization/index.md +82 -797
  33. package/content/extensions/components/authorization/usage.md +194 -527
  34. package/content/extensions/components/health-check.md +71 -243
  35. package/content/extensions/components/mail/api.md +504 -287
  36. package/content/extensions/components/mail/errors.md +73 -61
  37. package/content/extensions/components/mail/index.md +96 -467
  38. package/content/extensions/components/mail/usage.md +130 -172
  39. package/content/extensions/components/request-tracker.md +66 -173
  40. package/content/extensions/components/socket-io/api.md +195 -13
  41. package/content/extensions/components/socket-io/errors.md +3 -3
  42. package/content/extensions/components/socket-io/index.md +50 -337
  43. package/content/extensions/components/socket-io/usage.md +143 -26
  44. package/content/extensions/components/static-asset/api.md +410 -142
  45. package/content/extensions/components/static-asset/errors.md +110 -53
  46. package/content/extensions/components/static-asset/index.md +79 -608
  47. package/content/extensions/components/static-asset/usage.md +180 -300
  48. package/content/extensions/components/websocket/api.md +275 -399
  49. package/content/extensions/components/websocket/errors.md +47 -56
  50. package/content/extensions/components/websocket/index.md +74 -407
  51. package/content/extensions/components/websocket/usage.md +110 -341
  52. package/content/extensions/helpers/cron/index.md +51 -160
  53. package/content/extensions/helpers/crypto/index.md +62 -483
  54. package/content/extensions/helpers/crypto/reference.md +456 -0
  55. package/content/extensions/helpers/env/index.md +60 -178
  56. package/content/extensions/helpers/error/index.md +221 -207
  57. package/content/extensions/helpers/inversion/index.md +65 -556
  58. package/content/extensions/helpers/inversion/reference.md +522 -0
  59. package/content/extensions/helpers/kafka/admin.md +20 -1
  60. package/content/extensions/helpers/kafka/compile-binary.md +41 -35
  61. package/content/extensions/helpers/kafka/consumer.md +54 -20
  62. package/content/extensions/helpers/kafka/examples.md +21 -16
  63. package/content/extensions/helpers/kafka/index.md +80 -610
  64. package/content/extensions/helpers/kafka/producer.md +134 -5
  65. package/content/extensions/helpers/kafka/schema-registry.md +45 -70
  66. package/content/extensions/helpers/logger/hf-logger.md +193 -0
  67. package/content/extensions/helpers/logger/index.md +64 -563
  68. package/content/extensions/helpers/logger/pino.md +85 -0
  69. package/content/extensions/helpers/logger/reference.md +746 -0
  70. package/content/extensions/helpers/network/api.md +241 -195
  71. package/content/extensions/helpers/network/index.md +72 -530
  72. package/content/extensions/helpers/queue/index.md +72 -900
  73. package/content/extensions/helpers/queue/reference.md +467 -0
  74. package/content/extensions/helpers/redis/index.md +73 -645
  75. package/content/extensions/helpers/redis/reference.md +727 -0
  76. package/content/extensions/helpers/secrets/index.md +66 -0
  77. package/content/extensions/helpers/socket-io/api.md +305 -203
  78. package/content/extensions/helpers/socket-io/index.md +66 -432
  79. package/content/extensions/helpers/storage/api.md +564 -462
  80. package/content/extensions/helpers/storage/index.md +77 -573
  81. package/content/extensions/helpers/types/index.md +66 -499
  82. package/content/extensions/helpers/types/reference.md +650 -0
  83. package/content/extensions/helpers/uid/index.md +58 -227
  84. package/content/extensions/helpers/websocket/api.md +329 -216
  85. package/content/extensions/helpers/websocket/index.md +65 -503
  86. package/content/extensions/helpers/worker-thread/index.md +58 -396
  87. package/content/extensions/helpers/worker-thread/reference.md +428 -0
  88. package/content/guides/core-concepts/persistent/models.md +1 -1
  89. package/content/guides/core-concepts/persistent/search-typesense.md +3 -3
  90. package/content/guides/core-concepts/persistent/transactions.md +1 -1
  91. package/content/guides/core-concepts/secrets-vault.md +177 -0
  92. package/content/guides/core-concepts/services.md +1 -1
  93. package/content/guides/migrations/redis-helpers-migration.md +1 -1
  94. package/content/guides/migrations/unified-connectors-migration.md +2 -2
  95. package/content/guides/tutorials/ecommerce-api.md +3 -8
  96. package/content/references/base/application.md +1 -1
  97. package/content/references/base/connectors.md +79 -136
  98. package/content/references/base/datasources-reference.md +599 -0
  99. package/content/references/base/datasources.md +84 -444
  100. package/content/references/base/dependency-injection.md +17 -39
  101. package/content/references/base/filter-system/application-usage.md +69 -121
  102. package/content/references/base/filter-system/array-operators.md +12 -0
  103. package/content/references/base/filter-system/comparison-operators.md +12 -0
  104. package/content/references/base/filter-system/default-filter.md +136 -348
  105. package/content/references/base/filter-system/fields-order-pagination.md +38 -16
  106. package/content/references/base/filter-system/index.md +106 -257
  107. package/content/references/base/filter-system/json-filtering.md +12 -2
  108. package/content/references/base/filter-system/list-operators.md +16 -2
  109. package/content/references/base/filter-system/logical-operators.md +13 -0
  110. package/content/references/base/filter-system/null-operators.md +13 -0
  111. package/content/references/base/filter-system/pattern-matching.md +12 -0
  112. package/content/references/base/filter-system/quick-reference.md +11 -2
  113. package/content/references/base/filter-system/range-operators.md +12 -0
  114. package/content/references/base/filter-system/tips.md +70 -133
  115. package/content/references/base/filter-system/use-cases.md +156 -233
  116. package/content/references/base/middlewares.md +35 -21
  117. package/content/references/base/models-reference.md +886 -0
  118. package/content/references/base/models.md +80 -1452
  119. package/content/references/base/repositories/advanced.md +156 -192
  120. package/content/references/base/repositories/index.md +77 -650
  121. package/content/references/base/repositories/mixins.md +22 -18
  122. package/content/references/base/repositories/relations.md +123 -171
  123. package/content/references/base/repositories/soft-deletable.md +58 -56
  124. package/content/references/base/secrets.md +263 -0
  125. package/content/references/base/services.md +2 -2
  126. package/content/references/configuration/environment-variables.md +48 -4
  127. package/content/references/configuration/index.md +49 -31
  128. package/content/references/quick-reference.md +3 -16
  129. package/content/references/utilities/crypto.md +35 -76
  130. package/content/references/utilities/date.md +33 -73
  131. package/content/references/utilities/index.md +1 -1
  132. package/content/references/utilities/jsx-reference.md +298 -0
  133. package/content/references/utilities/jsx.md +82 -525
  134. package/content/references/utilities/module.md +29 -62
  135. package/content/references/utilities/parse.md +34 -64
  136. package/content/references/utilities/performance.md +33 -58
  137. package/content/references/utilities/promise.md +28 -62
  138. package/content/references/utilities/request.md +57 -218
  139. package/content/references/utilities/schema.md +43 -137
  140. package/content/references/utilities/statuses-reference.md +361 -0
  141. package/content/references/utilities/statuses.md +63 -667
  142. 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
- await repository.find({
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 repository.find({
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 repository.find({
51
+ await userRepository.find({
50
52
  filter: { order: ['createdAt DESC'] }
51
53
  });
52
54
 
53
55
  // Multiple columns
54
- await repository.find({
56
+ await userRepository.find({
55
57
  filter: { order: ['status ASC', 'createdAt DESC'] }
56
58
  });
57
59
 
58
60
  // Default direction is ASC
59
- await repository.find({
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 repository.find({
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 repository.find({
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 repository.find({
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 repository.find({
114
+ await userRepository.find({
113
115
  filter: { limit: 10, skip: 10 }
114
116
  });
115
117
 
116
118
  // Using offset (equivalent to skip)
117
- await repository.find({
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 repository.find({
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 repository.find({
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 repository.find({
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 repository.find({
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 repository.find({
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 Overview
3
- description: Complete reference for the IGNIS filter system
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
- Complete reference for the IGNIS filter system - operators, JSON filtering, array operators, default filters, and query patterns.
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
- > [!NOTE]
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
- ## Prerequisites
13
+ A filter shapes one query - `where` picks rows, `fields` picks columns, `order` sorts, `limit` bounds the result:
18
14
 
19
- Before reading this document, you should understand:
20
-
21
- - [Repositories](../repositories/) - Basic repository operations (find, create, update, delete)
22
- - [Models](../models.md) - Entity definitions and schemas
23
- - SQL basics - Understanding of WHERE clauses and operators
24
- - TypeScript type system - Type safety and inference
25
-
26
- ## Documentation
27
-
28
- | Guide | Description |
29
- |-------|-------------|
30
- | [**Quick Reference**](./quick-reference.md) | **Single-page cheat sheet of all operators** |
31
- | [Comparison Operators](./comparison-operators.md) | Equality, range, null checks |
32
- | [Pattern Matching](./pattern-matching.md) | LIKE, ILIKE, regex |
33
- | [Logical Operators](./logical-operators.md) | AND, OR combinations |
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
- The `TFilter<T>` object is the core mechanism for querying data in IGNIS. It provides a structured way to express complex queries without writing raw SQL.
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
- ```typescript
51
- type TFilter<T> = {
52
- where?: TWhere<T>; // Query conditions (SQL WHERE)
53
- fields?: TFields<T>; // Column selection (SQL SELECT)
54
- order?: string[]; // Sorting (SQL ORDER BY)
55
- limit?: number; // Max results (SQL LIMIT, default: 10)
56
- skip?: number; // Pagination offset (SQL OFFSET)
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
- ## SQL Mapping Overview
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 Property | SQL Equivalent | Purpose |
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 | Include related data |
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
- ## Quick Reference
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
- | Want to... | Filter Syntax |
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
- ## Common Filter Patterns
78
+ ### Default filter
137
79
 
138
- ### Multi-Condition Search
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
- where: {
143
- and: [
144
- { age: { gte: 18, lte: 65 } }, // Between 18 and 65
145
- { status: { in: ['active', 'pending'] } },
146
- { or: [
147
- { email: { ilike: '%@company.com' } },
148
- { role: 'admin' }
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
- ### Text Search
95
+ await postRepository.find({ filter: { where: { status: 'published' } } });
96
+ // WHERE "isDeleted" = false AND "status" = 'published' - both conditions apply
156
97
 
157
- ```typescript
158
- {
159
- where: {
160
- or: [
161
- { name: { ilike: '%john%' } },
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
- ### Multi-Tenant Filtering
196
-
197
- ```typescript
198
- {
199
- where: {
200
- and: [
201
- { tenantId: currentTenantId },
202
- { isActive: true }
203
- ]
204
- }
205
- }
206
- ```
207
-
208
-
209
- ## Operator Precedence
210
-
211
- When combining operators, IGNIS follows standard SQL precedence:
212
-
213
- 1. **AND** - Higher precedence
214
- 2. **OR** - Lower precedence
215
-
216
- Use explicit nesting (via `and`/`or` arrays) for clarity:
217
-
218
- ```typescript
219
- // Clear precedence
220
- {
221
- where: {
222
- and: [
223
- { status: 'active' },
224
- { or: [
225
- { role: 'admin' },
226
- { role: 'moderator' }
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 Also
249
+ ## See also
250
250
 
251
- - [Nested JSON Updates](../repositories/advanced.md#nested-json-updates) - Updating JSON fields
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 = getLargeIdList(); // 5000 IDs
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 repository.find({
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