@venizia/ignis-docs 0.0.3 → 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.
Files changed (131) hide show
  1. package/README.md +1 -1
  2. package/package.json +4 -2
  3. package/wiki/best-practices/api-usage-examples.md +591 -0
  4. package/wiki/best-practices/architectural-patterns.md +415 -0
  5. package/wiki/best-practices/architecture-decisions.md +488 -0
  6. package/wiki/{get-started/best-practices → best-practices}/code-style-standards.md +406 -17
  7. package/wiki/{get-started/best-practices → best-practices}/common-pitfalls.md +109 -4
  8. package/wiki/{get-started/best-practices → best-practices}/contribution-workflow.md +34 -7
  9. package/wiki/best-practices/data-modeling.md +376 -0
  10. package/wiki/best-practices/deployment-strategies.md +698 -0
  11. package/wiki/best-practices/index.md +27 -0
  12. package/wiki/best-practices/performance-optimization.md +196 -0
  13. package/wiki/best-practices/security-guidelines.md +218 -0
  14. package/wiki/{get-started/best-practices → best-practices}/troubleshooting-tips.md +97 -1
  15. package/wiki/changelogs/2025-12-16-initial-architecture.md +1 -1
  16. package/wiki/changelogs/2025-12-16-model-repo-datasource-refactor.md +1 -1
  17. package/wiki/changelogs/2025-12-17-refactor.md +1 -1
  18. package/wiki/changelogs/2025-12-18-performance-optimizations.md +5 -5
  19. package/wiki/changelogs/2025-12-18-repository-validation-security.md +13 -7
  20. package/wiki/changelogs/2025-12-26-nested-relations-and-generics.md +2 -2
  21. package/wiki/changelogs/2025-12-29-dynamic-binding-registration.md +104 -0
  22. package/wiki/changelogs/2025-12-29-snowflake-uid-helper.md +100 -0
  23. package/wiki/changelogs/2025-12-30-repository-enhancements.md +214 -0
  24. package/wiki/changelogs/2025-12-31-json-path-filtering-array-operators.md +214 -0
  25. package/wiki/changelogs/2025-12-31-string-id-custom-generator.md +137 -0
  26. package/wiki/changelogs/2026-01-02-default-filter-and-repository-mixins.md +418 -0
  27. package/wiki/changelogs/index.md +6 -0
  28. package/wiki/changelogs/planned-schema-migrator.md +0 -8
  29. package/wiki/{get-started/core-concepts → guides/core-concepts/application}/bootstrapping.md +18 -5
  30. package/wiki/{get-started/core-concepts/application.md → guides/core-concepts/application/index.md} +47 -104
  31. package/wiki/guides/core-concepts/components-guide.md +509 -0
  32. package/wiki/{get-started → guides}/core-concepts/components.md +24 -17
  33. package/wiki/{get-started → guides}/core-concepts/controllers.md +30 -13
  34. package/wiki/{get-started → guides}/core-concepts/dependency-injection.md +97 -0
  35. package/wiki/guides/core-concepts/persistent/datasources.md +179 -0
  36. package/wiki/guides/core-concepts/persistent/index.md +119 -0
  37. package/wiki/guides/core-concepts/persistent/models.md +241 -0
  38. package/wiki/guides/core-concepts/persistent/repositories.md +219 -0
  39. package/wiki/guides/core-concepts/persistent/transactions.md +170 -0
  40. package/wiki/{get-started → guides}/core-concepts/services.md +26 -3
  41. package/wiki/{get-started → guides/get-started}/5-minute-quickstart.md +59 -14
  42. package/wiki/guides/get-started/philosophy.md +682 -0
  43. package/wiki/guides/get-started/setup.md +157 -0
  44. package/wiki/guides/index.md +89 -0
  45. package/wiki/guides/reference/glossary.md +243 -0
  46. package/wiki/{get-started → guides/reference}/mcp-docs-server.md +0 -10
  47. package/wiki/{get-started → guides/tutorials}/building-a-crud-api.md +134 -132
  48. package/wiki/{get-started/quickstart.md → guides/tutorials/complete-installation.md} +107 -71
  49. package/wiki/guides/tutorials/ecommerce-api.md +1399 -0
  50. package/wiki/guides/tutorials/realtime-chat.md +1261 -0
  51. package/wiki/guides/tutorials/testing.md +723 -0
  52. package/wiki/index.md +176 -37
  53. package/wiki/references/base/application.md +27 -0
  54. package/wiki/references/base/bootstrapping.md +30 -26
  55. package/wiki/references/base/components.md +24 -7
  56. package/wiki/references/base/controllers.md +51 -20
  57. package/wiki/references/base/datasources.md +30 -0
  58. package/wiki/references/base/dependency-injection.md +39 -3
  59. package/wiki/references/base/filter-system/application-usage.md +224 -0
  60. package/wiki/references/base/filter-system/array-operators.md +132 -0
  61. package/wiki/references/base/filter-system/comparison-operators.md +109 -0
  62. package/wiki/references/base/filter-system/default-filter.md +428 -0
  63. package/wiki/references/base/filter-system/fields-order-pagination.md +155 -0
  64. package/wiki/references/base/filter-system/index.md +127 -0
  65. package/wiki/references/base/filter-system/json-filtering.md +197 -0
  66. package/wiki/references/base/filter-system/list-operators.md +71 -0
  67. package/wiki/references/base/filter-system/logical-operators.md +156 -0
  68. package/wiki/references/base/filter-system/null-operators.md +58 -0
  69. package/wiki/references/base/filter-system/pattern-matching.md +108 -0
  70. package/wiki/references/base/filter-system/quick-reference.md +431 -0
  71. package/wiki/references/base/filter-system/range-operators.md +63 -0
  72. package/wiki/references/base/filter-system/tips.md +190 -0
  73. package/wiki/references/base/filter-system/use-cases.md +452 -0
  74. package/wiki/references/base/index.md +90 -0
  75. package/wiki/references/base/middlewares.md +602 -0
  76. package/wiki/references/base/models.md +215 -23
  77. package/wiki/references/base/providers.md +732 -0
  78. package/wiki/references/base/repositories/advanced.md +555 -0
  79. package/wiki/references/base/repositories/index.md +228 -0
  80. package/wiki/references/base/repositories/mixins.md +331 -0
  81. package/wiki/references/base/repositories/relations.md +486 -0
  82. package/wiki/references/base/repositories.md +40 -635
  83. package/wiki/references/base/services.md +28 -4
  84. package/wiki/references/components/authentication.md +22 -2
  85. package/wiki/references/components/health-check.md +12 -0
  86. package/wiki/references/components/index.md +23 -0
  87. package/wiki/references/components/mail.md +687 -0
  88. package/wiki/references/components/request-tracker.md +16 -0
  89. package/wiki/references/components/socket-io.md +18 -0
  90. package/wiki/references/components/static-asset.md +14 -26
  91. package/wiki/references/components/swagger.md +17 -0
  92. package/wiki/references/configuration/environment-variables.md +427 -0
  93. package/wiki/references/configuration/index.md +73 -0
  94. package/wiki/references/helpers/cron.md +14 -0
  95. package/wiki/references/helpers/crypto.md +15 -0
  96. package/wiki/references/helpers/env.md +16 -0
  97. package/wiki/references/helpers/error.md +17 -0
  98. package/wiki/references/helpers/index.md +14 -0
  99. package/wiki/references/helpers/inversion.md +24 -4
  100. package/wiki/references/helpers/logger.md +19 -0
  101. package/wiki/references/helpers/network.md +11 -0
  102. package/wiki/references/helpers/queue.md +19 -0
  103. package/wiki/references/helpers/redis.md +21 -0
  104. package/wiki/references/helpers/socket-io.md +24 -5
  105. package/wiki/references/helpers/storage.md +18 -10
  106. package/wiki/references/helpers/testing.md +18 -0
  107. package/wiki/references/helpers/types.md +16 -0
  108. package/wiki/references/helpers/uid.md +167 -0
  109. package/wiki/references/helpers/worker-thread.md +16 -0
  110. package/wiki/references/index.md +177 -0
  111. package/wiki/references/quick-reference.md +634 -0
  112. package/wiki/references/src-details/boot.md +3 -3
  113. package/wiki/references/src-details/dev-configs.md +0 -4
  114. package/wiki/references/src-details/docs.md +2 -2
  115. package/wiki/references/src-details/index.md +86 -0
  116. package/wiki/references/src-details/inversion.md +1 -6
  117. package/wiki/references/src-details/mcp-server.md +3 -15
  118. package/wiki/references/utilities/index.md +86 -10
  119. package/wiki/references/utilities/jsx.md +577 -0
  120. package/wiki/references/utilities/request.md +0 -2
  121. package/wiki/references/utilities/statuses.md +740 -0
  122. package/wiki/get-started/best-practices/api-usage-examples.md +0 -266
  123. package/wiki/get-started/best-practices/architectural-patterns.md +0 -170
  124. package/wiki/get-started/best-practices/data-modeling.md +0 -177
  125. package/wiki/get-started/best-practices/deployment-strategies.md +0 -121
  126. package/wiki/get-started/best-practices/performance-optimization.md +0 -97
  127. package/wiki/get-started/best-practices/security-guidelines.md +0 -99
  128. package/wiki/get-started/core-concepts/persistent.md +0 -539
  129. package/wiki/get-started/index.md +0 -65
  130. package/wiki/get-started/philosophy.md +0 -296
  131. package/wiki/get-started/prerequisites.md +0 -113
@@ -0,0 +1,190 @@
1
+ ---
2
+ title: Pro Tips & Edge Cases
3
+ description: Advanced tips and common edge cases for filters
4
+ difficulty: intermediate
5
+ ---
6
+
7
+ # Pro Tips & Edge Cases
8
+
9
+ Advanced tips and common edge cases when working with filters.
10
+
11
+
12
+ ## Tip 1: JSON Numeric vs String Comparison
13
+
14
+ ```typescript
15
+ // JSON field contains: { "priority": "3" } (string)
16
+ // This WON'T match numeric comparison!
17
+ { where: { 'metadata.priority': { gt: 2 } } } // NULL due to safe casting
18
+
19
+ // Use string comparison instead
20
+ { where: { 'metadata.priority': { gt: '2' } } } // Lexicographic compare
21
+
22
+ // Or ensure your data stores numbers properly
23
+ { "priority": 3 } // Store as number, not string
24
+ ```
25
+
26
+
27
+ ## Tip 2: Empty Array Handling
28
+
29
+ ```typescript
30
+ // Empty IN -> no results
31
+ { where: { id: { in: [] } } } // SQL: WHERE false
32
+
33
+ // Empty NIN -> all results
34
+ { where: { id: { nin: [] } } } // SQL: WHERE true
35
+
36
+ // Check array length before filtering
37
+ const ids = getUserSelectedIds();
38
+ if (ids.length === 0) {
39
+ return []; // Early return instead of empty IN
40
+ }
41
+ ```
42
+
43
+
44
+ ## Tip 3: Null-Safe JSON Paths
45
+
46
+ ```typescript
47
+ // If JSON field doesn't exist, #>> returns NULL
48
+ // This is safe - no errors, just no matches
49
+ { where: { 'metadata.nonexistent.field': 'value' } }
50
+ // SQL: "metadata" #>> '{nonexistent,field}' = 'value'
51
+ // Result: No rows (NULL != 'value')
52
+ ```
53
+
54
+
55
+ ## Tip 4: Performance with Large IN Arrays
56
+
57
+ ```typescript
58
+ // For very large arrays (1000+ items), consider chunking
59
+ const allIds = getLargeIdList(); // 5000 IDs
60
+
61
+ const chunkSize = 500;
62
+ const results = [];
63
+ for (let i = 0; i < allIds.length; i += chunkSize) {
64
+ const chunk = allIds.slice(i, i + chunkSize);
65
+ const chunkResults = await repo.find({
66
+ filter: { where: { id: { in: chunk } } }
67
+ });
68
+ results.push(...chunkResults);
69
+ }
70
+ ```
71
+
72
+
73
+ ## Tip 5: Order By JSON Fields
74
+
75
+ ```typescript
76
+ // JSON ordering uses #> (preserves type) not #>> (text)
77
+ { order: ['metadata.priority DESC'] }
78
+ // SQL: "metadata" #> '{priority}' DESC
79
+
80
+ // JSONB comparison order:
81
+ // null < boolean < number < string < array < object
82
+ ```
83
+
84
+
85
+ ## Tip 6: Debugging Filters
86
+
87
+ ```typescript
88
+ // Enable logging to see generated SQL
89
+ const result = await repo.find({
90
+ filter: complexFilter,
91
+ options: {
92
+ log: { use: true, level: 'debug' },
93
+ },
94
+ });
95
+
96
+ // Or use buildQuery to inspect without executing
97
+ const queryOptions = repo.buildQuery({ filter: complexFilter });
98
+ console.log('Generated query options:', queryOptions);
99
+ ```
100
+
101
+
102
+ ## Tip 7: NOT IN with NULL Columns
103
+
104
+ ```typescript
105
+ // NOT IN excludes NULL values!
106
+ { where: { status: { nin: ['deleted'] } } }
107
+ // Rows where status IS NULL will NOT be returned
108
+
109
+ // Include NULL values explicitly
110
+ {
111
+ where: {
112
+ or: [
113
+ { status: { nin: ['deleted'] } },
114
+ { status: { is: null } }
115
+ ]
116
+ }
117
+ }
118
+ ```
119
+
120
+
121
+ ## Tip 8: Combining Multiple Array Conditions
122
+
123
+ ```typescript
124
+ await productRepo.find({
125
+ filter: {
126
+ where: {
127
+ // Must have ALL these categories
128
+ categories: { contains: ['electronics', 'portable'] },
129
+ // Tags must be subset of allowed tags
130
+ tags: { containedBy: ['new', 'sale', 'featured', 'popular'] },
131
+ // Must have at least one of these suppliers
132
+ suppliers: { overlaps: ['supplier-a', 'supplier-b'] }
133
+ }
134
+ }
135
+ });
136
+ ```
137
+
138
+
139
+ ## Tip 9: Date Range Queries
140
+
141
+ ```typescript
142
+ // This week's events
143
+ const startOfWeek = new Date();
144
+ startOfWeek.setDate(startOfWeek.getDate() - startOfWeek.getDay());
145
+ const endOfWeek = new Date(startOfWeek);
146
+ endOfWeek.setDate(endOfWeek.getDate() + 6);
147
+
148
+ {
149
+ where: {
150
+ eventDate: { between: [startOfWeek, endOfWeek] }
151
+ }
152
+ }
153
+
154
+ // Last 30 days
155
+ const thirtyDaysAgo = new Date();
156
+ thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
157
+
158
+ {
159
+ where: {
160
+ createdAt: { gte: thirtyDaysAgo }
161
+ }
162
+ }
163
+ ```
164
+
165
+
166
+ ## Tip 10: Reusable Filter Builders
167
+
168
+ ```typescript
169
+ // Create reusable filter builders
170
+ const createActiveFilter = <T extends { status: string; deletedAt: Date | null }>(): TWhere<T> => ({
171
+ status: 'active',
172
+ deletedAt: { is: null },
173
+ } as TWhere<T>);
174
+
175
+ const createPaginationFilter = (page: number, size: number = 20) => ({
176
+ limit: size,
177
+ skip: (page - 1) * size,
178
+ });
179
+
180
+ // Usage
181
+ const products = await productRepo.find({
182
+ filter: {
183
+ where: {
184
+ ...createActiveFilter(),
185
+ category: 'electronics',
186
+ },
187
+ ...createPaginationFilter(3),
188
+ }
189
+ });
190
+ ```
@@ -0,0 +1,452 @@
1
+ ---
2
+ title: Use Case Gallery
3
+ description: Real-world filter examples with corresponding SQL
4
+ difficulty: intermediate
5
+ ---
6
+
7
+ # Use Case Gallery
8
+
9
+ Real-world examples of filter usage with corresponding SQL.
10
+
11
+
12
+ ## E-commerce Product Search
13
+
14
+ ```typescript
15
+ const products = await productRepo.find({
16
+ filter: {
17
+ where: {
18
+ category: 'electronics',
19
+ price: { between: [100, 500] },
20
+ quantity: { gt: 0 },
21
+ status: 'active',
22
+ },
23
+ order: ['rating DESC', 'reviewCount DESC'],
24
+ fields: ['id', 'name', 'price', 'rating', 'imageUrl'],
25
+ limit: 24,
26
+ }
27
+ });
28
+
29
+ // SQL:
30
+ // SELECT "id", "name", "price", "rating", "image_url"
31
+ // FROM "Product"
32
+ // WHERE "category" = 'electronics'
33
+ // AND "price" BETWEEN 100 AND 500
34
+ // AND "quantity" > 0
35
+ // AND "status" = 'active'
36
+ // ORDER BY "rating" DESC, "review_count" DESC
37
+ // LIMIT 24
38
+ ```
39
+
40
+
41
+ ## Admin Dashboard: Recent Users
42
+
43
+ ```typescript
44
+ const thirtyDaysAgo = new Date();
45
+ thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
46
+
47
+ const recentUsers = await userRepo.find({
48
+ filter: {
49
+ where: {
50
+ createdAt: { gte: thirtyDaysAgo },
51
+ status: { nin: ['banned', 'suspended'] },
52
+ emailVerifiedAt: { isn: null },
53
+ },
54
+ order: ['createdAt DESC'],
55
+ fields: ['id', 'email', 'name', 'createdAt', 'status'],
56
+ limit: 50,
57
+ }
58
+ });
59
+
60
+ // SQL:
61
+ // SELECT "id", "email", "name", "created_at", "status"
62
+ // FROM "User"
63
+ // WHERE "created_at" >= '2024-12-01T00:00:00.000Z'
64
+ // AND "status" NOT IN ('banned', 'suspended')
65
+ // AND "email_verified_at" IS NOT NULL
66
+ // ORDER BY "created_at" DESC
67
+ // LIMIT 50
68
+ ```
69
+
70
+
71
+ ## Task Management: Priority Tags
72
+
73
+ ```typescript
74
+ const priorityTasks = await taskRepo.find({
75
+ filter: {
76
+ where: {
77
+ status: { nin: ['completed', 'cancelled'] },
78
+ tags: { overlaps: ['urgent', 'high-priority'] },
79
+ assigneeId: currentUserId,
80
+ },
81
+ order: ['dueDate ASC', 'createdAt ASC'],
82
+ include: [{ relation: 'project' }],
83
+ }
84
+ });
85
+
86
+ // SQL:
87
+ // SELECT "Task".*
88
+ // FROM "Task"
89
+ // WHERE "status" NOT IN ('completed', 'cancelled')
90
+ // AND "tags"::text[] && ARRAY['urgent', 'high-priority']::text[]
91
+ // AND "assignee_id" = 'user-123'
92
+ // ORDER BY "due_date" ASC, "created_at" ASC
93
+ //
94
+ // -- Separate query for relation:
95
+ // SELECT * FROM "Project" WHERE "id" IN (...)
96
+ ```
97
+
98
+
99
+ ## Soft Delete Handling
100
+
101
+ ```typescript
102
+ // Find active records (soft delete pattern)
103
+ const activeRecords = await repo.find({
104
+ filter: {
105
+ where: { deletedAt: { is: null } },
106
+ }
107
+ });
108
+
109
+ // SQL:
110
+ // SELECT * FROM "Record" WHERE "deleted_at" IS NULL
111
+ ```
112
+
113
+ ```typescript
114
+ // Find ONLY soft-deleted records
115
+ const deletedRecords = await repo.find({
116
+ filter: {
117
+ where: { deletedAt: { isn: null } },
118
+ }
119
+ });
120
+
121
+ // SQL:
122
+ // SELECT * FROM "Record" WHERE "deleted_at" IS NOT NULL
123
+ ```
124
+
125
+
126
+ ## Complex Authorization Filter
127
+
128
+ ```typescript
129
+ const getAuthorizedFilter = (user: User): TWhere<TDocumentSchema> => {
130
+ if (user.role === 'admin') {
131
+ return { deletedAt: { is: null } };
132
+ }
133
+
134
+ return {
135
+ deletedAt: { is: null },
136
+ or: [
137
+ { ownerId: user.id },
138
+ { isPublic: true },
139
+ { sharedWithTeams: { overlaps: user.teamIds } },
140
+ { sharedWithUsers: { contains: [user.id] } },
141
+ ],
142
+ };
143
+ };
144
+
145
+ const documents = await documentRepo.find({
146
+ filter: {
147
+ where: getAuthorizedFilter(currentUser),
148
+ order: ['updatedAt DESC'],
149
+ limit: 100,
150
+ },
151
+ });
152
+
153
+ // SQL (for admin):
154
+ // SELECT *
155
+ // FROM "Document"
156
+ // WHERE "deleted_at" IS NULL
157
+ // ORDER BY "updated_at" DESC
158
+ // LIMIT 100
159
+
160
+ // SQL (for regular user):
161
+ // SELECT *
162
+ // FROM "Document"
163
+ // WHERE "deleted_at" IS NULL
164
+ // AND (
165
+ // "owner_id" = 'user-123'
166
+ // OR "is_public" = true
167
+ // OR "shared_with_teams"::text[] && ARRAY['team-1', 'team-2']::text[]
168
+ // OR "shared_with_users"::text[] @> ARRAY['user-123']::text[]
169
+ // )
170
+ // ORDER BY "updated_at" DESC
171
+ // LIMIT 100
172
+ ```
173
+
174
+
175
+ ## Full-Text Search with Metadata
176
+
177
+ ```typescript
178
+ const searchProducts = async (query: string, filters: {
179
+ minRating?: number;
180
+ maxPrice?: number;
181
+ features?: string[];
182
+ }) => {
183
+ const where: TWhere<TProductSchema> = {
184
+ status: 'active',
185
+ deletedAt: { is: null },
186
+ };
187
+
188
+ if (query) {
189
+ where.or = [
190
+ { name: { ilike: `%${query}%` } },
191
+ { description: { ilike: `%${query}%` } },
192
+ { 'metadata.keywords': { ilike: `%${query}%` } },
193
+ ];
194
+ }
195
+
196
+ if (filters.minRating) {
197
+ where.rating = { gte: filters.minRating };
198
+ }
199
+
200
+ if (filters.maxPrice) {
201
+ where.price = { lte: filters.maxPrice };
202
+ }
203
+
204
+ if (filters.features?.length) {
205
+ where['metadata.features'] = { contains: filters.features };
206
+ }
207
+
208
+ return productRepo.find({
209
+ filter: {
210
+ where,
211
+ order: ['rating DESC', 'createdAt DESC'],
212
+ limit: 50,
213
+ },
214
+ });
215
+ };
216
+
217
+ // Example: searchProducts('wireless', { minRating: 4, maxPrice: 200, features: ['bluetooth'] })
218
+ //
219
+ // SQL:
220
+ // SELECT *
221
+ // FROM "Product"
222
+ // WHERE "status" = 'active'
223
+ // AND "deleted_at" IS NULL
224
+ // AND (
225
+ // "name" ILIKE '%wireless%'
226
+ // OR "description" ILIKE '%wireless%'
227
+ // OR "metadata" #>> '{keywords}' ILIKE '%wireless%'
228
+ // )
229
+ // AND "rating" >= 4
230
+ // AND "price" <= 200
231
+ // AND "metadata" #>> '{features}' @> '["bluetooth"]'
232
+ // ORDER BY "rating" DESC, "created_at" DESC
233
+ // LIMIT 50
234
+ ```
235
+
236
+
237
+ ## Massive Filter Example
238
+
239
+ ```typescript
240
+ const massiveFilter: TFilter<TProductSchema> = {
241
+ where: {
242
+ status: 'active',
243
+ deletedAt: { is: null },
244
+ price: { gte: 50, lte: 500 },
245
+ quantity: { gt: 0 },
246
+ tags: { contains: ['electronics', 'portable'] },
247
+ 'metadata.priority': { gte: 3 },
248
+ 'metadata.features.wireless': true,
249
+ or: [
250
+ { rating: { gte: 4.5 } },
251
+ {
252
+ and: [
253
+ { isFeatured: true },
254
+ { 'metadata.promotion.active': true },
255
+ { 'metadata.promotion.discount': { gte: 20 } },
256
+ ]
257
+ },
258
+ {
259
+ createdAt: { gte: new Date('2024-12-01') },
260
+ 'metadata.isNewArrival': true,
261
+ },
262
+ ],
263
+ category: { nin: ['discontinued', 'recalled'] },
264
+ suppliers: { overlaps: ['supplier-a', 'supplier-b'] },
265
+ },
266
+ fields: ['id', 'name', 'price', 'rating', 'tags', 'metadata'],
267
+ order: ['metadata.priority DESC', 'rating DESC', 'createdAt DESC'],
268
+ limit: 20,
269
+ skip: 0,
270
+ include: [
271
+ { relation: 'category' },
272
+ {
273
+ relation: 'reviews',
274
+ scope: {
275
+ where: { rating: { gte: 4 } },
276
+ order: ['createdAt DESC'],
277
+ limit: 5,
278
+ },
279
+ },
280
+ ],
281
+ };
282
+
283
+ const products = await productRepo.find({ filter: massiveFilter });
284
+
285
+ // SQL:
286
+ // SELECT "id", "name", "price", "rating", "tags", "metadata"
287
+ // FROM "Product"
288
+ // WHERE "status" = 'active'
289
+ // AND "deleted_at" IS NULL
290
+ // AND "price" >= 50 AND "price" <= 500
291
+ // AND "quantity" > 0
292
+ // AND "tags"::text[] @> ARRAY['electronics', 'portable']::text[]
293
+ // AND CASE
294
+ // WHEN ("metadata" #>> '{priority}') ~ '^-?[0-9]+(\.[0-9]+)?$'
295
+ // THEN ("metadata" #>> '{priority}')::numeric ELSE NULL
296
+ // END >= 3
297
+ // AND "metadata" #>> '{features,wireless}' = 'true'
298
+ // AND (
299
+ // "rating" >= 4.5
300
+ // OR (
301
+ // "is_featured" = true
302
+ // AND "metadata" #>> '{promotion,active}' = 'true'
303
+ // AND CASE
304
+ // WHEN ("metadata" #>> '{promotion,discount}') ~ '^-?[0-9]+(\.[0-9]+)?$'
305
+ // THEN ("metadata" #>> '{promotion,discount}')::numeric ELSE NULL
306
+ // END >= 20
307
+ // )
308
+ // OR (
309
+ // "created_at" >= '2024-12-01T00:00:00.000Z'
310
+ // AND "metadata" #>> '{isNewArrival}' = 'true'
311
+ // )
312
+ // )
313
+ // AND "category" NOT IN ('discontinued', 'recalled')
314
+ // AND "suppliers"::text[] && ARRAY['supplier-a', 'supplier-b']::text[]
315
+ // ORDER BY "metadata" #> '{priority}' DESC, "rating" DESC, "created_at" DESC
316
+ // LIMIT 20 OFFSET 0
317
+ //
318
+ // -- Separate query for category relation:
319
+ // SELECT * FROM "Category" WHERE "id" IN (...)
320
+ //
321
+ // -- Separate query for reviews relation:
322
+ // SELECT * FROM "Review"
323
+ // WHERE "product_id" IN (...) AND "rating" >= 4
324
+ // ORDER BY "created_at" DESC
325
+ // LIMIT 5
326
+ ```
327
+
328
+
329
+ ## Date Range Queries
330
+
331
+ ```typescript
332
+ // Events this week
333
+ const startOfWeek = new Date('2024-12-29');
334
+ const endOfWeek = new Date('2025-01-04');
335
+
336
+ const weekEvents = await eventRepo.find({
337
+ filter: {
338
+ where: {
339
+ eventDate: { between: [startOfWeek, endOfWeek] }
340
+ },
341
+ order: ['eventDate ASC']
342
+ }
343
+ });
344
+
345
+ // SQL:
346
+ // SELECT *
347
+ // FROM "Event"
348
+ // WHERE "event_date" BETWEEN '2024-12-29' AND '2025-01-04'
349
+ // ORDER BY "event_date" ASC
350
+ ```
351
+
352
+ ```typescript
353
+ // Orders in the last 7 days
354
+ const sevenDaysAgo = new Date();
355
+ sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
356
+
357
+ const recentOrders = await orderRepo.find({
358
+ filter: {
359
+ where: {
360
+ createdAt: { gte: sevenDaysAgo },
361
+ status: { in: ['completed', 'shipped'] },
362
+ total: { gte: 100 }
363
+ },
364
+ order: ['total DESC'],
365
+ limit: 100
366
+ }
367
+ });
368
+
369
+ // SQL:
370
+ // SELECT *
371
+ // FROM "Order"
372
+ // WHERE "created_at" >= '2024-12-24T00:00:00.000Z'
373
+ // AND "status" IN ('completed', 'shipped')
374
+ // AND "total" >= 100
375
+ // ORDER BY "total" DESC
376
+ // LIMIT 100
377
+ ```
378
+
379
+
380
+ ## Multi-Tenant Data Isolation
381
+
382
+ ```typescript
383
+ const getTenantProducts = async (tenantId: string, filter: TFilter<TProductSchema>) => {
384
+ return productRepo.find({
385
+ filter: {
386
+ ...filter,
387
+ where: {
388
+ ...filter.where,
389
+ tenantId, // Always enforce tenant isolation
390
+ deletedAt: { is: null },
391
+ },
392
+ },
393
+ });
394
+ };
395
+
396
+ // Usage
397
+ await getTenantProducts('tenant-abc', {
398
+ where: { category: 'electronics' },
399
+ order: ['createdAt DESC'],
400
+ limit: 20
401
+ });
402
+
403
+ // SQL:
404
+ // SELECT *
405
+ // FROM "Product"
406
+ // WHERE "category" = 'electronics'
407
+ // AND "tenant_id" = 'tenant-abc'
408
+ // AND "deleted_at" IS NULL
409
+ // ORDER BY "created_at" DESC
410
+ // LIMIT 20
411
+ ```
412
+
413
+
414
+ ## Inventory Low Stock Alert
415
+
416
+ ```typescript
417
+ const lowStockProducts = await productRepo.find({
418
+ filter: {
419
+ where: {
420
+ status: 'active',
421
+ quantity: { lte: 10 },
422
+ 'metadata.reorderPoint': { isn: null },
423
+ or: [
424
+ { quantity: { lt: 5 } }, // Critical: below 5
425
+ {
426
+ and: [
427
+ { quantity: { lte: 10 } },
428
+ { 'metadata.fastMoving': true }
429
+ ]
430
+ }
431
+ ]
432
+ },
433
+ order: ['quantity ASC'],
434
+ fields: ['id', 'name', 'quantity', 'metadata']
435
+ }
436
+ });
437
+
438
+ // SQL:
439
+ // SELECT "id", "name", "quantity", "metadata"
440
+ // FROM "Product"
441
+ // WHERE "status" = 'active'
442
+ // AND "quantity" <= 10
443
+ // AND "metadata" #>> '{reorderPoint}' IS NOT NULL
444
+ // AND (
445
+ // "quantity" < 5
446
+ // OR (
447
+ // "quantity" <= 10
448
+ // AND "metadata" #>> '{fastMoving}' = 'true'
449
+ // )
450
+ // )
451
+ // ORDER BY "quantity" ASC
452
+ ```