@venizia/ignis-docs 0.0.7-2 → 0.0.8-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 (161) hide show
  1. package/dist/mcp-server/common/paths.d.ts +4 -2
  2. package/dist/mcp-server/common/paths.d.ts.map +1 -1
  3. package/dist/mcp-server/common/paths.js +8 -6
  4. package/dist/mcp-server/common/paths.js.map +1 -1
  5. package/dist/mcp-server/tools/docs/get-document-content.tool.d.ts +1 -1
  6. package/dist/mcp-server/tools/docs/get-document-content.tool.d.ts.map +1 -1
  7. package/dist/mcp-server/tools/docs/get-document-content.tool.js +7 -7
  8. package/dist/mcp-server/tools/docs/get-document-metadata.tool.js +3 -3
  9. package/dist/mcp-server/tools/docs/get-package-overview.tool.d.ts +1 -1
  10. package/dist/mcp-server/tools/docs/get-package-overview.tool.js +1 -1
  11. package/package.json +1 -1
  12. package/wiki/best-practices/api-usage-examples.md +9 -9
  13. package/wiki/best-practices/architectural-patterns.md +19 -3
  14. package/wiki/best-practices/architecture-decisions.md +6 -6
  15. package/wiki/best-practices/code-style-standards/advanced-patterns.md +1 -1
  16. package/wiki/best-practices/code-style-standards/control-flow.md +1 -1
  17. package/wiki/best-practices/code-style-standards/function-patterns.md +2 -2
  18. package/wiki/best-practices/code-style-standards/index.md +2 -2
  19. package/wiki/best-practices/code-style-standards/naming-conventions.md +1 -1
  20. package/wiki/best-practices/code-style-standards/route-definitions.md +4 -4
  21. package/wiki/best-practices/data-modeling.md +1 -1
  22. package/wiki/best-practices/deployment-strategies.md +1 -1
  23. package/wiki/best-practices/error-handling.md +2 -2
  24. package/wiki/best-practices/performance-optimization.md +3 -3
  25. package/wiki/best-practices/security-guidelines.md +2 -2
  26. package/wiki/best-practices/troubleshooting-tips.md +1 -1
  27. package/wiki/{references → extensions}/components/authentication/api.md +12 -20
  28. package/wiki/{references → extensions}/components/authentication/errors.md +1 -1
  29. package/wiki/{references → extensions}/components/authentication/index.md +5 -8
  30. package/wiki/{references → extensions}/components/authentication/usage.md +20 -36
  31. package/wiki/{references → extensions}/components/authorization/api.md +62 -13
  32. package/wiki/{references → extensions}/components/authorization/errors.md +12 -7
  33. package/wiki/{references → extensions}/components/authorization/index.md +93 -6
  34. package/wiki/{references → extensions}/components/authorization/usage.md +42 -4
  35. package/wiki/{references → extensions}/components/health-check.md +5 -4
  36. package/wiki/{references → extensions}/components/index.md +2 -0
  37. package/wiki/{references → extensions}/components/mail/index.md +1 -1
  38. package/wiki/{references → extensions}/components/request-tracker.md +1 -1
  39. package/wiki/{references → extensions}/components/socket-io/api.md +2 -2
  40. package/wiki/{references → extensions}/components/socket-io/errors.md +2 -0
  41. package/wiki/{references → extensions}/components/socket-io/index.md +24 -20
  42. package/wiki/{references → extensions}/components/socket-io/usage.md +2 -2
  43. package/wiki/{references → extensions}/components/static-asset/api.md +14 -15
  44. package/wiki/{references → extensions}/components/static-asset/errors.md +3 -1
  45. package/wiki/{references → extensions}/components/static-asset/index.md +158 -89
  46. package/wiki/{references → extensions}/components/static-asset/usage.md +8 -5
  47. package/wiki/{references → extensions}/components/swagger.md +3 -3
  48. package/wiki/{references → extensions}/components/template/index.md +4 -4
  49. package/wiki/{references → extensions}/components/template/setup-page.md +1 -1
  50. package/wiki/{references → extensions}/components/template/single-page.md +1 -1
  51. package/wiki/{references → extensions}/components/websocket/api.md +7 -6
  52. package/wiki/{references → extensions}/components/websocket/errors.md +17 -3
  53. package/wiki/{references → extensions}/components/websocket/index.md +17 -11
  54. package/wiki/{references → extensions}/components/websocket/usage.md +2 -2
  55. package/wiki/{references → extensions}/helpers/crypto/index.md +1 -1
  56. package/wiki/{references → extensions}/helpers/env/index.md +9 -5
  57. package/wiki/{references → extensions}/helpers/error/index.md +2 -7
  58. package/wiki/{references → extensions}/helpers/index.md +18 -6
  59. package/wiki/{references → extensions}/helpers/kafka/admin.md +33 -16
  60. package/wiki/extensions/helpers/kafka/consumer.md +384 -0
  61. package/wiki/extensions/helpers/kafka/examples.md +361 -0
  62. package/wiki/extensions/helpers/kafka/index.md +639 -0
  63. package/wiki/{references → extensions}/helpers/kafka/producer.md +100 -96
  64. package/wiki/extensions/helpers/kafka/schema-registry.md +214 -0
  65. package/wiki/{references → extensions}/helpers/logger/index.md +2 -2
  66. package/wiki/{references → extensions}/helpers/queue/index.md +400 -4
  67. package/wiki/{references → extensions}/helpers/storage/api.md +170 -10
  68. package/wiki/{references → extensions}/helpers/storage/index.md +44 -8
  69. package/wiki/{references → extensions}/helpers/template/index.md +1 -1
  70. package/wiki/{references → extensions}/helpers/testing/index.md +4 -4
  71. package/wiki/{references → extensions}/helpers/types/index.md +63 -16
  72. package/wiki/{references → extensions}/helpers/websocket/index.md +1 -1
  73. package/wiki/extensions/index.md +48 -0
  74. package/wiki/guides/core-concepts/application/bootstrapping.md +55 -37
  75. package/wiki/guides/core-concepts/application/index.md +95 -35
  76. package/wiki/guides/core-concepts/components-guide.md +23 -19
  77. package/wiki/guides/core-concepts/components.md +34 -10
  78. package/wiki/guides/core-concepts/dependency-injection.md +99 -34
  79. package/wiki/guides/core-concepts/grpc-controllers.md +295 -0
  80. package/wiki/guides/core-concepts/persistent/datasources.md +27 -8
  81. package/wiki/guides/core-concepts/persistent/models.md +43 -1
  82. package/wiki/guides/core-concepts/persistent/repositories.md +75 -8
  83. package/wiki/guides/core-concepts/persistent/transactions.md +38 -8
  84. package/wiki/guides/core-concepts/{controllers.md → rest-controllers.md} +30 -33
  85. package/wiki/guides/core-concepts/services.md +19 -5
  86. package/wiki/guides/get-started/5-minute-quickstart.md +6 -7
  87. package/wiki/guides/get-started/philosophy.md +1 -1
  88. package/wiki/guides/index.md +2 -2
  89. package/wiki/guides/reference/glossary.md +7 -7
  90. package/wiki/guides/reference/mcp-docs-server.md +1 -1
  91. package/wiki/guides/tutorials/building-a-crud-api.md +2 -2
  92. package/wiki/guides/tutorials/complete-installation.md +17 -14
  93. package/wiki/guides/tutorials/ecommerce-api.md +18 -18
  94. package/wiki/guides/tutorials/realtime-chat.md +8 -8
  95. package/wiki/guides/tutorials/testing.md +2 -2
  96. package/wiki/index.md +4 -3
  97. package/wiki/references/base/application.md +341 -21
  98. package/wiki/references/base/bootstrapping.md +43 -13
  99. package/wiki/references/base/components.md +259 -8
  100. package/wiki/references/base/controllers.md +556 -253
  101. package/wiki/references/base/datasources.md +159 -79
  102. package/wiki/references/base/dependency-injection.md +299 -48
  103. package/wiki/references/base/filter-system/application-usage.md +18 -2
  104. package/wiki/references/base/filter-system/array-operators.md +14 -6
  105. package/wiki/references/base/filter-system/comparison-operators.md +9 -3
  106. package/wiki/references/base/filter-system/default-filter.md +28 -3
  107. package/wiki/references/base/filter-system/fields-order-pagination.md +17 -13
  108. package/wiki/references/base/filter-system/index.md +169 -11
  109. package/wiki/references/base/filter-system/json-filtering.md +51 -18
  110. package/wiki/references/base/filter-system/list-operators.md +4 -3
  111. package/wiki/references/base/filter-system/logical-operators.md +7 -2
  112. package/wiki/references/base/filter-system/null-operators.md +50 -0
  113. package/wiki/references/base/filter-system/quick-reference.md +82 -243
  114. package/wiki/references/base/filter-system/range-operators.md +7 -1
  115. package/wiki/references/base/filter-system/tips.md +34 -7
  116. package/wiki/references/base/filter-system/use-cases.md +6 -5
  117. package/wiki/references/base/grpc-controllers.md +984 -0
  118. package/wiki/references/base/index.md +32 -24
  119. package/wiki/references/base/middleware.md +347 -0
  120. package/wiki/references/base/models.md +390 -46
  121. package/wiki/references/base/providers.md +14 -14
  122. package/wiki/references/base/repositories/advanced.md +84 -69
  123. package/wiki/references/base/repositories/index.md +447 -12
  124. package/wiki/references/base/repositories/mixins.md +103 -98
  125. package/wiki/references/base/repositories/relations.md +129 -45
  126. package/wiki/references/base/repositories/soft-deletable.md +104 -23
  127. package/wiki/references/base/services.md +94 -14
  128. package/wiki/references/index.md +12 -10
  129. package/wiki/references/quick-reference.md +98 -65
  130. package/wiki/references/utilities/crypto.md +21 -4
  131. package/wiki/references/utilities/date.md +25 -7
  132. package/wiki/references/utilities/index.md +26 -24
  133. package/wiki/references/utilities/jsx.md +54 -54
  134. package/wiki/references/utilities/module.md +8 -6
  135. package/wiki/references/utilities/parse.md +16 -9
  136. package/wiki/references/utilities/performance.md +22 -7
  137. package/wiki/references/utilities/promise.md +19 -16
  138. package/wiki/references/utilities/request.md +48 -26
  139. package/wiki/references/utilities/schema.md +69 -6
  140. package/wiki/references/utilities/statuses.md +131 -140
  141. package/wiki/references/helpers/kafka/consumer.md +0 -473
  142. package/wiki/references/helpers/kafka/examples.md +0 -234
  143. package/wiki/references/helpers/kafka/index.md +0 -482
  144. /package/wiki/{references → extensions}/components/mail/api.md +0 -0
  145. /package/wiki/{references → extensions}/components/mail/errors.md +0 -0
  146. /package/wiki/{references → extensions}/components/mail/usage.md +0 -0
  147. /package/wiki/{references → extensions}/components/template/api-page.md +0 -0
  148. /package/wiki/{references → extensions}/components/template/errors-page.md +0 -0
  149. /package/wiki/{references → extensions}/components/template/usage-page.md +0 -0
  150. /package/wiki/{references → extensions}/helpers/cron/index.md +0 -0
  151. /package/wiki/{references → extensions}/helpers/inversion/index.md +0 -0
  152. /package/wiki/{references → extensions}/helpers/network/api.md +0 -0
  153. /package/wiki/{references → extensions}/helpers/network/index.md +0 -0
  154. /package/wiki/{references → extensions}/helpers/redis/index.md +0 -0
  155. /package/wiki/{references → extensions}/helpers/socket-io/api.md +0 -0
  156. /package/wiki/{references → extensions}/helpers/socket-io/index.md +0 -0
  157. /package/wiki/{references → extensions}/helpers/template/single-page.md +0 -0
  158. /package/wiki/{references → extensions}/helpers/uid/index.md +0 -0
  159. /package/wiki/{references → extensions}/helpers/websocket/api.md +0 -0
  160. /package/wiki/{references → extensions}/helpers/worker-thread/index.md +0 -0
  161. /package/wiki/{references → extensions}/src-details/mcp-server.md +0 -0
@@ -28,21 +28,17 @@ await repo.find({
28
28
  ### Object Format
29
29
 
30
30
  ```typescript
31
- // Include specific fields
31
+ // Include specific fields (only keys with `true` are selected)
32
32
  await repo.find({
33
33
  filter: {
34
34
  fields: { id: true, email: true, name: true }
35
35
  }
36
36
  });
37
-
38
- // Exclude specific fields
39
- await repo.find({
40
- filter: {
41
- fields: { password: false, secret: false }
42
- }
43
- });
44
37
  ```
45
38
 
39
+ > [!NOTE]
40
+ > The object format only supports inclusion (`true` values). Keys set to `false` are simply ignored -- they do not exclude fields. To select specific fields, list the ones you want with `true` or use the array format.
41
+
46
42
 
47
43
  ## Ordering
48
44
 
@@ -65,6 +61,14 @@ await repo.find({
65
61
  });
66
62
  ```
67
63
 
64
+ ### Valid Directions
65
+
66
+ Only `ASC` and `DESC` (case-insensitive) are accepted. Invalid directions throw an error:
67
+
68
+ ```
69
+ Error: Invalid direction: 'RANDOM' | Expected: 'ASC' or 'DESC'
70
+ ```
71
+
68
72
  ### JSON Path Ordering
69
73
 
70
74
  Order by nested fields in JSON columns:
@@ -96,10 +100,10 @@ await repo.find({
96
100
 
97
101
  ### Limit and Skip/Offset
98
102
 
99
- Both `skip` and `offset` are supported as aliases they both map to the SQL `OFFSET` clause. When both are provided, `skip` takes precedence.
103
+ Both `skip` and `offset` are supported as aliases -- they both map to the SQL `OFFSET` clause. When both are provided, `skip` takes precedence.
100
104
 
101
105
  ```typescript
102
- // First 10 results
106
+ // First 10 results (default limit is 10)
103
107
  await repo.find({
104
108
  filter: { limit: 10 }
105
109
  });
@@ -126,7 +130,7 @@ await repo.find({
126
130
  ```
127
131
 
128
132
  > [!TIP]
129
- > Always use `limit` for public-facing endpoints to prevent memory exhaustion.
133
+ > Always use `limit` for public-facing endpoints to prevent memory exhaustion. The default limit is 10 if not specified.
130
134
 
131
135
  ### Pagination Helper
132
136
 
@@ -186,7 +190,7 @@ const contentRange = data.length > 0
186
190
  : `records */${range.total}`;
187
191
 
188
192
  res.setHeader('Content-Range', contentRange);
189
- // "records 20-29/100"
193
+ // -> "records 20-29/100"
190
194
  ```
191
195
 
192
196
  ### TDataRange Type
@@ -242,5 +246,5 @@ const { data, range } = await repo.find({
242
246
  });
243
247
 
244
248
  console.log(`Showing ${range.start}-${range.end} of ${range.total}`);
245
- // "Showing 0-19 of 150"
249
+ // -> "Showing 0-19 of 150"
246
250
  ```
@@ -45,17 +45,17 @@ Before reading this document, you should understand:
45
45
 
46
46
  ## Filter Structure
47
47
 
48
- The `Filter<T>` object is the core mechanism for querying data in Ignis. It provides a structured, type-safe way to express complex queries without writing raw SQL.
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.
49
49
 
50
50
  ```typescript
51
51
  type TFilter<T> = {
52
52
  where?: TWhere<T>; // Query conditions (SQL WHERE)
53
53
  fields?: TFields<T>; // Column selection (SQL SELECT)
54
- order?: string[]; // Sorting (SQL ORDER BY)
55
- limit?: number; // Max results (SQL LIMIT)
54
+ order?: string[]; // Sorting (SQL ORDER BY)
55
+ limit?: number; // Max results (SQL LIMIT, default: 10)
56
56
  skip?: number; // Pagination offset (SQL OFFSET)
57
57
  offset?: number; // Alias for skip
58
- include?: TInclusion[]; // Related data (SQL JOIN / subqueries)
58
+ include?: TInclusion[]; // Related data (Drizzle relational queries)
59
59
  };
60
60
  ```
61
61
 
@@ -69,7 +69,7 @@ type TFilter<T> = {
69
69
  | `order` | `ORDER BY` | Sort results |
70
70
  | `limit` | `LIMIT` | Restrict number of results |
71
71
  | `skip` / `offset` | `OFFSET` | Skip rows for pagination |
72
- | `include` | `JOIN` / subquery | Include related data |
72
+ | `include` | Separate relational query | Include related data |
73
73
 
74
74
 
75
75
  ## Basic Example
@@ -86,7 +86,7 @@ const filter = {
86
86
 
87
87
  // Equivalent SQL
88
88
  // SELECT "id", "name", "email"
89
- // FROM "User"
89
+ // FROM "users"
90
90
  // WHERE "status" = 'active' AND "role" = 'admin'
91
91
  // ORDER BY "created_at" DESC
92
92
  // LIMIT 10 OFFSET 0
@@ -98,20 +98,23 @@ const filter = {
98
98
  | Want to... | Filter Syntax |
99
99
  |------------|---------------|
100
100
  | Equals | `{ field: value }` or `{ field: { eq: value } }` |
101
- | Not equals | `{ field: { ne: value } }` |
101
+ | Not equals | `{ field: { ne: value } }` or `{ field: { neq: value } }` |
102
102
  | Greater than | `{ field: { gt: value } }` |
103
103
  | Greater or equal | `{ field: { gte: value } }` |
104
104
  | Less than | `{ field: { lt: value } }` |
105
105
  | Less or equal | `{ field: { lte: value } }` |
106
106
  | Is null | `{ field: null }` or `{ field: { is: null } }` |
107
- | Is not null | `{ field: { isn: null } }` |
108
- | In list | `{ field: { in: [a, b, c] } }` |
107
+ | Is not null | `{ field: { isn: null } }` or `{ field: { ne: null } }` |
108
+ | In list | `{ field: { in: [a, b, c] } }` or `{ field: { inq: [a, b, c] } }` |
109
109
  | Not in list | `{ field: { nin: [a, b, c] } }` |
110
110
  | Range | `{ field: { between: [min, max] } }` |
111
111
  | Outside range | `{ field: { notBetween: [min, max] } }` |
112
112
  | Contains pattern | `{ field: { like: '%pattern%' } }` |
113
+ | Not contains pattern | `{ field: { nlike: '%pattern%' } }` |
113
114
  | Case-insensitive | `{ field: { ilike: '%pattern%' } }` |
115
+ | Not case-insensitive | `{ field: { nilike: '%pattern%' } }` |
114
116
  | Regex match | `{ field: { regexp: '^pattern$' } }` |
117
+ | Case-insensitive regex | `{ field: { iregexp: '^pattern$' } }` |
115
118
  | Array contains all | `{ arrayField: { contains: [a, b] } }` |
116
119
  | Array is subset | `{ arrayField: { containedBy: [a, b, c] } }` |
117
120
  | Array overlaps | `{ arrayField: { overlaps: [a, b] } }` |
@@ -121,7 +124,162 @@ const filter = {
121
124
  | OR conditions | `{ or: [{ a: 1 }, { b: 2 }] }` |
122
125
  | Include relation | `{ include: [{ relation: 'name' }] }` |
123
126
  | Nested include | `{ include: [{ relation: 'a', scope: { include: [{ relation: 'b' }] } }] }` |
124
- | Select fields | `{ fields: ['id', 'name'] }` |
127
+ | Select fields | `{ fields: ['id', 'name'] }` or `{ fields: { id: true, name: true } }` |
125
128
  | Order by | `{ order: ['field DESC'] }` |
126
129
  | Order by JSON | `{ order: ['jsonField.path DESC'] }` |
127
- | Paginate | `{ limit: 10, skip: 20 }` |
130
+ | Paginate | `{ limit: 10, skip: 20 }` or `{ limit: 10, offset: 20 }` |
131
+
132
+
133
+ ## Common Filter Patterns
134
+
135
+ ### Multi-Condition Search
136
+
137
+ ```typescript
138
+ {
139
+ where: {
140
+ and: [
141
+ { age: { gte: 18, lte: 65 } }, // Between 18 and 65
142
+ { status: { in: ['active', 'pending'] } },
143
+ { or: [
144
+ { email: { ilike: '%@company.com' } },
145
+ { role: 'admin' }
146
+ ]}
147
+ ]
148
+ }
149
+ }
150
+ ```
151
+
152
+ ### Text Search
153
+
154
+ ```typescript
155
+ {
156
+ where: {
157
+ or: [
158
+ { name: { ilike: '%john%' } },
159
+ { email: { ilike: '%john%' } },
160
+ { username: { ilike: '%john%' } }
161
+ ]
162
+ }
163
+ }
164
+ ```
165
+
166
+ ### Date Range
167
+
168
+ ```typescript
169
+ {
170
+ where: {
171
+ createdAt: {
172
+ gte: new Date('2024-01-01'),
173
+ lt: new Date('2024-02-01')
174
+ }
175
+ }
176
+ }
177
+ ```
178
+
179
+ ### Exclude Soft Deleted
180
+
181
+ ```typescript
182
+ {
183
+ where: {
184
+ and: [
185
+ { isDeleted: false },
186
+ { status: 'active' }
187
+ ]
188
+ }
189
+ }
190
+ ```
191
+
192
+ ### Multi-Tenant Filtering
193
+
194
+ ```typescript
195
+ {
196
+ where: {
197
+ and: [
198
+ { tenantId: currentTenantId },
199
+ { isActive: true }
200
+ ]
201
+ }
202
+ }
203
+ ```
204
+
205
+
206
+ ## Operator Precedence
207
+
208
+ When combining operators, IGNIS follows standard SQL precedence:
209
+
210
+ 1. **AND** - Higher precedence
211
+ 2. **OR** - Lower precedence
212
+
213
+ Use explicit nesting (via `and`/`or` arrays) for clarity:
214
+
215
+ ```typescript
216
+ // Clear precedence
217
+ {
218
+ where: {
219
+ and: [
220
+ { status: 'active' },
221
+ { or: [
222
+ { role: 'admin' },
223
+ { role: 'moderator' }
224
+ ]}
225
+ ]
226
+ }
227
+ }
228
+ ```
229
+
230
+
231
+ ## Performance Tips
232
+
233
+ 1. **Index frequently filtered columns:**
234
+ ```sql
235
+ CREATE INDEX idx_users_status ON users(status);
236
+ CREATE INDEX idx_posts_created_at ON posts(created_at DESC);
237
+ ```
238
+
239
+ 2. **Use `eq` instead of `like` when possible:**
240
+ ```typescript
241
+ // Fast: Uses index
242
+ { status: { eq: 'active' } }
243
+
244
+ // Slower: Full table scan
245
+ { status: { like: 'active' } }
246
+ ```
247
+
248
+ 3. **Limit array contains operations:**
249
+ ```typescript
250
+ // Better performance with smaller arrays
251
+ { tags: { contains: ['typescript'] } } // Good
252
+ { tags: { contains: ['tag1', 'tag2', /* ... 100 tags */] } } // Slow
253
+ ```
254
+
255
+ 4. **Use pagination for large result sets:**
256
+ ```typescript
257
+ {
258
+ where: { isActive: true },
259
+ limit: 100,
260
+ skip: 0,
261
+ order: ['id ASC']
262
+ }
263
+ ```
264
+
265
+
266
+ ## See Also
267
+
268
+ - **Detailed Guides:**
269
+ - [Comparison Operators](./comparison-operators.md)
270
+ - [Logical Operators](./logical-operators.md)
271
+ - [Pattern Matching](./pattern-matching.md)
272
+ - [JSON Filtering](./json-filtering.md)
273
+ - [Array Operators](./array-operators.md)
274
+
275
+ - **Related References:**
276
+ - [Repositories](../repositories/) - Using filters in repository queries
277
+ - [Models](../models.md) - Defining model schemas
278
+
279
+ - **Usage Guides:**
280
+ - [Application Usage](./application-usage.md) - Filters in the full stack
281
+ - [Use Case Gallery](./use-cases.md) - Real-world examples
282
+ - [Pro Tips & Edge Cases](./tips.md) - Advanced patterns
283
+
284
+ - **Quick Reference:**
285
+ - [Main Quick Reference](/references/quick-reference.md) - All IGNIS APIs
@@ -11,15 +11,18 @@ Query nested fields within JSON/JSONB columns using dot notation. This is a Post
11
11
 
12
12
  ## Basic JSON Path Syntax
13
13
 
14
+ JSON paths are expressed as dot-notation keys in the `where` clause. A key is recognized as a JSON path if it contains a `.` or `[`.
15
+
14
16
  ```typescript
15
17
  // Column: metadata jsonb
16
18
  // Data: { "user": { "id": 123, "role": "admin" }, "tags": ["urgent"] }
17
19
 
18
20
  // Simple nested field
19
21
  { where: { 'metadata.user.id': 123 } }
20
- // SQL: "metadata" #>> '{user,id}' = '123'
22
+ // SQL: CASE WHEN ("metadata" #>> '{user,id}') ~ '^-?[0-9]+(\.[0-9]+)?$'
23
+ // THEN ("metadata" #>> '{user,id}')::numeric ELSE NULL END = 123
21
24
 
22
- // Deep nesting
25
+ // String field (no numeric casting)
23
26
  { where: { 'metadata.user.role': 'admin' } }
24
27
  // SQL: "metadata" #>> '{user,role}' = 'admin'
25
28
 
@@ -60,18 +63,26 @@ All standard operators work with JSON paths:
60
63
  // Between
61
64
  { where: { 'metadata.score': { between: [70, 90] } } }
62
65
 
63
- // Pattern matching
66
+ // Pattern matching (text comparison, no numeric casting)
64
67
  { where: { 'metadata.level': { ilike: '%high%' } } }
65
68
  // SQL: "metadata" #>> '{level}' ILIKE '%high%'
66
69
 
67
- // IN operator
70
+ // IN operator (text comparison)
68
71
  { where: { 'metadata.status': { in: ['pending', 'review'] } } }
72
+
73
+ // Regex
74
+ { where: { 'metadata.code': { regexp: '^[A-Z]+$' } } }
75
+ // SQL: "metadata" #>> '{code}' ~ '^[A-Z]+$'
76
+
77
+ // Not equal
78
+ { where: { 'metadata.type': { ne: 'draft' } } }
79
+ // SQL: "metadata" #>> '{type}' != 'draft'
69
80
  ```
70
81
 
71
82
 
72
83
  ## Safe Numeric Casting
73
84
 
74
- JSON fields may contain mixed types. Ignis uses safe casting to prevent database errors:
85
+ When a numeric comparison operator (`gt`, `gte`, `lt`, `lte`, `between`, `notBetween`) is used with a JSON path, Ignis wraps the extraction in a safe CASE expression. This prevents database errors when JSON fields contain mixed types:
75
86
 
76
87
  ```typescript
77
88
  // Data in database:
@@ -82,6 +93,12 @@ JSON fields may contain mixed types. Ignis uses safe casting to prevent database
82
93
  // Query with numeric operator
83
94
  { where: { 'metadata.score': { gt: 50 } } }
84
95
 
96
+ // Generated SQL:
97
+ // CASE WHEN ("metadata" #>> '{score}') ~ '^-?[0-9]+(\.[0-9]+)?$'
98
+ // THEN ("metadata" #>> '{score}')::numeric
99
+ // ELSE NULL
100
+ // END > 50
101
+
85
102
  // Result:
86
103
  // Row 1: 85 > 50 -> matched
87
104
  // Row 2: "high" -> NULL -> not matched
@@ -91,19 +108,27 @@ JSON fields may contain mixed types. Ignis uses safe casting to prevent database
91
108
  | JSON Value | Numeric Operation Result |
92
109
  |------------|-------------------------|
93
110
  | `{ "score": 85 }` | Compares as `85` |
111
+ | `{ "score": "85" }` | Compares as `85` (string passes regex) |
94
112
  | `{ "score": "high" }` | Treated as `NULL` (no match) |
95
113
  | `{ "score": null }` | Treated as `NULL` (no match) |
96
114
 
115
+ Non-numeric operators (`eq`, `ne`, `like`, `ilike`, `in`, etc.) use text comparison via `#>>` without numeric casting.
97
116
 
98
- ## Boolean Values
99
117
 
100
- Booleans are compared as TEXT strings:
118
+ ## Numeric Value Equality
119
+
120
+ When a JSON path is compared to a number using direct equality (not an operator object), numeric casting is also applied:
101
121
 
102
122
  ```typescript
103
- // JSON data: { "enabled": true }
123
+ { where: { 'metadata.user.id': 123 } }
124
+ // Uses numeric CASE expression since value is typeof number
125
+ ```
126
+
127
+ When compared to a string, it uses text comparison:
104
128
 
105
- { where: { 'metadata.enabled': true } }
106
- // SQL: "metadata" #>> '{enabled}' = 'true'
129
+ ```typescript
130
+ { where: { 'metadata.user.role': 'admin' } }
131
+ // Uses "metadata" #>> '{user,role}' text comparison
107
132
  ```
108
133
 
109
134
 
@@ -112,13 +137,16 @@ Booleans are compared as TEXT strings:
112
137
  Order results by JSON fields:
113
138
 
114
139
  ```typescript
115
- { filter: { order: ['metadata.priority DESC'] } }
140
+ { order: ['metadata.priority DESC'] }
116
141
  // SQL: ORDER BY "metadata" #> '{priority}' DESC
117
142
 
118
143
  // Multiple JSON fields
119
- { filter: { order: ['metadata.priority DESC', 'metadata.score ASC'] } }
144
+ { order: ['metadata.priority DESC', 'metadata.score ASC'] }
120
145
  ```
121
146
 
147
+ > [!NOTE]
148
+ > JSON ordering uses `#>` (returns JSONB, preserves native types) unlike where clauses which use `#>>` (returns text). This means JSONB sort order applies.
149
+
122
150
  **Sort Order for JSONB Types:**
123
151
 
124
152
  | JSONB Type | Sort Order |
@@ -133,7 +161,7 @@ Order results by JSON fields:
133
161
 
134
162
  ## Path Validation & Security
135
163
 
136
- Path components are validated to prevent SQL injection:
164
+ Path components are validated against the pattern `/^[a-zA-Z_][a-zA-Z0-9_-]*$|^\d+$/` to prevent SQL injection:
137
165
 
138
166
  ```typescript
139
167
  // Valid paths
@@ -145,7 +173,7 @@ Path components are validated to prevent SQL injection:
145
173
 
146
174
  // Invalid (throws error)
147
175
  'metadata.field;DROP TABLE'
148
- 'data.123invalid'
176
+ 'data.123invalid' // starts with digit (not array index context)
149
177
  'config.(SELECT * FROM users)'
150
178
  ```
151
179
 
@@ -158,6 +186,8 @@ Error: Column 'name' is not JSON/JSONB type | dataType: 'text'
158
186
  Error: Invalid JSON path component: 'field;DROP'
159
187
  ```
160
188
 
189
+ The column referenced by the first path segment (before the first `.` or `[`) must be a `json` or `jsonb` column type. Using a JSON path on a non-JSON column throws an error.
190
+
161
191
 
162
192
  ## Performance Tips
163
193
 
@@ -169,16 +199,16 @@ CREATE INDEX idx_metadata_gin ON "Product" USING GIN ("metadata");
169
199
 
170
200
  2. **Use Appropriate Types in JSON:**
171
201
  ```json
172
- // Good
202
+ // Good - numeric operators will work correctly
173
203
  { "priority": 3, "enabled": true }
174
204
 
175
- // Bad
205
+ // Bad - numeric operators will need string-to-number casting
176
206
  { "priority": "3", "enabled": "true" }
177
207
  ```
178
208
 
179
209
  3. **Keep Paths Shallow:**
180
210
  ```typescript
181
- // Easier to work with
211
+ // Easier to work with and index
182
212
  'metadata.priority'
183
213
 
184
214
  // Harder to optimize
@@ -193,7 +223,10 @@ CREATE INDEX idx_metadata_gin ON "Product" USING GIN ("metadata");
193
223
  // This is safe - no errors, just no matches
194
224
  { where: { 'metadata.nonexistent.field': 'value' } }
195
225
  // SQL: "metadata" #>> '{nonexistent,field}' = 'value'
226
+ // Result: No rows (NULL != 'value')
227
+ ```
228
+
229
+
196
230
  ## See Also
197
231
 
198
232
  - [Nested JSON Updates](../repositories/advanced.md#nested-json-updates) - Updating JSON fields
199
-
@@ -11,7 +11,7 @@ Operators for matching values against arrays.
11
11
 
12
12
  ## in / inq - In Array
13
13
 
14
- Matches records where field value is in the provided array.
14
+ Matches records where field value is in the provided array. `in` and `inq` are aliases and behave identically.
15
15
 
16
16
  ```typescript
17
17
  { where: { status: { in: ['active', 'pending', 'review'] } } }
@@ -37,9 +37,10 @@ Matches records where field value is in the provided array.
37
37
 
38
38
  | Scenario | Behavior |
39
39
  |----------|----------|
40
- | `{ in: [] }` (empty array) | Returns no rows (`false`) |
41
- | `{ nin: [] }` (empty array) | Returns all rows (`true`) |
40
+ | `{ in: [] }` (empty array) | Returns no rows (`WHERE false`) |
41
+ | `{ nin: [] }` (empty array) | Returns all rows (`WHERE true`) |
42
42
  | `{ in: 'value' }` (non-array) | Treated as `{ eq: 'value' }` |
43
+ | `{ nin: 'value' }` (non-array) | Treated as `{ ne: 'value' }` |
43
44
 
44
45
  > [!WARNING]
45
46
  > `NOT IN` excludes rows where the column is `NULL`. If your column can be `NULL`, use `OR` to include them:
@@ -107,11 +107,12 @@ Combine AND and OR for complex logic:
107
107
 
108
108
  ## NOT Logic
109
109
 
110
- Use negation operators for NOT conditions:
110
+ Ignis does not have a standalone `not` logical operator. Instead, use negation operators for NOT conditions:
111
111
 
112
112
  ```typescript
113
113
  // NOT equal
114
114
  { where: { status: { ne: 'deleted' } } }
115
+ { where: { status: { neq: 'deleted' } } }
115
116
 
116
117
  // NOT IN
117
118
  { where: { status: { nin: ['deleted', 'banned'] } } }
@@ -119,8 +120,12 @@ Use negation operators for NOT conditions:
119
120
  // NOT LIKE
120
121
  { where: { email: { nlike: '%@test.com' } } }
121
122
 
122
- // NOT NULL
123
+ // NOT ILIKE
124
+ { where: { email: { nilike: '%@test.com' } } }
125
+
126
+ // IS NOT NULL
123
127
  { where: { verifiedAt: { isn: null } } }
128
+ { where: { verifiedAt: { ne: null } } }
124
129
 
125
130
  // NOT BETWEEN
126
131
  { where: { score: { notBetween: [40, 60] } } }
@@ -9,6 +9,42 @@ difficulty: intermediate
9
9
  Operators for checking null and non-null values.
10
10
 
11
11
 
12
+ ## Direct Null Assignment
13
+
14
+ The simplest way to check for NULL:
15
+
16
+ ```typescript
17
+ // IS NULL (implicit)
18
+ { where: { deletedAt: null } }
19
+ // SQL: WHERE "deleted_at" IS NULL
20
+ ```
21
+
22
+
23
+ ## eq with Null
24
+
25
+ ```typescript
26
+ { where: { deletedAt: { eq: null } } }
27
+ // SQL: WHERE "deleted_at" IS NULL
28
+
29
+ { where: { status: { eq: 'active' } } }
30
+ // SQL: WHERE "status" = 'active'
31
+ ```
32
+
33
+
34
+ ## ne / neq with Null
35
+
36
+ ```typescript
37
+ // IS NOT NULL
38
+ { where: { deletedAt: { ne: null } } }
39
+ { where: { deletedAt: { neq: null } } }
40
+ // SQL: WHERE "deleted_at" IS NOT NULL
41
+
42
+ // Not equal to value
43
+ { where: { status: { ne: 'deleted' } } }
44
+ // SQL: WHERE "status" != 'deleted'
45
+ ```
46
+
47
+
12
48
  ## is - IS NULL / Equality
13
49
 
14
50
  ```typescript
@@ -35,6 +71,20 @@ Operators for checking null and non-null values.
35
71
  ```
36
72
 
37
73
 
74
+ ## Null Check Summary
75
+
76
+ | Syntax | SQL | Description |
77
+ |--------|-----|-------------|
78
+ | `{ field: null }` | `IS NULL` | Direct null check |
79
+ | `{ field: { eq: null } }` | `IS NULL` | Explicit null equality |
80
+ | `{ field: { is: null } }` | `IS NULL` | IS operator with null |
81
+ | `{ field: { ne: null } }` | `IS NOT NULL` | Not-equal null check |
82
+ | `{ field: { neq: null } }` | `IS NOT NULL` | Alias for ne with null |
83
+ | `{ field: { isn: null } }` | `IS NOT NULL` | IS NOT operator with null |
84
+
85
+ All six syntaxes for IS NULL / IS NOT NULL are equivalent -- use whichever reads best in context.
86
+
87
+
38
88
  ## Common Patterns
39
89
 
40
90
  ### Soft Delete Pattern