@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,224 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Using Filters in Your Application
|
|
3
|
+
description: How filters flow through application layers
|
|
4
|
+
difficulty: intermediate
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Using Filters in Your Application
|
|
8
|
+
|
|
9
|
+
How filters flow through the application layers.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## Architecture Overview
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
+-----------------------------------------------------------------+
|
|
16
|
+
| HTTP Request |
|
|
17
|
+
| GET /products?filter={"where":{"status":"active"},"limit":10} |
|
|
18
|
+
+--------------------------------+--------------------------------+
|
|
19
|
+
|
|
|
20
|
+
v
|
|
21
|
+
+-----------------------------------------------------------------+
|
|
22
|
+
| Controller Layer |
|
|
23
|
+
| - Validates filter via Zod schema |
|
|
24
|
+
| - Parses JSON string -> Filter object |
|
|
25
|
+
| - Passes to service/repository |
|
|
26
|
+
+-----------------------------------------------------------------+
|
|
27
|
+
|
|
|
28
|
+
v
|
|
29
|
+
+-----------------------------------------------------------------+
|
|
30
|
+
| Service Layer (Optional) |
|
|
31
|
+
| - Business logic, authorization |
|
|
32
|
+
| - May modify filter before passing |
|
|
33
|
+
+-----------------------------------------------------------------+
|
|
34
|
+
|
|
|
35
|
+
v
|
|
36
|
+
+-----------------------------------------------------------------+
|
|
37
|
+
| Repository Layer |
|
|
38
|
+
| - FilterBuilder transforms Filter -> SQL |
|
|
39
|
+
| - Executes query via Drizzle ORM |
|
|
40
|
+
| - Returns typed results |
|
|
41
|
+
+-----------------------------------------------------------------+
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
## Controller Layer
|
|
46
|
+
|
|
47
|
+
### Using ControllerFactory (Recommended)
|
|
48
|
+
|
|
49
|
+
The `ControllerFactory` automatically handles filter parsing and validation:
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
// src/controllers/product.controller.ts
|
|
53
|
+
import { Product } from '@/models';
|
|
54
|
+
import { ProductRepository } from '@/repositories';
|
|
55
|
+
import {
|
|
56
|
+
controller,
|
|
57
|
+
ControllerFactory,
|
|
58
|
+
inject,
|
|
59
|
+
BindingKeys,
|
|
60
|
+
BindingNamespaces,
|
|
61
|
+
} from '@venizia/ignis';
|
|
62
|
+
|
|
63
|
+
const BASE_PATH = '/products';
|
|
64
|
+
|
|
65
|
+
const _Controller = ControllerFactory.defineCrudController({
|
|
66
|
+
repository: { name: ProductRepository.name },
|
|
67
|
+
controller: {
|
|
68
|
+
name: 'ProductController',
|
|
69
|
+
basePath: BASE_PATH,
|
|
70
|
+
isStrict: true,
|
|
71
|
+
defaultLimit: 20,
|
|
72
|
+
},
|
|
73
|
+
entity: () => Product,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
@controller({ path: BASE_PATH })
|
|
77
|
+
export class ProductController extends _Controller {
|
|
78
|
+
constructor(
|
|
79
|
+
@inject({
|
|
80
|
+
key: BindingKeys.build({
|
|
81
|
+
namespace: BindingNamespaces.REPOSITORY,
|
|
82
|
+
key: ProductRepository.name,
|
|
83
|
+
}),
|
|
84
|
+
})
|
|
85
|
+
repository: ProductRepository,
|
|
86
|
+
) {
|
|
87
|
+
super(repository);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Generated Endpoints:**
|
|
93
|
+
|
|
94
|
+
| Method | Endpoint | Filter Location |
|
|
95
|
+
|--------|----------|-----------------|
|
|
96
|
+
| GET | `/products` | Query param: `?filter={...}` |
|
|
97
|
+
| GET | `/products/:id` | Query param: `?filter={...}` (for includes) |
|
|
98
|
+
| GET | `/products/one` | Query param: `?filter={...}` |
|
|
99
|
+
| GET | `/products/count` | Query param: `?where={...}` |
|
|
100
|
+
|
|
101
|
+
### Custom Controller with Manual Filter Handling
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
@controller({ path: '/products' })
|
|
105
|
+
export class ProductController extends BaseController {
|
|
106
|
+
constructor(
|
|
107
|
+
@inject({ key: 'repositories.ProductRepository' })
|
|
108
|
+
private _productRepo: ProductRepository,
|
|
109
|
+
) {
|
|
110
|
+
super({ scope: 'ProductController', path: '/products' });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
override binding() {
|
|
114
|
+
this.defineRoute({
|
|
115
|
+
configs: {
|
|
116
|
+
path: '/search',
|
|
117
|
+
method: 'get',
|
|
118
|
+
query: {
|
|
119
|
+
filter: FilterSchema,
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
handler: async (context) => {
|
|
123
|
+
const { filter = {} } = context.req.valid('query');
|
|
124
|
+
const results = await this._productRepo.find({ filter });
|
|
125
|
+
return context.json(results);
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
## Service Layer
|
|
134
|
+
|
|
135
|
+
Services can modify filters before passing to repositories:
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
@service()
|
|
139
|
+
export class ProductService {
|
|
140
|
+
constructor(
|
|
141
|
+
@inject({ key: 'repositories.ProductRepository' })
|
|
142
|
+
private _productRepo: ProductRepository,
|
|
143
|
+
) {}
|
|
144
|
+
|
|
145
|
+
async findProducts(filter: TFilter<TProductSchema> = {}) {
|
|
146
|
+
// Merge user filter with soft-delete condition
|
|
147
|
+
const enhancedFilter: TFilter<TProductSchema> = {
|
|
148
|
+
...filter,
|
|
149
|
+
where: {
|
|
150
|
+
...filter.where,
|
|
151
|
+
deletedAt: { is: null },
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
return this._productRepo.find({ filter: enhancedFilter });
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async findProductsForTenant(
|
|
159
|
+
tenantId: string,
|
|
160
|
+
filter: TFilter<TProductSchema> = {},
|
|
161
|
+
) {
|
|
162
|
+
const isolatedFilter: TFilter<TProductSchema> = {
|
|
163
|
+
...filter,
|
|
164
|
+
where: {
|
|
165
|
+
...filter.where,
|
|
166
|
+
tenantId,
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
return this._productRepo.find({ filter: isolatedFilter });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
## HTTP Request Examples
|
|
177
|
+
|
|
178
|
+
**cURL:**
|
|
179
|
+
```bash
|
|
180
|
+
# Simple filter
|
|
181
|
+
curl "http://localhost:3000/products?filter=%7B%22where%22%3A%7B%22status%22%3A%22active%22%7D%2C%22limit%22%3A10%7D"
|
|
182
|
+
|
|
183
|
+
# Decoded filter: {"where":{"status":"active"},"limit":10}
|
|
184
|
+
|
|
185
|
+
# Complex filter with URL encoding
|
|
186
|
+
curl -G "http://localhost:3000/products" \
|
|
187
|
+
--data-urlencode 'filter={"where":{"price":{"gte":100,"lte":500},"tags":{"contains":["featured"]}},"order":["price ASC"],"limit":20}'
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**JavaScript/TypeScript:**
|
|
191
|
+
```typescript
|
|
192
|
+
// Using fetch
|
|
193
|
+
const filter = {
|
|
194
|
+
where: { status: 'active', price: { lte: 100 } },
|
|
195
|
+
order: ['createdAt DESC'],
|
|
196
|
+
limit: 10,
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const response = await fetch(
|
|
200
|
+
`/api/products?filter=${encodeURIComponent(JSON.stringify(filter))}`
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
// Using axios
|
|
204
|
+
const response = await axios.get('/api/products', {
|
|
205
|
+
params: { filter: JSON.stringify(filter) },
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
## Debugging Filters
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
// Enable logging to see generated SQL
|
|
214
|
+
const result = await repo.find({
|
|
215
|
+
filter: complexFilter,
|
|
216
|
+
options: {
|
|
217
|
+
log: { use: true, level: 'debug' },
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// Or use buildQuery to inspect without executing
|
|
222
|
+
const queryOptions = repo.buildQuery({ filter: complexFilter });
|
|
223
|
+
console.log('Generated query options:', queryOptions);
|
|
224
|
+
```
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: PostgreSQL Array Operators
|
|
3
|
+
description: Operators for PostgreSQL array columns
|
|
4
|
+
difficulty: intermediate
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# PostgreSQL Array Operators
|
|
8
|
+
|
|
9
|
+
Operators for PostgreSQL array columns (`varchar[]`, `text[]`, `integer[]`, etc.).
|
|
10
|
+
|
|
11
|
+
| Operator | PostgreSQL | Description |
|
|
12
|
+
|----------|------------|-------------|
|
|
13
|
+
| `contains` | `@>` | Array contains **ALL** specified elements |
|
|
14
|
+
| `containedBy` | `<@` | Array is a **subset** of specified elements |
|
|
15
|
+
| `overlaps` | `&&` | Array shares **ANY** element with specified |
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## contains (@>)
|
|
19
|
+
|
|
20
|
+
Find rows where the array column contains **all** specified elements.
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
// Schema: tags varchar(100)[]
|
|
24
|
+
// Data: Product A has ['electronics', 'featured', 'sale']
|
|
25
|
+
|
|
26
|
+
// Find products with BOTH 'electronics' AND 'featured'
|
|
27
|
+
{ where: { tags: { contains: ['electronics', 'featured'] } } }
|
|
28
|
+
// SQL: "tags"::text[] @> ARRAY['electronics', 'featured']::text[]
|
|
29
|
+
|
|
30
|
+
// Single element
|
|
31
|
+
{ where: { tags: { contains: ['featured'] } } }
|
|
32
|
+
// Matches: ['featured'], ['featured', 'sale'], ['a', 'featured', 'b']
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## containedBy (<@)
|
|
37
|
+
|
|
38
|
+
Find rows where **all** array elements are within the specified set.
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
// Find products where ALL tags are in the allowed list
|
|
42
|
+
{ where: { tags: { containedBy: ['sale', 'featured', 'new', 'popular'] } } }
|
|
43
|
+
// SQL: "tags"::text[] <@ ARRAY['sale', 'featured', 'new', 'popular']::text[]
|
|
44
|
+
|
|
45
|
+
// Product A ['featured', 'sale'] -> matches (all in list)
|
|
46
|
+
// Product B ['featured', 'clearance'] -> no match ('clearance' not in list)
|
|
47
|
+
// Product C [] -> matches (empty is subset of everything)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
## overlaps (&&)
|
|
52
|
+
|
|
53
|
+
Find rows where the arrays share at least one common element.
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// Find products with 'premium' OR 'sale' tag
|
|
57
|
+
{ where: { tags: { overlaps: ['premium', 'sale'] } } }
|
|
58
|
+
// SQL: "tags"::text[] && ARRAY['premium', 'sale']::text[]
|
|
59
|
+
|
|
60
|
+
// Product A ['featured', 'sale'] -> matches (has 'sale')
|
|
61
|
+
// Product B ['premium', 'luxury'] -> matches (has 'premium')
|
|
62
|
+
// Product C ['new', 'featured'] -> no match (no overlap)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
## Visual Comparison
|
|
67
|
+
|
|
68
|
+
| Product | tags | `contains ['featured']` | `containedBy ['a','b','featured']` | `overlaps ['sale','premium']` |
|
|
69
|
+
|---------|------|------------------------|-----------------------------------|------------------------------|
|
|
70
|
+
| A | `['featured', 'sale']` | Yes | No (has 'sale') | Yes (has 'sale') |
|
|
71
|
+
| B | `['featured']` | Yes | Yes | No |
|
|
72
|
+
| C | `['a', 'b']` | No | Yes | No |
|
|
73
|
+
| D | `['premium']` | No | No | Yes (has 'premium') |
|
|
74
|
+
| E | `[]` | No | Yes (empty subset) | No |
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
## Decision Guide
|
|
78
|
+
|
|
79
|
+
| Question | Use |
|
|
80
|
+
|----------|-----|
|
|
81
|
+
| "Must have ALL these tags" | `contains` |
|
|
82
|
+
| "Tags must only be from this list" | `containedBy` |
|
|
83
|
+
| "Must have AT LEAST ONE of these tags" | `overlaps` |
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
## Empty Array Behavior
|
|
87
|
+
|
|
88
|
+
| Operator | Empty Value `[]` | Behavior |
|
|
89
|
+
|----------|------------------|----------|
|
|
90
|
+
| `contains: []` | Returns **ALL** rows | Everything contains empty set |
|
|
91
|
+
| `containedBy: []` | Returns only rows with **empty arrays** | Only `[]` is subset of `[]` |
|
|
92
|
+
| `overlaps: []` | Returns **NO** rows | Nothing overlaps with empty |
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
## Type Handling
|
|
96
|
+
|
|
97
|
+
**String Arrays** (`varchar[]`, `text[]`, `char[]`):
|
|
98
|
+
```typescript
|
|
99
|
+
{ where: { tags: { contains: ['a', 'b'] } } }
|
|
100
|
+
// SQL: "tags"::text[] @> ARRAY['a', 'b']::text[]
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Numeric Arrays** (`integer[]`, `numeric[]`):
|
|
104
|
+
```typescript
|
|
105
|
+
{ where: { scores: { contains: [100, 200] } } }
|
|
106
|
+
// SQL: "scores" @> ARRAY[100, 200]
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Boolean Arrays**:
|
|
110
|
+
```typescript
|
|
111
|
+
{ where: { flags: { contains: [true, false] } } }
|
|
112
|
+
// SQL: "flags" @> ARRAY[true, false]
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
## Defining Array Columns
|
|
117
|
+
|
|
118
|
+
In your Drizzle schema:
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import { pgTable, text, varchar, integer } from 'drizzle-orm/pg-core';
|
|
122
|
+
|
|
123
|
+
export const productTable = pgTable('Product', {
|
|
124
|
+
id: text('id').primaryKey(),
|
|
125
|
+
name: text('name').notNull(),
|
|
126
|
+
|
|
127
|
+
// Array columns
|
|
128
|
+
tags: varchar('tags', { length: 100 }).array(), // varchar(100)[]
|
|
129
|
+
categories: text('categories').array(), // text[]
|
|
130
|
+
scores: integer('scores').array(), // integer[]
|
|
131
|
+
});
|
|
132
|
+
```
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Comparison Operators
|
|
3
|
+
description: Equality and comparison operators for filtering records
|
|
4
|
+
difficulty: intermediate
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Comparison Operators
|
|
8
|
+
|
|
9
|
+
Equality and comparison operators for filtering records.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## eq - Equal To
|
|
13
|
+
|
|
14
|
+
Matches records where field equals the value.
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
// Implicit equality
|
|
18
|
+
{ where: { status: 'active' } }
|
|
19
|
+
|
|
20
|
+
// Explicit form
|
|
21
|
+
{ where: { status: { eq: 'active' } } }
|
|
22
|
+
|
|
23
|
+
// SQL: WHERE "status" = 'active'
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Special Cases:**
|
|
27
|
+
```typescript
|
|
28
|
+
// Null equality
|
|
29
|
+
{ where: { deletedAt: null } }
|
|
30
|
+
{ where: { deletedAt: { eq: null } } }
|
|
31
|
+
// SQL: WHERE "deleted_at" IS NULL
|
|
32
|
+
|
|
33
|
+
// Array shorthand (becomes IN)
|
|
34
|
+
{ where: { id: [1, 2, 3] } }
|
|
35
|
+
// SQL: WHERE "id" IN (1, 2, 3)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
## ne / neq - Not Equal To
|
|
40
|
+
|
|
41
|
+
Matches records where field does NOT equal the value.
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
{ where: { status: { ne: 'deleted' } } }
|
|
45
|
+
{ where: { status: { neq: 'deleted' } } } // Alias
|
|
46
|
+
|
|
47
|
+
// SQL: WHERE "status" != 'deleted'
|
|
48
|
+
|
|
49
|
+
// Null handling
|
|
50
|
+
{ where: { deletedAt: { ne: null } } }
|
|
51
|
+
// SQL: WHERE "deleted_at" IS NOT NULL
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
## gt - Greater Than
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
// Numbers
|
|
59
|
+
{ where: { price: { gt: 100 } } }
|
|
60
|
+
// SQL: WHERE "price" > 100
|
|
61
|
+
|
|
62
|
+
// Dates
|
|
63
|
+
{ where: { createdAt: { gt: new Date('2024-01-01') } } }
|
|
64
|
+
// SQL: WHERE "created_at" > '2024-01-01'
|
|
65
|
+
|
|
66
|
+
// Strings (lexicographic)
|
|
67
|
+
{ where: { name: { gt: 'M' } } }
|
|
68
|
+
// SQL: WHERE "name" > 'M'
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
## gte - Greater Than or Equal
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
{ where: { quantity: { gte: 10 } } }
|
|
76
|
+
// SQL: WHERE "quantity" >= 10
|
|
77
|
+
|
|
78
|
+
// Combined with other operators
|
|
79
|
+
{ where: { age: { gte: 18, lt: 65 } } }
|
|
80
|
+
// SQL: WHERE "age" >= 18 AND "age" < 65
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
## lt - Less Than
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
{ where: { stock: { lt: 5 } } }
|
|
88
|
+
// SQL: WHERE "stock" < 5
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
## lte - Less Than or Equal
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
{ where: { rating: { lte: 3 } } }
|
|
96
|
+
// SQL: WHERE "rating" <= 3
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
## Summary
|
|
101
|
+
|
|
102
|
+
| Operator | SQL | Description |
|
|
103
|
+
|----------|-----|-------------|
|
|
104
|
+
| `eq` | `=` | Equal to |
|
|
105
|
+
| `ne` / `neq` | `!=` | Not equal to |
|
|
106
|
+
| `gt` | `>` | Greater than |
|
|
107
|
+
| `gte` | `>=` | Greater than or equal |
|
|
108
|
+
| `lt` | `<` | Less than |
|
|
109
|
+
| `lte` | `<=` | Less than or equal |
|