@venizia/ignis-docs 0.0.2 → 0.0.4-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +4 -2
- package/wiki/best-practices/api-usage-examples.md +591 -0
- package/wiki/best-practices/architectural-patterns.md +415 -0
- package/wiki/best-practices/architecture-decisions.md +488 -0
- package/wiki/{get-started/best-practices → best-practices}/code-style-standards.md +647 -182
- package/wiki/{get-started/best-practices → best-practices}/common-pitfalls.md +109 -4
- package/wiki/{get-started/best-practices → best-practices}/contribution-workflow.md +34 -7
- package/wiki/best-practices/data-modeling.md +376 -0
- package/wiki/best-practices/deployment-strategies.md +698 -0
- package/wiki/best-practices/index.md +27 -0
- package/wiki/best-practices/performance-optimization.md +196 -0
- package/wiki/best-practices/security-guidelines.md +218 -0
- package/wiki/{get-started/best-practices → best-practices}/troubleshooting-tips.md +97 -1
- package/wiki/changelogs/2025-12-16-initial-architecture.md +1 -1
- package/wiki/changelogs/2025-12-16-model-repo-datasource-refactor.md +1 -1
- package/wiki/changelogs/2025-12-17-refactor.md +1 -1
- package/wiki/changelogs/2025-12-18-performance-optimizations.md +5 -5
- package/wiki/changelogs/2025-12-18-repository-validation-security.md +13 -7
- package/wiki/changelogs/2025-12-26-nested-relations-and-generics.md +86 -0
- package/wiki/changelogs/2025-12-26-transaction-support.md +57 -0
- package/wiki/changelogs/2025-12-29-dynamic-binding-registration.md +104 -0
- package/wiki/changelogs/2025-12-29-snowflake-uid-helper.md +100 -0
- package/wiki/changelogs/2025-12-30-repository-enhancements.md +214 -0
- package/wiki/changelogs/2025-12-31-json-path-filtering-array-operators.md +214 -0
- package/wiki/changelogs/2025-12-31-string-id-custom-generator.md +137 -0
- package/wiki/changelogs/2026-01-02-default-filter-and-repository-mixins.md +418 -0
- package/wiki/changelogs/index.md +8 -1
- package/wiki/changelogs/planned-schema-migrator.md +2 -10
- package/wiki/{get-started/core-concepts → guides/core-concepts/application}/bootstrapping.md +18 -5
- package/wiki/{get-started/core-concepts/application.md → guides/core-concepts/application/index.md} +47 -104
- package/wiki/guides/core-concepts/components-guide.md +509 -0
- package/wiki/guides/core-concepts/components.md +122 -0
- package/wiki/{get-started → guides}/core-concepts/controllers.md +30 -13
- package/wiki/{get-started → guides}/core-concepts/dependency-injection.md +97 -0
- package/wiki/guides/core-concepts/persistent/datasources.md +179 -0
- package/wiki/guides/core-concepts/persistent/index.md +119 -0
- package/wiki/guides/core-concepts/persistent/models.md +241 -0
- package/wiki/guides/core-concepts/persistent/repositories.md +219 -0
- package/wiki/guides/core-concepts/persistent/transactions.md +170 -0
- package/wiki/{get-started → guides}/core-concepts/services.md +26 -3
- package/wiki/{get-started → guides/get-started}/5-minute-quickstart.md +59 -14
- package/wiki/guides/get-started/philosophy.md +682 -0
- package/wiki/guides/get-started/setup.md +157 -0
- package/wiki/guides/index.md +89 -0
- package/wiki/guides/reference/glossary.md +243 -0
- package/wiki/{get-started → guides/reference}/mcp-docs-server.md +0 -10
- package/wiki/{get-started → guides/tutorials}/building-a-crud-api.md +134 -132
- package/wiki/{get-started/quickstart.md → guides/tutorials/complete-installation.md} +107 -71
- package/wiki/guides/tutorials/ecommerce-api.md +1399 -0
- package/wiki/guides/tutorials/realtime-chat.md +1261 -0
- package/wiki/guides/tutorials/testing.md +723 -0
- package/wiki/index.md +176 -37
- package/wiki/references/base/application.md +27 -0
- package/wiki/references/base/bootstrapping.md +30 -26
- package/wiki/references/base/components.md +532 -31
- package/wiki/references/base/controllers.md +136 -38
- package/wiki/references/base/datasources.md +108 -5
- package/wiki/references/base/dependency-injection.md +39 -3
- package/wiki/references/base/filter-system/application-usage.md +224 -0
- package/wiki/references/base/filter-system/array-operators.md +132 -0
- package/wiki/references/base/filter-system/comparison-operators.md +109 -0
- package/wiki/references/base/filter-system/default-filter.md +428 -0
- package/wiki/references/base/filter-system/fields-order-pagination.md +155 -0
- package/wiki/references/base/filter-system/index.md +127 -0
- package/wiki/references/base/filter-system/json-filtering.md +197 -0
- package/wiki/references/base/filter-system/list-operators.md +71 -0
- package/wiki/references/base/filter-system/logical-operators.md +156 -0
- package/wiki/references/base/filter-system/null-operators.md +58 -0
- package/wiki/references/base/filter-system/pattern-matching.md +108 -0
- package/wiki/references/base/filter-system/quick-reference.md +431 -0
- package/wiki/references/base/filter-system/range-operators.md +63 -0
- package/wiki/references/base/filter-system/tips.md +190 -0
- package/wiki/references/base/filter-system/use-cases.md +452 -0
- package/wiki/references/base/index.md +90 -0
- package/wiki/references/base/middlewares.md +602 -0
- package/wiki/references/base/models.md +215 -23
- package/wiki/references/base/providers.md +732 -0
- package/wiki/references/base/repositories/advanced.md +555 -0
- package/wiki/references/base/repositories/index.md +228 -0
- package/wiki/references/base/repositories/mixins.md +331 -0
- package/wiki/references/base/repositories/relations.md +486 -0
- package/wiki/references/base/repositories.md +40 -549
- package/wiki/references/base/services.md +28 -4
- package/wiki/references/components/authentication.md +22 -2
- package/wiki/references/components/health-check.md +12 -0
- package/wiki/references/components/index.md +23 -0
- package/wiki/references/components/mail.md +687 -0
- package/wiki/references/components/request-tracker.md +16 -0
- package/wiki/references/components/socket-io.md +18 -0
- package/wiki/references/components/static-asset.md +14 -26
- package/wiki/references/components/swagger.md +17 -0
- package/wiki/references/configuration/environment-variables.md +427 -0
- package/wiki/references/configuration/index.md +73 -0
- package/wiki/references/helpers/cron.md +14 -0
- package/wiki/references/helpers/crypto.md +15 -0
- package/wiki/references/helpers/env.md +16 -0
- package/wiki/references/helpers/error.md +17 -0
- package/wiki/references/helpers/index.md +15 -0
- package/wiki/references/helpers/inversion.md +24 -4
- package/wiki/references/helpers/logger.md +19 -0
- package/wiki/references/helpers/network.md +11 -0
- package/wiki/references/helpers/queue.md +19 -0
- package/wiki/references/helpers/redis.md +21 -0
- package/wiki/references/helpers/socket-io.md +24 -5
- package/wiki/references/helpers/storage.md +18 -10
- package/wiki/references/helpers/testing.md +18 -0
- package/wiki/references/helpers/types.md +167 -0
- package/wiki/references/helpers/uid.md +167 -0
- package/wiki/references/helpers/worker-thread.md +16 -0
- package/wiki/references/index.md +177 -0
- package/wiki/references/quick-reference.md +634 -0
- package/wiki/references/src-details/boot.md +3 -3
- package/wiki/references/src-details/dev-configs.md +0 -4
- package/wiki/references/src-details/docs.md +2 -2
- package/wiki/references/src-details/index.md +86 -0
- package/wiki/references/src-details/inversion.md +1 -6
- package/wiki/references/src-details/mcp-server.md +3 -15
- package/wiki/references/utilities/index.md +86 -10
- package/wiki/references/utilities/jsx.md +577 -0
- package/wiki/references/utilities/request.md +0 -2
- package/wiki/references/utilities/statuses.md +740 -0
- package/wiki/changelogs/planned-transaction-support.md +0 -216
- package/wiki/get-started/best-practices/api-usage-examples.md +0 -266
- package/wiki/get-started/best-practices/architectural-patterns.md +0 -170
- package/wiki/get-started/best-practices/data-modeling.md +0 -177
- package/wiki/get-started/best-practices/deployment-strategies.md +0 -121
- package/wiki/get-started/best-practices/performance-optimization.md +0 -88
- package/wiki/get-started/best-practices/security-guidelines.md +0 -99
- package/wiki/get-started/core-concepts/components.md +0 -98
- package/wiki/get-started/core-concepts/persistent.md +0 -543
- package/wiki/get-started/index.md +0 -65
- package/wiki/get-started/philosophy.md +0 -296
- package/wiki/get-started/prerequisites.md +0 -113
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: JSON/JSONB Filtering
|
|
3
|
+
description: Query nested fields within JSON/JSONB columns using dot notation
|
|
4
|
+
difficulty: intermediate
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# JSON/JSONB Filtering
|
|
8
|
+
|
|
9
|
+
Query nested fields within JSON/JSONB columns using dot notation. This is a PostgreSQL-specific feature.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## Basic JSON Path Syntax
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// Column: metadata jsonb
|
|
16
|
+
// Data: { "user": { "id": 123, "role": "admin" }, "tags": ["urgent"] }
|
|
17
|
+
|
|
18
|
+
// Simple nested field
|
|
19
|
+
{ where: { 'metadata.user.id': 123 } }
|
|
20
|
+
// SQL: "metadata" #>> '{user,id}' = '123'
|
|
21
|
+
|
|
22
|
+
// Deep nesting
|
|
23
|
+
{ where: { 'metadata.user.role': 'admin' } }
|
|
24
|
+
// SQL: "metadata" #>> '{user,role}' = 'admin'
|
|
25
|
+
|
|
26
|
+
// Array index access
|
|
27
|
+
{ where: { 'metadata.tags[0]': 'urgent' } }
|
|
28
|
+
// SQL: "metadata" #>> '{tags,0}' = 'urgent'
|
|
29
|
+
|
|
30
|
+
// Kebab-case keys
|
|
31
|
+
{ where: { 'metadata.user-id': 'abc123' } }
|
|
32
|
+
// SQL: "metadata" #>> '{user-id}' = 'abc123'
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## Supported Path Formats
|
|
37
|
+
|
|
38
|
+
| Format | Example | SQL Path |
|
|
39
|
+
|--------|---------|----------|
|
|
40
|
+
| Simple field | `metadata.name` | `{name}` |
|
|
41
|
+
| Nested field | `metadata.user.email` | `{user,email}` |
|
|
42
|
+
| Array index | `metadata.tags[0]` | `{tags,0}` |
|
|
43
|
+
| Nested with array | `metadata.items[2].name` | `{items,2,name}` |
|
|
44
|
+
| Kebab-case | `metadata.user-id` | `{user-id}` |
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
## JSON with Operators
|
|
48
|
+
|
|
49
|
+
All standard operators work with JSON paths:
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
// Numeric comparison (automatic safe casting)
|
|
53
|
+
{ where: { 'metadata.score': { gt: 80 } } }
|
|
54
|
+
// SQL: CASE WHEN ("metadata" #>> '{score}') ~ '^-?[0-9]+(\.[0-9]+)?$'
|
|
55
|
+
// THEN ("metadata" #>> '{score}')::numeric ELSE NULL END > 80
|
|
56
|
+
|
|
57
|
+
// Range comparison
|
|
58
|
+
{ where: { 'metadata.priority': { gte: 1, lte: 5 } } }
|
|
59
|
+
|
|
60
|
+
// Between
|
|
61
|
+
{ where: { 'metadata.score': { between: [70, 90] } } }
|
|
62
|
+
|
|
63
|
+
// Pattern matching
|
|
64
|
+
{ where: { 'metadata.level': { ilike: '%high%' } } }
|
|
65
|
+
// SQL: "metadata" #>> '{level}' ILIKE '%high%'
|
|
66
|
+
|
|
67
|
+
// IN operator
|
|
68
|
+
{ where: { 'metadata.status': { in: ['pending', 'review'] } } }
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
## Safe Numeric Casting
|
|
73
|
+
|
|
74
|
+
JSON fields may contain mixed types. Ignis uses safe casting to prevent database errors:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
// Data in database:
|
|
78
|
+
// Row 1: { "score": 85 } <- number
|
|
79
|
+
// Row 2: { "score": "high" } <- string
|
|
80
|
+
// Row 3: { "score": null } <- null
|
|
81
|
+
|
|
82
|
+
// Query with numeric operator
|
|
83
|
+
{ where: { 'metadata.score': { gt: 50 } } }
|
|
84
|
+
|
|
85
|
+
// Result:
|
|
86
|
+
// Row 1: 85 > 50 -> matched
|
|
87
|
+
// Row 2: "high" -> NULL -> not matched
|
|
88
|
+
// Row 3: null -> NULL -> not matched
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
| JSON Value | Numeric Operation Result |
|
|
92
|
+
|------------|-------------------------|
|
|
93
|
+
| `{ "score": 85 }` | Compares as `85` |
|
|
94
|
+
| `{ "score": "high" }` | Treated as `NULL` (no match) |
|
|
95
|
+
| `{ "score": null }` | Treated as `NULL` (no match) |
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
## Boolean Values
|
|
99
|
+
|
|
100
|
+
Booleans are compared as TEXT strings:
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
// JSON data: { "enabled": true }
|
|
104
|
+
|
|
105
|
+
{ where: { 'metadata.enabled': true } }
|
|
106
|
+
// SQL: "metadata" #>> '{enabled}' = 'true'
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
## JSON Path Ordering
|
|
111
|
+
|
|
112
|
+
Order results by JSON fields:
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
{ filter: { order: ['metadata.priority DESC'] } }
|
|
116
|
+
// SQL: ORDER BY "metadata" #> '{priority}' DESC
|
|
117
|
+
|
|
118
|
+
// Multiple JSON fields
|
|
119
|
+
{ filter: { order: ['metadata.priority DESC', 'metadata.score ASC'] } }
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Sort Order for JSONB Types:**
|
|
123
|
+
|
|
124
|
+
| JSONB Type | Sort Order |
|
|
125
|
+
|------------|------------|
|
|
126
|
+
| `null` | First (lowest) |
|
|
127
|
+
| `boolean` | `false` < `true` |
|
|
128
|
+
| `number` | Numeric order |
|
|
129
|
+
| `string` | Lexicographic |
|
|
130
|
+
| `array` | Element-wise |
|
|
131
|
+
| `object` | Key-value |
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
## Path Validation & Security
|
|
135
|
+
|
|
136
|
+
Path components are validated to prevent SQL injection:
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
// Valid paths
|
|
140
|
+
'metadata.fieldName'
|
|
141
|
+
'metadata.nested.deep.value'
|
|
142
|
+
'data.items[0]'
|
|
143
|
+
'config.user_id'
|
|
144
|
+
'data.meta-data' // kebab-case allowed
|
|
145
|
+
|
|
146
|
+
// Invalid (throws error)
|
|
147
|
+
'metadata.field;DROP TABLE'
|
|
148
|
+
'data.123invalid'
|
|
149
|
+
'config.(SELECT * FROM users)'
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Error Messages:**
|
|
153
|
+
```
|
|
154
|
+
// Non-JSON column
|
|
155
|
+
Error: Column 'name' is not JSON/JSONB type | dataType: 'text'
|
|
156
|
+
|
|
157
|
+
// Invalid path
|
|
158
|
+
Error: Invalid JSON path component: 'field;DROP'
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
## Performance Tips
|
|
163
|
+
|
|
164
|
+
1. **Index Your JSON Paths:**
|
|
165
|
+
```sql
|
|
166
|
+
CREATE INDEX idx_metadata_priority ON "Product" (("metadata" ->> 'priority'));
|
|
167
|
+
CREATE INDEX idx_metadata_gin ON "Product" USING GIN ("metadata");
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
2. **Use Appropriate Types in JSON:**
|
|
171
|
+
```json
|
|
172
|
+
// Good
|
|
173
|
+
{ "priority": 3, "enabled": true }
|
|
174
|
+
|
|
175
|
+
// Bad
|
|
176
|
+
{ "priority": "3", "enabled": "true" }
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
3. **Keep Paths Shallow:**
|
|
180
|
+
```typescript
|
|
181
|
+
// Easier to work with
|
|
182
|
+
'metadata.priority'
|
|
183
|
+
|
|
184
|
+
// Harder to optimize
|
|
185
|
+
'data.level1.level2.level3.level4.value'
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
## Null-Safe JSON Paths
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
// If JSON field doesn't exist, #>> returns NULL
|
|
193
|
+
// This is safe - no errors, just no matches
|
|
194
|
+
{ where: { 'metadata.nonexistent.field': 'value' } }
|
|
195
|
+
// SQL: "metadata" #>> '{nonexistent,field}' = 'value'
|
|
196
|
+
// Result: No rows (NULL != 'value')
|
|
197
|
+
```
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: List Operators
|
|
3
|
+
description: Operators for matching values against arrays
|
|
4
|
+
difficulty: intermediate
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# List Operators
|
|
8
|
+
|
|
9
|
+
Operators for matching values against arrays.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## in / inq - In Array
|
|
13
|
+
|
|
14
|
+
Matches records where field value is in the provided array.
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
{ where: { status: { in: ['active', 'pending', 'review'] } } }
|
|
18
|
+
{ where: { status: { inq: ['active', 'pending', 'review'] } } } // Alias
|
|
19
|
+
|
|
20
|
+
// 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
|
+
```
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
## nin - Not In Array
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
{ where: { status: { nin: ['deleted', 'archived', 'banned'] } } }
|
|
32
|
+
// SQL: WHERE "status" NOT IN ('deleted', 'archived', 'banned')
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## Edge Cases
|
|
37
|
+
|
|
38
|
+
| Scenario | Behavior |
|
|
39
|
+
|----------|----------|
|
|
40
|
+
| `{ in: [] }` (empty array) | Returns no rows (`false`) |
|
|
41
|
+
| `{ nin: [] }` (empty array) | Returns all rows (`true`) |
|
|
42
|
+
| `{ in: 'value' }` (non-array) | Treated as `{ eq: 'value' }` |
|
|
43
|
+
|
|
44
|
+
> [!WARNING]
|
|
45
|
+
> `NOT IN` excludes rows where the column is `NULL`. If your column can be `NULL`, use `OR` to include them:
|
|
46
|
+
> ```typescript
|
|
47
|
+
> where: {
|
|
48
|
+
> or: [
|
|
49
|
+
> { status: { nin: ['deleted'] } },
|
|
50
|
+
> { status: { is: null } }
|
|
51
|
+
> ]
|
|
52
|
+
> }
|
|
53
|
+
> ```
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
## Performance Tip
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// For very large arrays (1000+ items), consider chunking
|
|
60
|
+
const allIds = getLargeIdList(); // 5000 IDs
|
|
61
|
+
|
|
62
|
+
const chunkSize = 500;
|
|
63
|
+
const results = [];
|
|
64
|
+
for (let i = 0; i < allIds.length; i += chunkSize) {
|
|
65
|
+
const chunk = allIds.slice(i, i + chunkSize);
|
|
66
|
+
const chunkResults = await repo.find({
|
|
67
|
+
filter: { where: { id: { in: chunk } } }
|
|
68
|
+
});
|
|
69
|
+
results.push(...chunkResults);
|
|
70
|
+
}
|
|
71
|
+
```
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Logical Operators
|
|
3
|
+
description: Combine multiple conditions with AND and OR logic
|
|
4
|
+
difficulty: intermediate
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Logical Operators
|
|
8
|
+
|
|
9
|
+
Combine multiple conditions with AND and OR logic.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## Implicit AND
|
|
13
|
+
|
|
14
|
+
Multiple conditions in the same object are combined with AND:
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
{
|
|
18
|
+
where: {
|
|
19
|
+
status: 'active',
|
|
20
|
+
role: 'admin',
|
|
21
|
+
verified: true,
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// SQL: WHERE "status" = 'active' AND "role" = 'admin' AND "verified" = true
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
## Explicit AND
|
|
29
|
+
|
|
30
|
+
Use `and` array for explicit AND conditions:
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
{
|
|
34
|
+
where: {
|
|
35
|
+
and: [
|
|
36
|
+
{ status: 'active' },
|
|
37
|
+
{ role: { in: ['admin', 'moderator'] } },
|
|
38
|
+
{ createdAt: { gte: new Date('2024-01-01') } },
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// SQL: WHERE ("status" = 'active')
|
|
43
|
+
// AND ("role" IN ('admin', 'moderator'))
|
|
44
|
+
// AND ("created_at" >= '2024-01-01')
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
## OR Operator
|
|
49
|
+
|
|
50
|
+
Use `or` array for OR conditions:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
{
|
|
54
|
+
where: {
|
|
55
|
+
or: [
|
|
56
|
+
{ status: 'active' },
|
|
57
|
+
{ isPublished: true },
|
|
58
|
+
{ featured: true },
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// SQL: WHERE ("status" = 'active')
|
|
63
|
+
// OR ("is_published" = true)
|
|
64
|
+
// OR ("featured" = true)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
## Nested AND/OR
|
|
69
|
+
|
|
70
|
+
Combine AND and OR for complex logic:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
// (status = 'active' AND verified = true) OR (role = 'admin')
|
|
74
|
+
{
|
|
75
|
+
where: {
|
|
76
|
+
or: [
|
|
77
|
+
{
|
|
78
|
+
and: [
|
|
79
|
+
{ status: 'active' },
|
|
80
|
+
{ verified: true },
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
{ role: 'admin' },
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
}
|
|
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
|
+
```
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
## NOT Logic
|
|
109
|
+
|
|
110
|
+
Use negation operators for NOT conditions:
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
// NOT equal
|
|
114
|
+
{ where: { status: { ne: 'deleted' } } }
|
|
115
|
+
|
|
116
|
+
// NOT IN
|
|
117
|
+
{ where: { status: { nin: ['deleted', 'banned'] } } }
|
|
118
|
+
|
|
119
|
+
// NOT LIKE
|
|
120
|
+
{ where: { email: { nlike: '%@test.com' } } }
|
|
121
|
+
|
|
122
|
+
// NOT NULL
|
|
123
|
+
{ where: { verifiedAt: { isn: null } } }
|
|
124
|
+
|
|
125
|
+
// NOT BETWEEN
|
|
126
|
+
{ where: { score: { notBetween: [40, 60] } } }
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
## Complex Example
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
// Find active products that are either:
|
|
134
|
+
// - Featured with high rating, OR
|
|
135
|
+
// - On sale with good stock
|
|
136
|
+
{
|
|
137
|
+
where: {
|
|
138
|
+
status: 'active',
|
|
139
|
+
deletedAt: { is: null },
|
|
140
|
+
or: [
|
|
141
|
+
{
|
|
142
|
+
and: [
|
|
143
|
+
{ featured: true },
|
|
144
|
+
{ rating: { gte: 4.5 } }
|
|
145
|
+
]
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
and: [
|
|
149
|
+
{ onSale: true },
|
|
150
|
+
{ stock: { gte: 10 } }
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
]
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Null Check Operators
|
|
3
|
+
description: Operators for checking null and non-null values
|
|
4
|
+
difficulty: intermediate
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Null Check Operators
|
|
8
|
+
|
|
9
|
+
Operators for checking null and non-null values.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## is - IS NULL / Equality
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// NULL check
|
|
16
|
+
{ where: { deletedAt: { is: null } } }
|
|
17
|
+
// SQL: WHERE "deleted_at" IS NULL
|
|
18
|
+
|
|
19
|
+
// Value check (same as eq)
|
|
20
|
+
{ where: { status: { is: 'active' } } }
|
|
21
|
+
// SQL: WHERE "status" = 'active'
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## isn - IS NOT NULL / Not Equality
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
// NOT NULL check
|
|
29
|
+
{ where: { verifiedAt: { isn: null } } }
|
|
30
|
+
// SQL: WHERE "verified_at" IS NOT NULL
|
|
31
|
+
|
|
32
|
+
// Value check (same as ne)
|
|
33
|
+
{ where: { status: { isn: 'deleted' } } }
|
|
34
|
+
// SQL: WHERE "status" != 'deleted'
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
## Common Patterns
|
|
39
|
+
|
|
40
|
+
### Soft Delete Pattern
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
// Find active records (not deleted)
|
|
44
|
+
{ where: { deletedAt: { is: null } } }
|
|
45
|
+
|
|
46
|
+
// Find deleted records only
|
|
47
|
+
{ where: { deletedAt: { isn: null } } }
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Verified Users
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
// Find verified users
|
|
54
|
+
{ where: { emailVerifiedAt: { isn: null } } }
|
|
55
|
+
|
|
56
|
+
// Find unverified users
|
|
57
|
+
{ where: { emailVerifiedAt: { is: null } } }
|
|
58
|
+
```
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Pattern Matching Operators
|
|
3
|
+
description: Operators for string pattern matching and regular expressions
|
|
4
|
+
difficulty: intermediate
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Pattern Matching Operators
|
|
8
|
+
|
|
9
|
+
Operators for string pattern matching and regular expressions.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## like - Pattern Matching (Case-Sensitive)
|
|
13
|
+
|
|
14
|
+
Matches strings using SQL LIKE patterns.
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
// Starts with
|
|
18
|
+
{ where: { email: { like: '%@gmail.com' } } }
|
|
19
|
+
// SQL: WHERE "email" LIKE '%@gmail.com'
|
|
20
|
+
|
|
21
|
+
// Contains
|
|
22
|
+
{ where: { name: { like: '%john%' } } }
|
|
23
|
+
// SQL: WHERE "name" LIKE '%john%'
|
|
24
|
+
|
|
25
|
+
// Ends with
|
|
26
|
+
{ where: { filename: { like: '%.pdf' } } }
|
|
27
|
+
// SQL: WHERE "filename" LIKE '%.pdf'
|
|
28
|
+
|
|
29
|
+
// Single character wildcard
|
|
30
|
+
{ where: { code: { like: 'A_B' } } } // Matches 'A1B', 'AXB', etc.
|
|
31
|
+
// SQL: WHERE "code" LIKE 'A_B'
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Pattern Characters:**
|
|
35
|
+
- `%` - Matches any sequence of characters (including empty)
|
|
36
|
+
- `_` - Matches exactly one character
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
## nlike - Not Like
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
{ where: { email: { nlike: '%@test.com' } } }
|
|
43
|
+
// SQL: WHERE "email" NOT LIKE '%@test.com'
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
## ilike - Case-Insensitive Pattern Matching
|
|
48
|
+
|
|
49
|
+
PostgreSQL-specific case-insensitive LIKE.
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
{ where: { name: { ilike: '%john%' } } }
|
|
53
|
+
// SQL: WHERE "name" ILIKE '%john%'
|
|
54
|
+
// Matches: 'John', 'JOHN', 'john', 'JoHn'
|
|
55
|
+
|
|
56
|
+
{ where: { email: { ilike: '%@GMAIL.COM' } } }
|
|
57
|
+
// Matches: 'user@gmail.com', 'USER@Gmail.Com'
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
## nilike - Not ILike
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
{ where: { email: { nilike: '%@example%' } } }
|
|
65
|
+
// SQL: WHERE NOT ("email" ILIKE '%@example%')
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
## regexp - Regular Expression (Case-Sensitive)
|
|
70
|
+
|
|
71
|
+
PostgreSQL POSIX regex matching.
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
// Starts with letter
|
|
75
|
+
{ where: { code: { regexp: '^[A-Z]' } } }
|
|
76
|
+
// SQL: WHERE "code" ~ '^[A-Z]'
|
|
77
|
+
|
|
78
|
+
// Email pattern
|
|
79
|
+
{ where: { email: { regexp: '^[a-z]+@[a-z]+\\.[a-z]+$' } } }
|
|
80
|
+
// SQL: WHERE "email" ~ '^[a-z]+@[a-z]+\.[a-z]+$'
|
|
81
|
+
|
|
82
|
+
// Phone number pattern
|
|
83
|
+
{ where: { phone: { regexp: '^\\+?[0-9]{10,15}$' } } }
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
> [!NOTE]
|
|
87
|
+
> Escape backslashes in TypeScript strings: `\\d` for regex `\d`.
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
## iregexp - Case-Insensitive Regular Expression
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
{ where: { name: { iregexp: '^john' } } }
|
|
94
|
+
// SQL: WHERE "name" ~* '^john'
|
|
95
|
+
// Matches: 'John Doe', 'JOHN SMITH', 'john'
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
## Summary
|
|
100
|
+
|
|
101
|
+
| Operator | SQL | Case | Description |
|
|
102
|
+
|----------|-----|------|-------------|
|
|
103
|
+
| `like` | `LIKE` | Sensitive | Pattern with wildcards |
|
|
104
|
+
| `nlike` | `NOT LIKE` | Sensitive | Negative pattern |
|
|
105
|
+
| `ilike` | `ILIKE` | Insensitive | PostgreSQL only |
|
|
106
|
+
| `nilike` | `NOT ILIKE` | Insensitive | PostgreSQL only |
|
|
107
|
+
| `regexp` | `~` | Sensitive | POSIX regex match |
|
|
108
|
+
| `iregexp` | `~*` | Insensitive | POSIX regex match |
|