@venizia/ignis-docs 0.2.0 → 0.2.1-1

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 (174) 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 +24 -13
  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 +27 -3
  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 +8 -4
  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 +247 -153
  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 +109 -323
  26. package/content/extensions/components/authentication/api.md +489 -602
  27. package/content/extensions/components/authentication/errors.md +135 -498
  28. package/content/extensions/components/authentication/index.md +89 -801
  29. package/content/extensions/components/authentication/usage.md +231 -955
  30. package/content/extensions/components/authorization/api.md +991 -644
  31. package/content/extensions/components/authorization/errors.md +204 -208
  32. package/content/extensions/components/authorization/getting-started.md +227 -0
  33. package/content/extensions/components/authorization/index.md +88 -795
  34. package/content/extensions/components/authorization/usage.md +219 -528
  35. package/content/extensions/components/health-check.md +77 -243
  36. package/content/extensions/components/index.md +24 -90
  37. package/content/extensions/components/mail/api.md +553 -285
  38. package/content/extensions/components/mail/errors.md +76 -62
  39. package/content/extensions/components/mail/index.md +111 -463
  40. package/content/extensions/components/mail/usage.md +139 -173
  41. package/content/extensions/components/request-tracker.md +70 -173
  42. package/content/extensions/components/socket-io/api.md +462 -785
  43. package/content/extensions/components/socket-io/errors.md +49 -51
  44. package/content/extensions/components/socket-io/index.md +69 -372
  45. package/content/extensions/components/socket-io/usage.md +212 -105
  46. package/content/extensions/components/static-asset/api.md +461 -141
  47. package/content/extensions/components/static-asset/errors.md +121 -53
  48. package/content/extensions/components/static-asset/index.md +84 -606
  49. package/content/extensions/components/static-asset/usage.md +184 -299
  50. package/content/extensions/components/template/index.md +3 -3
  51. package/content/extensions/components/websocket/api.md +311 -404
  52. package/content/extensions/components/websocket/errors.md +47 -56
  53. package/content/extensions/components/websocket/index.md +75 -407
  54. package/content/extensions/components/websocket/usage.md +120 -338
  55. package/content/extensions/helpers/cron/index.md +52 -160
  56. package/content/extensions/helpers/crypto/index.md +67 -480
  57. package/content/extensions/helpers/crypto/reference.md +528 -0
  58. package/content/extensions/helpers/env/index.md +64 -178
  59. package/content/extensions/helpers/error/index.md +286 -196
  60. package/content/extensions/helpers/index.md +61 -47
  61. package/content/extensions/helpers/inversion/index.md +76 -550
  62. package/content/extensions/helpers/inversion/reference.md +530 -0
  63. package/content/extensions/helpers/kafka/admin.md +23 -3
  64. package/content/extensions/helpers/kafka/compile-binary.md +83 -52
  65. package/content/extensions/helpers/kafka/consumer.md +65 -28
  66. package/content/extensions/helpers/kafka/examples.md +46 -253
  67. package/content/extensions/helpers/kafka/index.md +55 -617
  68. package/content/extensions/helpers/kafka/producer.md +156 -22
  69. package/content/extensions/helpers/kafka/schema-registry.md +59 -79
  70. package/content/extensions/helpers/logger/hf-logger.md +220 -0
  71. package/content/extensions/helpers/logger/index.md +78 -552
  72. package/content/extensions/helpers/logger/pino.md +105 -0
  73. package/content/extensions/helpers/logger/reference.md +937 -0
  74. package/content/extensions/helpers/network/api.md +276 -195
  75. package/content/extensions/helpers/network/index.md +87 -524
  76. package/content/extensions/helpers/queue/index.md +80 -897
  77. package/content/extensions/helpers/queue/reference.md +494 -0
  78. package/content/extensions/helpers/redis/index.md +86 -640
  79. package/content/extensions/helpers/redis/reference.md +784 -0
  80. package/content/extensions/helpers/secrets/index.md +136 -0
  81. package/content/extensions/helpers/socket-io/api.md +325 -204
  82. package/content/extensions/helpers/socket-io/index.md +79 -429
  83. package/content/extensions/helpers/storage/api.md +584 -464
  84. package/content/extensions/helpers/storage/index.md +80 -573
  85. package/content/extensions/helpers/types/index.md +75 -495
  86. package/content/extensions/helpers/types/reference.md +689 -0
  87. package/content/extensions/helpers/uid/index.md +176 -189
  88. package/content/extensions/helpers/websocket/api.md +366 -214
  89. package/content/extensions/helpers/websocket/index.md +68 -503
  90. package/content/extensions/helpers/worker-thread/index.md +62 -396
  91. package/content/extensions/helpers/worker-thread/reference.md +428 -0
  92. package/content/extensions/index.md +38 -39
  93. package/content/extensions/src-details/mcp-server.md +96 -548
  94. package/content/guides/core-concepts/persistent/datasources.md +2 -2
  95. package/content/guides/core-concepts/persistent/index.md +5 -1
  96. package/content/guides/core-concepts/persistent/models.md +1 -1
  97. package/content/guides/core-concepts/persistent/pglite.md +218 -0
  98. package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
  99. package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
  100. package/content/guides/core-concepts/persistent/search-typesense.md +28 -16
  101. package/content/guides/core-concepts/persistent/sqlite.md +296 -0
  102. package/content/guides/core-concepts/persistent/transactions.md +12 -11
  103. package/content/guides/core-concepts/secrets-vault.md +177 -0
  104. package/content/guides/core-concepts/services.md +1 -1
  105. package/content/guides/get-started/5-minute-quickstart.md +59 -206
  106. package/content/guides/get-started/philosophy.md +135 -670
  107. package/content/guides/get-started/setup.md +53 -74
  108. package/content/guides/migrations/redis-helpers-migration.md +5 -4
  109. package/content/guides/migrations/scoped-rbac-migration.md +5 -5
  110. package/content/guides/migrations/unified-connectors-migration.md +8 -7
  111. package/content/guides/tutorials/ecommerce-api.md +3 -8
  112. package/content/guides/tutorials/realtime-chat.md +1 -1
  113. package/content/references/base/application.md +64 -22
  114. package/content/references/base/components.md +3 -3
  115. package/content/references/base/connectors.md +79 -136
  116. package/content/references/base/controllers.md +12 -12
  117. package/content/references/base/datasources-reference.md +600 -0
  118. package/content/references/base/datasources.md +84 -444
  119. package/content/references/base/dependency-injection.md +25 -46
  120. package/content/references/base/filter-system/application-usage.md +95 -123
  121. package/content/references/base/filter-system/array-operators.md +33 -43
  122. package/content/references/base/filter-system/comparison-operators.md +55 -63
  123. package/content/references/base/filter-system/default-filter.md +144 -350
  124. package/content/references/base/filter-system/fields-order-pagination.md +116 -148
  125. package/content/references/base/filter-system/index.md +114 -253
  126. package/content/references/base/filter-system/json-filtering.md +52 -181
  127. package/content/references/base/filter-system/list-operators.md +28 -44
  128. package/content/references/base/filter-system/logical-operators.md +69 -114
  129. package/content/references/base/filter-system/null-operators.md +41 -98
  130. package/content/references/base/filter-system/pattern-matching.md +48 -51
  131. package/content/references/base/filter-system/quick-reference.md +93 -196
  132. package/content/references/base/filter-system/range-operators.md +22 -38
  133. package/content/references/base/filter-system/tips.md +70 -133
  134. package/content/references/base/filter-system/use-cases.md +173 -232
  135. package/content/references/base/grpc-controllers.md +53 -17
  136. package/content/references/base/index.md +5 -3
  137. package/content/references/base/middlewares.md +43 -28
  138. package/content/references/base/models-reference.md +886 -0
  139. package/content/references/base/models.md +81 -1452
  140. package/content/references/base/providers.md +8 -8
  141. package/content/references/base/repositories/advanced.md +281 -419
  142. package/content/references/base/repositories/index.md +84 -644
  143. package/content/references/base/repositories/mixins.md +25 -21
  144. package/content/references/base/repositories/relations.md +194 -375
  145. package/content/references/base/repositories/soft-deletable.md +67 -55
  146. package/content/references/base/secrets.md +267 -0
  147. package/content/references/base/services.md +8 -6
  148. package/content/references/configuration/environment-variables.md +73 -21
  149. package/content/references/configuration/index.md +51 -31
  150. package/content/references/index.md +1 -1
  151. package/content/references/quick-reference.md +3 -16
  152. package/content/references/utilities/crypto.md +35 -76
  153. package/content/references/utilities/date.md +33 -73
  154. package/content/references/utilities/duration.md +85 -0
  155. package/content/references/utilities/index.md +6 -2
  156. package/content/references/utilities/jsx-reference.md +298 -0
  157. package/content/references/utilities/jsx.md +82 -525
  158. package/content/references/utilities/module.md +81 -61
  159. package/content/references/utilities/parse.md +34 -64
  160. package/content/references/utilities/performance.md +33 -58
  161. package/content/references/utilities/promise.md +28 -62
  162. package/content/references/utilities/request.md +58 -218
  163. package/content/references/utilities/retry.md +139 -0
  164. package/content/references/utilities/schema.md +43 -137
  165. package/content/references/utilities/statuses-reference.md +361 -0
  166. package/content/references/utilities/statuses.md +63 -667
  167. package/dist/mcp-server/index.js +0 -0
  168. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  169. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
  170. package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
  171. package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
  172. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
  173. package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
  174. package/package.json +24 -23
@@ -6,39 +6,9 @@ difficulty: intermediate
6
6
 
7
7
  # JSON/JSONB Filtering
8
8
 
9
- Query nested fields within JSON/JSONB columns using dot notation. This is a PostgreSQL-specific feature.
9
+ Query nested fields within JSON/JSONB columns using dot notation. PostgreSQL-specific.
10
10
 
11
-
12
- ## Basic JSON Path Syntax
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
-
16
- ```typescript
17
- // Column: metadata jsonb
18
- // Data: { "user": { "id": 123, "role": "admin" }, "tags": ["urgent"] }
19
-
20
- // Simple nested field
21
- { where: { '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
24
-
25
- // String field (no numeric casting)
26
- { where: { 'metadata.user.role': 'admin' } }
27
- // SQL: "metadata" #>> '{user,role}' = 'admin'
28
-
29
- // Array index access
30
- { where: { 'metadata.tags[0]': 'urgent' } }
31
- // SQL: "metadata" #>> '{tags,0}' = 'urgent'
32
-
33
- // Kebab-case keys
34
- { where: { 'metadata.user-id': 'abc123' } }
35
- // SQL: "metadata" #>> '{user-id}' = 'abc123'
36
- ```
37
-
38
-
39
- ## Supported Path Formats
40
-
41
- | Format | Example | SQL Path |
11
+ | Format | Example | SQL path |
42
12
  |--------|---------|----------|
43
13
  | Simple field | `metadata.name` | `{name}` |
44
14
  | Nested field | `metadata.user.email` | `{user,email}` |
@@ -46,206 +16,107 @@ JSON paths are expressed as dot-notation keys in the `where` clause. A key is re
46
16
  | Nested with array | `metadata.items[2].name` | `{items,2,name}` |
47
17
  | Kebab-case | `metadata.user-id` | `{user-id}` |
48
18
 
19
+ > [!NOTE]
20
+ > A key is recognized as a JSON path if it contains a `.` or `[`. The column named by the first path segment must be a `json`/`jsonb` column, or the query throws.
49
21
 
50
- ## JSON with Operators
51
22
 
52
- All standard operators work with JSON paths:
23
+ ## Basic Usage
53
24
 
54
25
  ```typescript
55
- // Numeric comparison (automatic safe casting)
56
- { where: { 'metadata.score': { gt: 80 } } }
57
- // SQL: CASE WHEN ("metadata" #>> '{score}') ~ '^-?[0-9]+(\.[0-9]+)?$'
58
- // THEN ("metadata" #>> '{score}')::numeric ELSE NULL END > 80
26
+ // Column: metadata jsonb
27
+ // Data: { "user": { "id": 123, "role": "admin" } }
59
28
 
60
- // Range comparison
61
- { where: { 'metadata.priority': { gte: 1, lte: 5 } } }
29
+ { where: { 'metadata.user.role': 'admin' } }
30
+ // SQL: "metadata" #>> '{user,role}' = 'admin'
31
+ ```
62
32
 
63
- // Between
64
- { where: { 'metadata.score': { between: [70, 90] } } }
33
+ All standard operators work with a JSON path key:
65
34
 
66
- // Pattern matching (text comparison, no numeric casting)
35
+ ```typescript
36
+ { where: { 'metadata.score': { gt: 80 } } }
67
37
  { where: { 'metadata.level': { ilike: '%high%' } } }
68
- // SQL: "metadata" #>> '{level}' ILIKE '%high%'
69
-
70
- // IN operator (text comparison)
71
38
  { where: { 'metadata.status': { in: ['pending', 'review'] } } }
72
-
73
- // Regex
74
39
  { 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'
80
40
  ```
81
41
 
42
+ > [!NOTE]
43
+ > If the path does not exist in a row's JSON, `#>>` returns `NULL` - the row is safely excluded, never an error.
82
44
 
83
- ## Safe Numeric Casting
84
45
 
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:
46
+ ## Numeric Casting
86
47
 
87
- ```typescript
88
- // Data in database:
89
- // Row 1: { "score": 85 } <- number
90
- // Row 2: { "score": "high" } <- string
91
- // Row 3: { "score": null } <- null
48
+ A JSON `#>>` extraction is text, so a numeric comparison needs a cast or Postgres raises `operator does not exist`. IGNIS wraps the extraction in a safe `CASE` expression, decided per operator:
92
49
 
93
- // Query with numeric operator
50
+ ```typescript
94
51
  { where: { 'metadata.score': { gt: 50 } } }
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
-
102
- // Result:
103
- // Row 1: 85 > 50 -> matched
104
- // Row 2: "high" -> NULL -> not matched
105
- // Row 3: null -> NULL -> not matched
52
+ // SQL: CASE WHEN ("metadata" #>> '{score}') ~ '^-?[0-9]+(\.[0-9]+)?$'
53
+ // THEN ("metadata" #>> '{score}')::numeric ELSE NULL END > 50
106
54
  ```
107
55
 
108
- | JSON Value | Numeric Operation Result |
109
- |------------|-------------------------|
110
- | `{ "score": 85 }` | Compares as `85` |
111
- | `{ "score": "85" }` | Compares as `85` (string passes regex) |
112
- | `{ "score": "high" }` | Treated as `NULL` (no match) |
113
- | `{ "score": null }` | Treated as `NULL` (no match) |
56
+ | Operators | Casts to numeric when... |
57
+ |-----------|---------------------------|
58
+ | `gt`, `gte`, `lt`, `lte` | the operand is a `number` |
59
+ | `between`, `notBetween` | both bounds are numbers |
60
+ | `eq`, `ne`, `neq` | the operand is a `number` |
61
+ | `in`, `inq`, `nin` | every array element is a `number` |
62
+ | `like`, `ilike`, `nlike`, `nilike`, `regexp`, `iregexp` | never - always text |
63
+ | direct value (no operator object) | the value is `typeof number` |
114
64
 
115
- Non-numeric operators (`eq`, `ne`, `like`, `ilike`, `in`, etc.) use text comparison via `#>>` without numeric casting.
65
+ > [!NOTE]
66
+ > A numeric-looking string still passes the cast (`"85"` -> `85`); a non-numeric string or `null` falls through to `NULL` and never matches.
116
67
 
117
- The cast is decided **per operator**, not once for the whole operator object -- a mixed object casts only the operators that need it:
68
+ The cast applies per operator, not once for the whole object - a mixed object casts only the operators that need it:
118
69
 
119
70
  ```typescript
120
- // Mixed operator object: gte casts, like does not
121
71
  { where: { 'metadata.score': { gte: 1, like: '%a%' } } }
122
- // SQL: CASE WHEN ("metadata" #>> '{score}') ~ '^-?[0-9]+(\.[0-9]+)?$'
123
- // THEN ("metadata" #>> '{score}')::numeric ELSE NULL END >= 1
124
- // AND "metadata" #>> '{score}' LIKE '%a%'
72
+ // gte casts to numeric; like stays text - both read the same #>> extraction
125
73
  ```
126
74
 
127
75
  `not` recurses into whatever it wraps, so a numeric operator nested under `not` still gets cast:
128
76
 
129
77
  ```typescript
130
- // not wrapping an operator - the nested gt still gets the numeric cast
131
78
  { where: { 'metadata.score': { not: { gt: 50 } } } }
132
79
  // SQL: NOT (CASE WHEN ("metadata" #>> '{score}') ~ '^-?[0-9]+(\.[0-9]+)?$'
133
80
  // THEN ("metadata" #>> '{score}')::numeric ELSE NULL END > 50)
134
81
  ```
135
82
 
136
83
 
137
- ## Numeric Value Equality
138
-
139
- When a JSON path is compared to a number using direct equality (not an operator object), numeric casting is also applied:
140
-
141
- ```typescript
142
- { where: { 'metadata.user.id': 123 } }
143
- // Uses numeric CASE expression since value is typeof number
144
- ```
145
-
146
- When compared to a string, it uses text comparison:
147
-
148
- ```typescript
149
- { where: { 'metadata.user.role': 'admin' } }
150
- // Uses "metadata" #>> '{user,role}' text comparison
151
- ```
152
-
153
-
154
- ## JSON Path Ordering
155
-
156
- Order results by JSON fields:
84
+ ## Ordering
157
85
 
158
86
  ```typescript
159
87
  { order: ['metadata.priority DESC'] }
160
88
  // SQL: ORDER BY "metadata" #> '{priority}' DESC
161
-
162
- // Multiple JSON fields
163
- { order: ['metadata.priority DESC', 'metadata.score ASC'] }
164
89
  ```
165
90
 
166
91
  > [!NOTE]
167
- > JSON ordering uses `#>` (returns JSONB, preserves native types) unlike where clauses which use `#>>` (returns text). This means JSONB sort order applies.
168
-
169
- **Sort Order for JSONB Types:**
170
-
171
- | JSONB Type | Sort Order |
172
- |------------|------------|
173
- | `null` | First (lowest) |
174
- | `boolean` | `false` < `true` |
175
- | `number` | Numeric order |
176
- | `string` | Lexicographic |
177
- | `array` | Element-wise |
178
- | `object` | Key-value |
179
-
180
-
181
- ## Path Validation & Security
92
+ > Ordering uses `#>` (returns JSONB, preserves native type ordering); `where` uses `#>>` (returns text) instead.
182
93
 
183
- Path components are validated against the pattern `/^[a-zA-Z_][a-zA-Z0-9_-]*$|^\d+$/` to prevent SQL injection:
184
94
 
185
- ```typescript
186
- // Valid paths
187
- 'metadata.fieldName'
188
- 'metadata.nested.deep.value'
189
- 'data.items[0]'
190
- 'config.user_id'
191
- 'data.meta-data' // kebab-case allowed
192
-
193
- // Invalid (throws error)
194
- 'metadata.field;DROP TABLE'
195
- 'data.123invalid' // starts with digit (not array index context)
196
- 'config.(SELECT * FROM users)'
197
- ```
95
+ ## Path Validation
198
96
 
199
- **Error Messages:**
200
- ```
201
- // Non-JSON column
202
- Error: Column 'name' is not JSON/JSONB type | dataType: 'text'
203
-
204
- // Invalid path
205
- Error: Invalid JSON path component: 'field;DROP'
206
- ```
97
+ Every path component must match `/^[a-zA-Z_][a-zA-Z0-9_-]*$|^\d+$/` - a letter/underscore start followed by letters, digits, underscore or hyphen, or a bare digit run for an array index.
207
98
 
208
- 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.
209
-
210
-
211
- ## Performance Tips
212
-
213
- 1. **Index Your JSON Paths:**
214
- ```sql
215
- CREATE INDEX idx_metadata_priority ON "Product" (("metadata" ->> 'priority'));
216
- CREATE INDEX idx_metadata_gin ON "Product" USING GIN ("metadata");
217
- ```
218
-
219
- 2. **Use Appropriate Types in JSON:**
220
- ```json
221
- // Good - numeric operators will work correctly
222
- { "priority": 3, "enabled": true }
223
-
224
- // Bad - numeric operators will need string-to-number casting
225
- { "priority": "3", "enabled": "true" }
226
- ```
227
-
228
- 3. **Keep Paths Shallow:**
229
99
  ```typescript
230
- // Easier to work with and index
231
- 'metadata.priority'
232
-
233
- // Harder to optimize
234
- 'data.level1.level2.level3.level4.value'
100
+ 'metadata.fieldName' // valid
101
+ 'data.meta-data' // valid - kebab-case allowed
102
+ 'data.123invalid' // invalid - starts with a digit outside array-index context
103
+ 'metadata.field;DROP TABLE' // invalid - throws
235
104
  ```
236
105
 
106
+ > [!NOTE]
107
+ > A path on a non-JSON column also throws: `Column 'name' is not a JSON column`.
237
108
 
238
- ## Null-Safe JSON Paths
239
109
 
240
- ```typescript
241
- // If JSON field doesn't exist, #>> returns NULL
242
- // This is safe - no errors, just no matches
243
- { where: { 'metadata.nonexistent.field': 'value' } }
244
- // SQL: "metadata" #>> '{nonexistent,field}' = 'value'
245
- // Result: No rows (NULL != 'value')
246
- ```
110
+ ## See also
247
111
 
112
+ - [Filter System Overview](./) - the `filter` shape and the full `where` operator table
113
+ - [Fields, Order & Pagination](./fields-order-pagination) - JSON path ordering (`#>`, sorted by native JSONB type)
114
+ - [Pattern Matching](./pattern-matching) - `like`/`ilike`/`regexp` also work on a JSON path, with no numeric casting
115
+ - [Nested JSON Updates](../repositories/advanced.md#nested-json-updates) - writing to JSON paths
116
+ - [Quick Reference](./quick-reference) - every operator, one line each
248
117
 
249
- ## See Also
118
+ **Files:**
250
119
 
251
- - [Nested JSON Updates](../repositories/advanced.md#nested-json-updates) - Updating JSON fields
120
+ - [`packages/core-server/src/connectors/relational/repositories/dialect/filter.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/relational/repositories/dialect/filter.ts) - `FilterBuilder`, `buildJsonWhereCondition`/`buildJsonOperatorConditions`/`buildJsonOrderBy`
121
+ - [`packages/core-server/src/connectors/relational/repositories/dialect/internal/json-utils.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/relational/repositories/dialect/internal/json-utils.ts) - `isJsonPath`, `parseJsonPath`, path validation regex
122
+ - [`packages/filter/src/common/operators.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/filter/src/common/operators.ts) - `QueryOperators` constants
@@ -6,67 +6,51 @@ difficulty: intermediate
6
6
 
7
7
  # List Operators
8
8
 
9
- Operators for matching values against arrays.
9
+ Matches a field against a set of candidate values.
10
10
 
11
+ | Operator | SQL | Meaning |
12
+ |----------|-----|---------|
13
+ | `in` | `IN` | Value is one of the array |
14
+ | `inq` | `IN` | Alias for `in` |
15
+ | `nin` | `NOT IN` | Value is none of the array |
11
16
 
12
- ## in / inq - In Array
13
-
14
- Matches records where field value is in the provided array. `in` and `inq` are aliases and behave identically.
17
+ ## in / inq
15
18
 
16
19
  ```typescript
17
20
  { where: { status: { in: ['active', 'pending', 'review'] } } }
18
- { where: { status: { inq: ['active', 'pending', 'review'] } } } // Alias
19
-
20
21
  // SQL: WHERE "status" IN ('active', 'pending', 'review')
21
-
22
- // Numeric IDs
23
- { where: { categoryId: { in: [1, 2, 3, 4, 5] } } }
24
- // SQL: WHERE "category_id" IN (1, 2, 3, 4, 5)
25
22
  ```
26
23
 
24
+ **Notice:** `in` and `inq` are the same operator under two names.
25
+
26
+ **Edge cases:**
27
+ - `{ in: [] }` (empty array) matches no rows (`WHERE false`).
28
+ - `{ in: 'value' }` (non-array operand) falls back to `=`.
29
+ - `{ in: null }` falls back to `= NULL` (not `IS NULL`) and matches no rows; use `is`/`eq` for null checks.
27
30
 
28
- ## nin - Not In Array
31
+ ## nin
29
32
 
30
33
  ```typescript
31
34
  { where: { status: { nin: ['deleted', 'archived', 'banned'] } } }
32
35
  // SQL: WHERE "status" NOT IN ('deleted', 'archived', 'banned')
33
36
  ```
34
37
 
38
+ **Notice:** `NOT IN` excludes rows where the column is `NULL`.
35
39
 
36
- ## Edge Cases
40
+ **Edge cases:**
41
+ - Include NULL rows with an explicit `or` branch: `{ or: [{ status: { nin: [...] } }, { status: { is: null } }] }`.
42
+ - `{ nin: [] }` (empty array) matches all rows (`WHERE true`).
43
+ - `{ nin: 'value' }` (non-array operand) falls back to `!=`.
44
+ - `{ nin: null }` falls back to `!= NULL` (not `IS NOT NULL`) and matches no rows.
37
45
 
38
- | Scenario | Behavior |
39
- |----------|----------|
40
- | `{ in: [] }` (empty array) | Returns no rows (`WHERE false`) |
41
- | `{ nin: [] }` (empty array) | Returns all rows (`WHERE true`) |
42
- | `{ in: 'value' }` (non-array) | Treated as `{ eq: 'value' }` |
43
- | `{ nin: 'value' }` (non-array) | Treated as `{ ne: 'value' }` |
46
+ ## See also
44
47
 
45
- > [!WARNING]
46
- > `NOT IN` excludes rows where the column is `NULL`. If your column can be `NULL`, use `OR` to include them:
47
- > ```typescript
48
- > where: {
49
- > or: [
50
- > { status: { nin: ['deleted'] } },
51
- > { status: { is: null } }
52
- > ]
53
- > }
54
- > ```
48
+ - [Filter System Overview](./) - the `filter` shape and the full `where` operator table
49
+ - [Array Operators](./array-operators) - `contains`/`containedBy`/`overlaps` match against array COLUMNS, not to be confused with `in`/`nin`
50
+ - [Quick Reference](./quick-reference) - every operator, one line each
55
51
 
52
+ **Files:**
56
53
 
57
- ## Performance Tip
58
-
59
- ```typescript
60
- // For very large arrays (1000+ items), consider chunking
61
- const allIds = getLargeIdList(); // 5000 IDs
62
-
63
- const chunkSize = 500;
64
- const results = [];
65
- for (let i = 0; i < allIds.length; i += chunkSize) {
66
- const chunk = allIds.slice(i, i + chunkSize);
67
- const chunkResults = await repository.find({
68
- filter: { where: { id: { in: chunk } } }
69
- });
70
- results.push(...chunkResults);
71
- }
72
- ```
54
+ - [`packages/core-server/src/connectors/postgres/repositories/dialect/query.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/postgres/repositories/dialect/query.ts) - `PostgresQueryOperators.FNS`, per-operator SQL builders
55
+ - [`packages/core-server/src/connectors/relational/repositories/dialect/filter.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/relational/repositories/dialect/filter.ts) - `FilterBuilder`, translates `TFilter` to Drizzle/SQL
56
+ - [`packages/filter/src/common/operators.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/filter/src/common/operators.ts) - `QueryOperators` constants
@@ -8,26 +8,27 @@ difficulty: intermediate
8
8
 
9
9
  Combine multiple conditions with AND and OR logic.
10
10
 
11
+ | Form | Where shape | SQL |
12
+ |------|-------------|-----|
13
+ | Implicit AND | multiple keys in one object | `AND` between each key |
14
+ | Explicit AND | `{ and: [...] }` | `AND` between grouped clauses |
15
+ | OR | `{ or: [...] }` | `OR` between grouped clauses |
16
+ | NOT | `{ field: { not: ... } }` | `NOT (...)` around the negated condition |
17
+
11
18
 
12
19
  ## Implicit AND
13
20
 
14
- Multiple conditions in the same object are combined with AND:
21
+ Multiple conditions in the same object combine with AND.
15
22
 
16
23
  ```typescript
17
- {
18
- where: {
19
- status: 'active',
20
- role: 'admin',
21
- verified: true,
22
- }
23
- }
24
+ { where: { status: 'active', role: 'admin', verified: true } }
24
25
  // SQL: WHERE "status" = 'active' AND "role" = 'admin' AND "verified" = true
25
26
  ```
26
27
 
27
28
 
28
29
  ## Explicit AND
29
30
 
30
- Use `and` array for explicit AND conditions:
31
+ Use an `and` array to group conditions explicitly.
31
32
 
32
33
  ```typescript
33
34
  {
@@ -35,19 +36,16 @@ Use `and` array for explicit AND conditions:
35
36
  and: [
36
37
  { status: 'active' },
37
38
  { role: { in: ['admin', 'moderator'] } },
38
- { createdAt: { gte: new Date('2024-01-01') } },
39
39
  ]
40
40
  }
41
41
  }
42
- // SQL: WHERE ("status" = 'active')
43
- // AND ("role" IN ('admin', 'moderator'))
44
- // AND ("created_at" >= '2024-01-01')
42
+ // SQL: WHERE ("status" = 'active') AND ("role" IN ('admin', 'moderator'))
45
43
  ```
46
44
 
47
45
 
48
- ## OR Operator
46
+ ## OR
49
47
 
50
- Use `or` array for OR conditions:
48
+ Use an `or` array to match any of several conditions.
51
49
 
52
50
  ```typescript
53
51
  {
@@ -55,139 +53,96 @@ Use `or` array for OR conditions:
55
53
  or: [
56
54
  { status: 'active' },
57
55
  { isPublished: true },
58
- { featured: true },
59
56
  ]
60
57
  }
61
58
  }
62
- // SQL: WHERE ("status" = 'active')
63
- // OR ("is_published" = true)
64
- // OR ("featured" = true)
59
+ // SQL: WHERE ("status" = 'active') OR ("is_published" = true)
65
60
  ```
66
61
 
67
62
 
63
+ ## NOT
64
+
65
+ `not` negates whatever it wraps: a bare value negates `eq`, a nested operator object negates that operator.
66
+
67
+ ```typescript
68
+ { where: { status: { not: 'archived' } } }
69
+ // SQL: WHERE NOT ("status" = 'archived')
70
+
71
+ { where: { views: { not: { gt: 100 } } } }
72
+ // SQL: WHERE NOT ("views" > 100)
73
+ ```
74
+
75
+ > [!NOTE]
76
+ > `not` is supported on the PostgreSQL connector. The dedicated negation operators below are often clearer for a single condition.
77
+
78
+
79
+ ## Dedicated Negation Operators
80
+
81
+ | Operator | Example | SQL |
82
+ |----------|---------|-----|
83
+ | `ne` / `neq` | `{ status: { ne: 'deleted' } }` | `!=` |
84
+ | `nin` | `{ status: { nin: ['deleted', 'banned'] } }` | `NOT IN` |
85
+ | `nlike` | `{ email: { nlike: '%@test.com' } }` | `NOT LIKE` |
86
+ | `nilike` | `{ email: { nilike: '%@test.com' } }` | `NOT ILIKE` |
87
+ | `isn` / `ne: null` | `{ verifiedAt: { isn: null } }` | `IS NOT NULL` |
88
+ | `notBetween` | `{ score: { notBetween: [40, 60] } }` | `NOT BETWEEN` |
89
+
90
+ > [!NOTE]
91
+ > `ne`/`neq`/`nin` follow SQL three-valued logic - a row whose field is `NULL` never matches them (`NULL <> value` is UNKNOWN, not TRUE). Use `exists`/`notExists` or an explicit `{ field: null }` branch to include NULL rows.
92
+
93
+
68
94
  ## Nested AND/OR
69
95
 
70
- Combine AND and OR for complex logic:
96
+ Combine AND and OR for multi-level logic.
71
97
 
72
98
  ```typescript
73
99
  // (status = 'active' AND verified = true) OR (role = 'admin')
74
100
  {
75
101
  where: {
76
102
  or: [
77
- {
78
- and: [
79
- { status: 'active' },
80
- { verified: true },
81
- ]
82
- },
103
+ { and: [{ status: 'active' }, { verified: true }] },
83
104
  { role: 'admin' },
84
105
  ]
85
106
  }
86
107
  }
87
-
88
- // status = 'active' AND (role = 'admin' OR role = 'moderator')
89
- {
90
- where: {
91
- status: 'active',
92
- or: [
93
- { role: 'admin' },
94
- { role: 'moderator' },
95
- ]
96
- }
97
- }
98
- // Equivalent to:
99
- {
100
- where: {
101
- status: 'active',
102
- role: { in: ['admin', 'moderator'] },
103
- }
104
- }
105
108
  ```
106
109
 
107
-
108
- ## Empty Groups
109
-
110
- An empty `and`/`or` array is not a no-op -- each empty case resolves to what the operator means with zero conditions:
110
+ A top-level key alongside `or` ANDs with it - these two filters are equivalent:
111
111
 
112
112
  ```typescript
113
- // Empty AND is vacuously TRUE - dropped from the query entirely
114
- { where: { and: [] } }
115
- // SQL: (no condition added)
113
+ // status = 'active' AND (role = 'admin' OR role = 'moderator')
114
+ { where: { status: 'active', or: [{ role: 'admin' }, { role: 'moderator' }] } }
116
115
 
117
- // Empty OR is vacuously FALSE - compiles to a condition that matches nothing
118
- { where: { or: [] } }
119
- // SQL: WHERE false
116
+ // Same result, using in instead
117
+ { where: { status: 'active', role: { in: ['admin', 'moderator'] } } }
120
118
  ```
121
119
 
122
- This matters when the array is built from a caller-supplied list, e.g. `{ or: permittedOrgIds.map(id => ({ orgId: id })) }`: an empty permission list must return zero rows, not every row, so `or: []` matching nothing is the safe default.
123
-
124
120
 
125
- ## NOT Logic
121
+ ## Empty Groups
126
122
 
127
- IGNIS has a general-purpose `not` operator that negates whatever condition it wraps - a bare value negates `eq`, and a nested operator object negates that operator. It is supported on the PostgreSQL connector:
123
+ An empty `and`/`or` array is not a no-op - each resolves to what the operator means with zero conditions.
128
124
 
129
125
  ```typescript
130
- // NOT equal (bare value negates eq)
131
- { where: { status: { not: 'archived' } } }
132
- // SQL: WHERE NOT ("status" = 'archived')
126
+ { where: { and: [] } }
127
+ // Vacuously TRUE - dropped from the query entirely, no condition added
133
128
 
134
- // Negate a nested operator condition
135
- { where: { views: { not: { gt: 100 } } } }
136
- // SQL: WHERE NOT ("views" > 100)
129
+ { where: { or: [] } }
130
+ // SQL: WHERE false - vacuously FALSE, matches nothing
137
131
  ```
138
132
 
139
- The dedicated negation operators remain available and are often clearer for a single condition:
140
-
141
- ```typescript
142
- // NOT equal
143
- { where: { status: { ne: 'deleted' } } }
144
- { where: { status: { neq: 'deleted' } } }
145
-
146
- // NOT IN
147
- { where: { status: { nin: ['deleted', 'banned'] } } }
148
-
149
- // NOT LIKE
150
- { where: { email: { nlike: '%@test.com' } } }
151
-
152
- // NOT ILIKE
153
- { where: { email: { nilike: '%@test.com' } } }
154
-
155
- // IS NOT NULL
156
- { where: { verifiedAt: { isn: null } } }
157
- { where: { verifiedAt: { ne: null } } }
133
+ > [!NOTE]
134
+ > This matters for a caller-built list, e.g. `{ or: permittedOrgIds.map(id => ({ orgId: id })) }`: an empty permission list must return zero rows, so `or: []` matching nothing is the safe default.
158
135
 
159
- // NOT BETWEEN
160
- { where: { score: { notBetween: [40, 60] } } }
161
- ```
162
136
 
163
- > [!NOTE]
164
- > `ne`/`neq`/`nin` follow SQL three-valued logic - a row whose field is `NULL` never matches them (`NULL <> value` is UNKNOWN, not TRUE). Use `exists`/`notExists` or an explicit `{ field: null }` branch when you need NULL rows in the result.
137
+ ## See also
165
138
 
139
+ - [Filter System Overview](./) - the `filter` shape and the full `where` operator table
140
+ - [Null Operators](./null-operators) - `isn`, one of the dedicated negation operators above
141
+ - [Comparison Operators](./comparison-operators) - `ne`/`neq`, the other dedicated negation operators
142
+ - [Quick Reference](./quick-reference) - every operator, one line each
166
143
 
167
- ## Complex Example
144
+ **Files:**
168
145
 
169
- ```typescript
170
- // Find active products that are either:
171
- // - Featured with high rating, OR
172
- // - On sale with good stock
173
- {
174
- where: {
175
- status: 'active',
176
- deletedAt: { is: null },
177
- or: [
178
- {
179
- and: [
180
- { featured: true },
181
- { rating: { gte: 4.5 } }
182
- ]
183
- },
184
- {
185
- and: [
186
- { onSale: true },
187
- { stock: { gte: 10 } }
188
- ]
189
- }
190
- ]
191
- }
192
- }
193
- ```
146
+ - [`packages/core-server/src/connectors/relational/repositories/dialect/filter.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/relational/repositories/dialect/filter.ts) - `FilterBuilder`, `buildLogicalGroupCondition`/`buildNotCondition`
147
+ - [`packages/core-server/src/connectors/postgres/repositories/dialect/query.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/postgres/repositories/dialect/query.ts) - `PostgresQueryOperators.FNS`, per-operator SQL builders
148
+ - [`packages/filter/src/common/operators.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/filter/src/common/operators.ts) - `QueryOperators` constants