@venizia/ignis-docs 0.0.3 → 0.0.4-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.
- 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 +406 -17
- 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 +2 -2
- 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 +6 -0
- package/wiki/changelogs/planned-schema-migrator.md +0 -8
- 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/{get-started → guides}/core-concepts/components.md +24 -17
- 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 +31 -26
- package/wiki/references/base/components.md +24 -7
- package/wiki/references/base/controllers.md +50 -20
- package/wiki/references/base/datasources.md +30 -0
- 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 +604 -0
- package/wiki/references/base/models.md +215 -23
- package/wiki/references/base/providers.md +731 -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 -635
- 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 +14 -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 +16 -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/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 -97
- package/wiki/get-started/best-practices/security-guidelines.md +0 -99
- package/wiki/get-started/core-concepts/persistent.md +0 -539
- 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,127 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Filter System Overview
|
|
3
|
+
description: Complete reference for the IGNIS filter system
|
|
4
|
+
difficulty: intermediate
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Filter System
|
|
8
|
+
|
|
9
|
+
Complete reference for the Ignis filter system - operators, JSON filtering, array operators, default filters, and query patterns.
|
|
10
|
+
|
|
11
|
+
> [!NOTE]
|
|
12
|
+
> If you're new to Ignis, start with:
|
|
13
|
+
> - [5-Minute Quickstart](/guides/get-started/5-minute-quickstart) - Get up and running
|
|
14
|
+
> - [Building a CRUD API](/guides/tutorials/building-a-crud-api) - Learn the basics
|
|
15
|
+
> - [Repositories](/references/base/repositories) - Repository overview
|
|
16
|
+
|
|
17
|
+
## Prerequisites
|
|
18
|
+
|
|
19
|
+
Before reading this document, you should understand:
|
|
20
|
+
|
|
21
|
+
- [Repositories](../repositories/) - Basic repository operations (find, create, update, delete)
|
|
22
|
+
- [Models](../models.md) - Entity definitions and schemas
|
|
23
|
+
- SQL basics - Understanding of WHERE clauses and operators
|
|
24
|
+
- TypeScript type system - Type safety and inference
|
|
25
|
+
|
|
26
|
+
## Documentation
|
|
27
|
+
|
|
28
|
+
| Guide | Description |
|
|
29
|
+
|-------|-------------|
|
|
30
|
+
| [**⚡ Quick Reference**](./quick-reference.md) | **Single-page cheat sheet of all operators** |
|
|
31
|
+
| [Comparison Operators](./comparison-operators.md) | Equality, range, null checks |
|
|
32
|
+
| [Pattern Matching](./pattern-matching.md) | LIKE, ILIKE, regex |
|
|
33
|
+
| [Logical Operators](./logical-operators.md) | AND, OR combinations |
|
|
34
|
+
| [List Operators](./list-operators.md) | IN, NOT IN |
|
|
35
|
+
| [Range Operators](./range-operators.md) | BETWEEN, NOT BETWEEN |
|
|
36
|
+
| [Null Operators](./null-operators.md) | IS NULL, IS NOT NULL |
|
|
37
|
+
| [Array Operators](./array-operators.md) | PostgreSQL array operations |
|
|
38
|
+
| [JSON Filtering](./json-filtering.md) | JSON/JSONB path queries |
|
|
39
|
+
| [Fields, Order, Pagination](./fields-order-pagination.md) | SELECT, ORDER BY, LIMIT |
|
|
40
|
+
| [**Default Filter**](./default-filter.md) | Automatic filter application |
|
|
41
|
+
| [Application Usage](./application-usage.md) | Filter flow in applications |
|
|
42
|
+
| [Tips & Best Practices](./tips.md) | Performance and patterns |
|
|
43
|
+
| [Use Cases](./use-cases.md) | Real-world examples |
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
## Filter Structure
|
|
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.
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
type TFilter<T> = {
|
|
52
|
+
where?: TWhere<T>; // Query conditions (SQL WHERE)
|
|
53
|
+
fields?: TFields<T>; // Column selection (SQL SELECT)
|
|
54
|
+
order?: string[]; // Sorting (SQL ORDER BY)
|
|
55
|
+
limit?: number; // Max results (SQL LIMIT)
|
|
56
|
+
skip?: number; // Pagination offset (SQL OFFSET)
|
|
57
|
+
offset?: number; // Alias for skip
|
|
58
|
+
include?: TInclusion[]; // Related data (SQL JOIN / subqueries)
|
|
59
|
+
};
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
## SQL Mapping Overview
|
|
64
|
+
|
|
65
|
+
| Filter Property | SQL Equivalent | Purpose |
|
|
66
|
+
|-----------------|----------------|---------|
|
|
67
|
+
| `where` | `WHERE` | Filter rows by conditions |
|
|
68
|
+
| `fields` | `SELECT col1, col2` | Select specific columns |
|
|
69
|
+
| `order` | `ORDER BY` | Sort results |
|
|
70
|
+
| `limit` | `LIMIT` | Restrict number of results |
|
|
71
|
+
| `skip` / `offset` | `OFFSET` | Skip rows for pagination |
|
|
72
|
+
| `include` | `JOIN` / subquery | Include related data |
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
## Basic Example
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
// Filter object
|
|
79
|
+
const filter = {
|
|
80
|
+
where: { status: 'active', role: 'admin' },
|
|
81
|
+
fields: ['id', 'name', 'email'],
|
|
82
|
+
order: ['createdAt DESC'],
|
|
83
|
+
limit: 10,
|
|
84
|
+
skip: 0
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// Equivalent SQL
|
|
88
|
+
// SELECT "id", "name", "email"
|
|
89
|
+
// FROM "User"
|
|
90
|
+
// WHERE "status" = 'active' AND "role" = 'admin'
|
|
91
|
+
// ORDER BY "created_at" DESC
|
|
92
|
+
// LIMIT 10 OFFSET 0
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
## Quick Reference
|
|
97
|
+
|
|
98
|
+
| Want to... | Filter Syntax |
|
|
99
|
+
|------------|---------------|
|
|
100
|
+
| Equals | `{ field: value }` or `{ field: { eq: value } }` |
|
|
101
|
+
| Not equals | `{ field: { ne: value } }` |
|
|
102
|
+
| Greater than | `{ field: { gt: value } }` |
|
|
103
|
+
| Greater or equal | `{ field: { gte: value } }` |
|
|
104
|
+
| Less than | `{ field: { lt: value } }` |
|
|
105
|
+
| Less or equal | `{ field: { lte: value } }` |
|
|
106
|
+
| Is null | `{ field: null }` or `{ field: { is: null } }` |
|
|
107
|
+
| Is not null | `{ field: { isn: null } }` |
|
|
108
|
+
| In list | `{ field: { in: [a, b, c] } }` |
|
|
109
|
+
| Not in list | `{ field: { nin: [a, b, c] } }` |
|
|
110
|
+
| Range | `{ field: { between: [min, max] } }` |
|
|
111
|
+
| Outside range | `{ field: { notBetween: [min, max] } }` |
|
|
112
|
+
| Contains pattern | `{ field: { like: '%pattern%' } }` |
|
|
113
|
+
| Case-insensitive | `{ field: { ilike: '%pattern%' } }` |
|
|
114
|
+
| Regex match | `{ field: { regexp: '^pattern$' } }` |
|
|
115
|
+
| Array contains all | `{ arrayField: { contains: [a, b] } }` |
|
|
116
|
+
| Array is subset | `{ arrayField: { containedBy: [a, b, c] } }` |
|
|
117
|
+
| Array overlaps | `{ arrayField: { overlaps: [a, b] } }` |
|
|
118
|
+
| JSON nested | `{ 'jsonField.nested.path': value }` |
|
|
119
|
+
| JSON with operator | `{ 'jsonField.path': { gt: 10 } }` |
|
|
120
|
+
| AND conditions | `{ a: 1, b: 2 }` or `{ and: [{a: 1}, {b: 2}] }` |
|
|
121
|
+
| OR conditions | `{ or: [{ a: 1 }, { b: 2 }] }` |
|
|
122
|
+
| Include relation | `{ include: [{ relation: 'name' }] }` |
|
|
123
|
+
| Nested include | `{ include: [{ relation: 'a', scope: { include: [{ relation: 'b' }] } }] }` |
|
|
124
|
+
| Select fields | `{ fields: ['id', 'name'] }` |
|
|
125
|
+
| Order by | `{ order: ['field DESC'] }` |
|
|
126
|
+
| Order by JSON | `{ order: ['jsonField.path DESC'] }` |
|
|
127
|
+
| Paginate | `{ limit: 10, skip: 20 }` |
|
|
@@ -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
|
+
```
|