bunsane 0.1.0 → 0.1.2
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/.github/workflows/deploy-docs.yml +57 -0
- package/LICENSE.md +1 -1
- package/README.md +2 -28
- package/TODO.md +8 -1
- package/bun.lock +3 -0
- package/config/upload.config.ts +135 -0
- package/core/App.ts +168 -4
- package/core/ArcheType.ts +122 -0
- package/core/BatchLoader.ts +100 -0
- package/core/ComponentRegistry.ts +4 -3
- package/core/Components.ts +2 -2
- package/core/Decorators.ts +15 -8
- package/core/Entity.ts +193 -14
- package/core/EntityCache.ts +15 -0
- package/core/EntityHookManager.ts +855 -0
- package/core/EntityManager.ts +12 -2
- package/core/ErrorHandler.ts +64 -7
- package/core/FileValidator.ts +284 -0
- package/core/Query.ts +503 -85
- package/core/RequestContext.ts +24 -0
- package/core/RequestLoaders.ts +89 -0
- package/core/SchedulerManager.ts +710 -0
- package/core/UploadManager.ts +261 -0
- package/core/components/UploadComponent.ts +206 -0
- package/core/decorators/EntityHooks.ts +190 -0
- package/core/decorators/ScheduledTask.ts +83 -0
- package/core/events/EntityLifecycleEvents.ts +177 -0
- package/core/processors/ImageProcessor.ts +423 -0
- package/core/storage/LocalStorageProvider.ts +290 -0
- package/core/storage/StorageProvider.ts +112 -0
- package/database/DatabaseHelper.ts +183 -58
- package/database/index.ts +5 -5
- package/database/sqlHelpers.ts +7 -0
- package/docs/README.md +149 -0
- package/docs/_coverpage.md +36 -0
- package/docs/_sidebar.md +23 -0
- package/docs/api/core.md +568 -0
- package/docs/api/hooks.md +554 -0
- package/docs/api/index.md +222 -0
- package/docs/api/query.md +678 -0
- package/docs/api/service.md +744 -0
- package/docs/core-concepts/archetypes.md +512 -0
- package/docs/core-concepts/components.md +498 -0
- package/docs/core-concepts/entity.md +314 -0
- package/docs/core-concepts/hooks.md +683 -0
- package/docs/core-concepts/query.md +588 -0
- package/docs/core-concepts/services.md +647 -0
- package/docs/examples/code-examples.md +425 -0
- package/docs/getting-started.md +337 -0
- package/docs/index.html +97 -0
- package/gql/Generator.ts +58 -35
- package/gql/decorators/Upload.ts +176 -0
- package/gql/helpers.ts +67 -0
- package/gql/index.ts +65 -31
- package/gql/types.ts +1 -1
- package/index.ts +79 -11
- package/package.json +19 -10
- package/rest/Generator.ts +3 -0
- package/rest/index.ts +22 -0
- package/service/Service.ts +1 -1
- package/service/ServiceRegistry.ts +10 -6
- package/service/index.ts +12 -1
- package/tests/bench/insert.bench.ts +59 -0
- package/tests/bench/relations.bench.ts +269 -0
- package/tests/bench/sorting.bench.ts +415 -0
- package/tests/component-hooks.test.ts +1409 -0
- package/tests/component.test.ts +338 -0
- package/tests/errorHandling.test.ts +155 -0
- package/tests/hooks.test.ts +666 -0
- package/tests/query-sorting.test.ts +101 -0
- package/tests/relations.test.ts +169 -0
- package/tests/scheduler.test.ts +724 -0
- package/tsconfig.json +35 -34
- package/types/graphql.types.ts +87 -0
- package/types/hooks.types.ts +141 -0
- package/types/scheduler.types.ts +165 -0
- package/types/upload.types.ts +184 -0
- package/upload/index.ts +140 -0
- package/utils/UploadHelper.ts +305 -0
- package/utils/cronParser.ts +366 -0
- package/utils/errorMessages.ts +151 -0
- package/core/Events.ts +0 -0
|
@@ -0,0 +1,678 @@
|
|
|
1
|
+
# Query API Reference
|
|
2
|
+
|
|
3
|
+
This page provides detailed API reference for BunSane's query system and database operations.
|
|
4
|
+
|
|
5
|
+
## 🔍 Query Class
|
|
6
|
+
|
|
7
|
+
The `Query` class provides a fluent interface for building database queries based on Entity-Component-System (ECS) architecture.
|
|
8
|
+
|
|
9
|
+
### Constructor
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
new Query()
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Creates a new query instance. Queries are built by chaining methods to specify which components are required, optional filters, sorting, and pagination.
|
|
16
|
+
|
|
17
|
+
**Returns:** `Query` - New query instance
|
|
18
|
+
|
|
19
|
+
**Example:**
|
|
20
|
+
```typescript
|
|
21
|
+
const query = new Query();
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Static Filter Methods
|
|
25
|
+
|
|
26
|
+
#### `Query.filter(field, operator, value)`
|
|
27
|
+
|
|
28
|
+
Creates a filter object for use with component queries.
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
static filter(field: string, operator: FilterOperator, value: any): QueryFilter
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Parameters:**
|
|
35
|
+
- `field`: String - The component data field name
|
|
36
|
+
- `operator`: FilterOperator - Comparison operator (`=`, `>`, `<`, `>=`, `<=`, `!=`, `LIKE`, `IN`, `NOT IN`)
|
|
37
|
+
- `value`: Any - Value to compare against
|
|
38
|
+
|
|
39
|
+
**Returns:** `QueryFilter` - Filter configuration object
|
|
40
|
+
|
|
41
|
+
**Example:**
|
|
42
|
+
```typescript
|
|
43
|
+
const emailFilter = Query.filter("value", Query.filterOp.EQ, "user@example.com");
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
#### `Query.typedFilter(componentCtor, field, operator, value)`
|
|
47
|
+
|
|
48
|
+
Creates a type-safe filter for a specific component.
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
static typedFilter<T extends BaseComponent>(
|
|
52
|
+
componentCtor: new (...args: any[]) => T,
|
|
53
|
+
field: keyof ComponentDataType<T>,
|
|
54
|
+
operator: FilterOperator,
|
|
55
|
+
value: any
|
|
56
|
+
): QueryFilter
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Type Parameters:**
|
|
60
|
+
- `T`: Component class extending BaseComponent
|
|
61
|
+
|
|
62
|
+
**Parameters:**
|
|
63
|
+
- `componentCtor`: Component constructor
|
|
64
|
+
- `field`: keyof ComponentDataType<T> - Component data field
|
|
65
|
+
- `operator`: FilterOperator - Comparison operator
|
|
66
|
+
- `value`: Any - Value to compare against
|
|
67
|
+
|
|
68
|
+
**Returns:** `QueryFilter` - Filter configuration object
|
|
69
|
+
|
|
70
|
+
**Example:**
|
|
71
|
+
```typescript
|
|
72
|
+
const nameFilter = Query.typedFilter(NameComponent, "value", Query.filterOp.LIKE, "John%");
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
#### `Query.filters(...filters)`
|
|
76
|
+
|
|
77
|
+
Creates filter options from multiple filter objects.
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
static filters(...filters: QueryFilter[]): QueryFilterOptions
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Parameters:**
|
|
84
|
+
- `filters`: QueryFilter[] - Array of filter objects
|
|
85
|
+
|
|
86
|
+
**Returns:** `QueryFilterOptions` - Filter options for component queries
|
|
87
|
+
|
|
88
|
+
**Example:**
|
|
89
|
+
```typescript
|
|
90
|
+
const filterOptions = Query.filters(
|
|
91
|
+
Query.filter("value", Query.filterOp.EQ, "active"),
|
|
92
|
+
Query.filter("value", Query.filterOp.GT, 100)
|
|
93
|
+
);
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Filter Operators
|
|
97
|
+
|
|
98
|
+
The `FilterOp` object provides constants for filter operators:
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
FilterOp.EQ // "="
|
|
102
|
+
FilterOp.GT // ">"
|
|
103
|
+
FilterOp.LT // "<"
|
|
104
|
+
FilterOp.GTE // ">="
|
|
105
|
+
FilterOp.LTE // "<="
|
|
106
|
+
FilterOp.NEQ // "!="
|
|
107
|
+
FilterOp.LIKE // "LIKE"
|
|
108
|
+
FilterOp.IN // "IN"
|
|
109
|
+
FilterOp.NOT_IN // "NOT IN"
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Instance Methods
|
|
113
|
+
|
|
114
|
+
#### `query.with(componentCtor, options?)`
|
|
115
|
+
|
|
116
|
+
Adds a required component to the query. Entities must have this component to be included in results.
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
with<T extends BaseComponent>(
|
|
120
|
+
ctor: new (...args: any[]) => T,
|
|
121
|
+
options?: QueryFilterOptions
|
|
122
|
+
): this
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**Type Parameters:**
|
|
126
|
+
- `T`: Component class extending BaseComponent
|
|
127
|
+
|
|
128
|
+
**Parameters:**
|
|
129
|
+
- `ctor`: Component constructor - The component class that entities must have
|
|
130
|
+
- `options` (optional): QueryFilterOptions - Filters to apply to this component
|
|
131
|
+
|
|
132
|
+
**Returns:** `this` - Query instance for chaining
|
|
133
|
+
|
|
134
|
+
**Example:**
|
|
135
|
+
```typescript
|
|
136
|
+
const users = await new Query()
|
|
137
|
+
.with(UserTag)
|
|
138
|
+
.with(EmailComponent, Query.filters(
|
|
139
|
+
Query.filter("value", Query.filterOp.LIKE, "%@example.com")
|
|
140
|
+
))
|
|
141
|
+
.exec();
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
#### `query.without(componentCtor)`
|
|
145
|
+
|
|
146
|
+
Excludes entities that have the specified component.
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
without<T extends BaseComponent>(ctor: new (...args: any[]) => T): this
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Type Parameters:**
|
|
153
|
+
- `T`: Component class extending BaseComponent
|
|
154
|
+
|
|
155
|
+
**Parameters:**
|
|
156
|
+
- `ctor`: Component constructor - The component that entities must NOT have
|
|
157
|
+
|
|
158
|
+
**Returns:** `this` - Query instance for chaining
|
|
159
|
+
|
|
160
|
+
**Example:**
|
|
161
|
+
```typescript
|
|
162
|
+
const activeUsers = await new Query()
|
|
163
|
+
.with(UserTag)
|
|
164
|
+
.without(BannedComponent)
|
|
165
|
+
.exec();
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
#### `query.eagerLoadComponents(componentCtors)`
|
|
169
|
+
|
|
170
|
+
Eager loads the specified components for all matching entities. This improves performance by batch loading component data.
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
eagerLoadComponents(ctors: Array<new () => BaseComponent>): this
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Parameters:**
|
|
177
|
+
- `ctors`: Array<new () => BaseComponent> - Array of component constructors to eager load
|
|
178
|
+
|
|
179
|
+
**Returns:** `this` - Query instance for chaining
|
|
180
|
+
|
|
181
|
+
**Example:**
|
|
182
|
+
```typescript
|
|
183
|
+
const users = await new Query()
|
|
184
|
+
.with(UserTag)
|
|
185
|
+
.eagerLoadComponents([NameComponent, EmailComponent, PhoneComponent])
|
|
186
|
+
.exec();
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
#### `query.populate()`
|
|
190
|
+
|
|
191
|
+
Fully populates all components for matching entities. This is equivalent to calling `Entity.LoadMultiple()` on the results.
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
populate(): this
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Returns:** `this` - Query instance for chaining
|
|
198
|
+
|
|
199
|
+
**Example:**
|
|
200
|
+
```typescript
|
|
201
|
+
const fullUsers = await new Query()
|
|
202
|
+
.with(UserTag)
|
|
203
|
+
.populate()
|
|
204
|
+
.exec(); // Returns fully loaded Entity objects
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
#### `query.findById(id)`
|
|
208
|
+
|
|
209
|
+
Filters results to only include the entity with the specified ID.
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
findById(id: string): this
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**Parameters:**
|
|
216
|
+
- `id`: String - Entity ID to find
|
|
217
|
+
|
|
218
|
+
**Returns:** `this` - Query instance for chaining
|
|
219
|
+
|
|
220
|
+
**Example:**
|
|
221
|
+
```typescript
|
|
222
|
+
const user = await new Query()
|
|
223
|
+
.with(UserTag)
|
|
224
|
+
.findById("01HXXX...")
|
|
225
|
+
.exec();
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
#### `query.take(limit)`
|
|
229
|
+
|
|
230
|
+
Limits the number of results returned.
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
take(limit: number): this
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**Parameters:**
|
|
237
|
+
- `limit`: Number - Maximum number of entities to return
|
|
238
|
+
|
|
239
|
+
**Returns:** `this` - Query instance for chaining
|
|
240
|
+
|
|
241
|
+
**Example:**
|
|
242
|
+
```typescript
|
|
243
|
+
const firstTenUsers = await new Query()
|
|
244
|
+
.with(UserTag)
|
|
245
|
+
.take(10)
|
|
246
|
+
.exec();
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
#### `query.offset(offset)`
|
|
250
|
+
|
|
251
|
+
Skips the first N results (for pagination).
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
offset(offset: number): this
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
**Parameters:**
|
|
258
|
+
- `offset`: Number - Number of results to skip
|
|
259
|
+
|
|
260
|
+
**Returns:** `this` - Query instance for chaining
|
|
261
|
+
|
|
262
|
+
**Example:**
|
|
263
|
+
```typescript
|
|
264
|
+
const pageTwoUsers = await new Query()
|
|
265
|
+
.with(UserTag)
|
|
266
|
+
.take(10)
|
|
267
|
+
.offset(10) // Skip first 10, get next 10
|
|
268
|
+
.exec();
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
#### `query.sortBy(componentCtor, property, direction?, nullsFirst?)`
|
|
272
|
+
|
|
273
|
+
Sorts results by a component property.
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
sortBy<T extends BaseComponent>(
|
|
277
|
+
componentCtor: new (...args: any[]) => T,
|
|
278
|
+
property: keyof ComponentDataType<T>,
|
|
279
|
+
direction?: SortDirection,
|
|
280
|
+
nullsFirst?: boolean
|
|
281
|
+
): this
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**Type Parameters:**
|
|
285
|
+
- `T`: Component class extending BaseComponent
|
|
286
|
+
|
|
287
|
+
**Parameters:**
|
|
288
|
+
- `componentCtor`: Component constructor - Component to sort by
|
|
289
|
+
- `property`: keyof ComponentDataType<T> - Property name to sort by
|
|
290
|
+
- `direction` (optional): "ASC" | "DESC" - Sort direction (default: "ASC")
|
|
291
|
+
- `nullsFirst` (optional): Boolean - Whether nulls should appear first (default: false)
|
|
292
|
+
|
|
293
|
+
**Returns:** `this` - Query instance for chaining
|
|
294
|
+
|
|
295
|
+
**Example:**
|
|
296
|
+
```typescript
|
|
297
|
+
const sortedUsers = await new Query()
|
|
298
|
+
.with(UserTag)
|
|
299
|
+
.with(NameComponent)
|
|
300
|
+
.sortBy(NameComponent, "value", "ASC")
|
|
301
|
+
.exec();
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
#### `query.orderBy(orders)`
|
|
305
|
+
|
|
306
|
+
Sorts results by multiple criteria using SortOrder objects.
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
orderBy(orders: SortOrder[]): this
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
**Parameters:**
|
|
313
|
+
- `orders`: SortOrder[] - Array of sort specifications
|
|
314
|
+
|
|
315
|
+
**Returns:** `this` - Query instance for chaining
|
|
316
|
+
|
|
317
|
+
**Example:**
|
|
318
|
+
```typescript
|
|
319
|
+
const sortedUsers = await new Query()
|
|
320
|
+
.with(UserTag)
|
|
321
|
+
.with(NameComponent)
|
|
322
|
+
.with(EmailComponent)
|
|
323
|
+
.orderBy([
|
|
324
|
+
{ component: "NameComponent", property: "value", direction: "ASC" },
|
|
325
|
+
{ component: "EmailComponent", property: "value", direction: "DESC" }
|
|
326
|
+
])
|
|
327
|
+
.exec();
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
#### `query.exec()`
|
|
331
|
+
|
|
332
|
+
Executes the query and returns matching entities.
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
async exec(): Promise<Entity[]>
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
**Returns:** `Promise<Entity[]>` - Array of Entity objects matching the query criteria
|
|
339
|
+
|
|
340
|
+
**Example:**
|
|
341
|
+
```typescript
|
|
342
|
+
const users = await new Query()
|
|
343
|
+
.with(UserTag)
|
|
344
|
+
.exec();
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
#### `query.findOneById(id)`
|
|
348
|
+
|
|
349
|
+
Convenience method to find and return a single entity by ID.
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
async findOneById(id: string): Promise<Entity | null>
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
**Parameters:**
|
|
356
|
+
- `id`: String - Entity ID to find
|
|
357
|
+
|
|
358
|
+
**Returns:** `Promise<Entity | null>` - Single entity or null if not found
|
|
359
|
+
|
|
360
|
+
**Example:**
|
|
361
|
+
```typescript
|
|
362
|
+
const user = await new Query()
|
|
363
|
+
.findOneById("01HXXX...");
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
## 📋 QueryCondition & Filter Types
|
|
367
|
+
|
|
368
|
+
### QueryFilter
|
|
369
|
+
|
|
370
|
+
Interface for defining component filters.
|
|
371
|
+
|
|
372
|
+
```typescript
|
|
373
|
+
interface QueryFilter {
|
|
374
|
+
field: string;
|
|
375
|
+
operator: FilterOperator;
|
|
376
|
+
value: any;
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### QueryFilterOptions
|
|
381
|
+
|
|
382
|
+
Interface for filter options.
|
|
383
|
+
|
|
384
|
+
```typescript
|
|
385
|
+
interface QueryFilterOptions {
|
|
386
|
+
filters: QueryFilter[];
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### SortOrder
|
|
391
|
+
|
|
392
|
+
Interface for sort specifications.
|
|
393
|
+
|
|
394
|
+
```typescript
|
|
395
|
+
interface SortOrder {
|
|
396
|
+
component: string;
|
|
397
|
+
property: string;
|
|
398
|
+
direction: SortDirection;
|
|
399
|
+
nullsFirst?: boolean;
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### FilterOperator
|
|
404
|
+
|
|
405
|
+
Supported filter operators:
|
|
406
|
+
|
|
407
|
+
```typescript
|
|
408
|
+
type FilterOperator = "=" | ">" | "<" | ">=" | "<=" | "!=" | "LIKE" | "IN" | "NOT IN";
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
## 🔍 Query Examples
|
|
412
|
+
|
|
413
|
+
### Basic Component Queries
|
|
414
|
+
|
|
415
|
+
#### Find all users
|
|
416
|
+
```typescript
|
|
417
|
+
const users = await new Query()
|
|
418
|
+
.with(UserTag)
|
|
419
|
+
.exec();
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
#### Find users with specific email
|
|
423
|
+
```typescript
|
|
424
|
+
const users = await new Query()
|
|
425
|
+
.with(UserTag)
|
|
426
|
+
.with(EmailComponent, Query.filters(
|
|
427
|
+
Query.filter("value", Query.filterOp.EQ, "john@example.com")
|
|
428
|
+
))
|
|
429
|
+
.exec();
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
#### Find active users (exclude banned)
|
|
433
|
+
```typescript
|
|
434
|
+
const activeUsers = await new Query()
|
|
435
|
+
.with(UserTag)
|
|
436
|
+
.without(BannedComponent)
|
|
437
|
+
.exec();
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### Filtering Examples
|
|
441
|
+
|
|
442
|
+
#### Multiple filters on same component
|
|
443
|
+
```typescript
|
|
444
|
+
const filteredUsers = await new Query()
|
|
445
|
+
.with(UserTag)
|
|
446
|
+
.with(NameComponent, Query.filters(
|
|
447
|
+
Query.filter("value", Query.filterOp.LIKE, "John%")
|
|
448
|
+
))
|
|
449
|
+
.with(EmailComponent, Query.filters(
|
|
450
|
+
Query.filter("value", Query.filterOp.LIKE, "%@example.com")
|
|
451
|
+
))
|
|
452
|
+
.exec();
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
#### Using IN operator
|
|
456
|
+
```typescript
|
|
457
|
+
const admins = await new Query()
|
|
458
|
+
.with(UserTag)
|
|
459
|
+
.with(RoleComponent, Query.filters(
|
|
460
|
+
Query.filter("value", Query.filterOp.IN, ["admin", "moderator"])
|
|
461
|
+
))
|
|
462
|
+
.exec();
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
#### Range queries
|
|
466
|
+
```typescript
|
|
467
|
+
const recentUsers = await new Query()
|
|
468
|
+
.with(UserTag)
|
|
469
|
+
.with(CreatedAtComponent, Query.filters(
|
|
470
|
+
Query.filter("value", Query.filterOp.GTE, new Date('2024-01-01'))
|
|
471
|
+
))
|
|
472
|
+
.exec();
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
### Eager Loading
|
|
476
|
+
|
|
477
|
+
#### Load multiple components efficiently
|
|
478
|
+
```typescript
|
|
479
|
+
const usersWithDetails = await new Query()
|
|
480
|
+
.with(UserTag)
|
|
481
|
+
.eagerLoadComponents([NameComponent, EmailComponent, PhoneComponent])
|
|
482
|
+
.exec();
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
#### Full entity population
|
|
486
|
+
```typescript
|
|
487
|
+
const fullUsers = await new Query()
|
|
488
|
+
.with(UserTag)
|
|
489
|
+
.populate() // Loads all components for each entity
|
|
490
|
+
.exec();
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
### Sorting and Pagination
|
|
494
|
+
|
|
495
|
+
#### Sort by name
|
|
496
|
+
```typescript
|
|
497
|
+
const sortedUsers = await new Query()
|
|
498
|
+
.with(UserTag)
|
|
499
|
+
.with(NameComponent)
|
|
500
|
+
.sortBy(NameComponent, "value", "ASC")
|
|
501
|
+
.exec();
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
#### Multiple sort criteria
|
|
505
|
+
```typescript
|
|
506
|
+
const sortedUsers = await new Query()
|
|
507
|
+
.with(UserTag)
|
|
508
|
+
.with(NameComponent)
|
|
509
|
+
.with(CreatedAtComponent)
|
|
510
|
+
.orderBy([
|
|
511
|
+
{ component: "NameComponent", property: "value", direction: "ASC" },
|
|
512
|
+
{ component: "CreatedAtComponent", property: "value", direction: "DESC" }
|
|
513
|
+
])
|
|
514
|
+
.exec();
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
#### Pagination
|
|
518
|
+
```typescript
|
|
519
|
+
const pageSize = 20;
|
|
520
|
+
const page = 2;
|
|
521
|
+
|
|
522
|
+
const users = await new Query()
|
|
523
|
+
.with(UserTag)
|
|
524
|
+
.take(pageSize)
|
|
525
|
+
.offset((page - 1) * pageSize)
|
|
526
|
+
.exec();
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
### Real-world Usage Patterns
|
|
530
|
+
|
|
531
|
+
#### User Service Query (from UserService.ts)
|
|
532
|
+
```typescript
|
|
533
|
+
const query = new Query()
|
|
534
|
+
.with(UserTag)
|
|
535
|
+
.with(EmailComponent,
|
|
536
|
+
Query.filters(
|
|
537
|
+
Query.filter("value", Query.filterOp.EQ, input.email)
|
|
538
|
+
)
|
|
539
|
+
)
|
|
540
|
+
.exec();
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
#### Post Service Query with Relationships (from PostService.ts)
|
|
544
|
+
```typescript
|
|
545
|
+
const query = new Query()
|
|
546
|
+
.with(PostTag)
|
|
547
|
+
.with(AuthorComponent,
|
|
548
|
+
Query.filters(
|
|
549
|
+
Query.filter("value", Query.filterOp.IN, userIds)
|
|
550
|
+
)
|
|
551
|
+
)
|
|
552
|
+
.eagerLoadComponents(postComponentsToLoad)
|
|
553
|
+
.exec();
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
#### Finding by ID
|
|
557
|
+
```typescript
|
|
558
|
+
const user = await new Query()
|
|
559
|
+
.with(UserTag)
|
|
560
|
+
.findById(userId)
|
|
561
|
+
.exec();
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
## 🔗 Advanced Query Features
|
|
565
|
+
|
|
566
|
+
### Component Archetypes
|
|
567
|
+
|
|
568
|
+
Queries work with component archetypes - groups of components that define entity types:
|
|
569
|
+
|
|
570
|
+
```typescript
|
|
571
|
+
const UserArcheType = new ArcheType([
|
|
572
|
+
UserTag,
|
|
573
|
+
NameComponent,
|
|
574
|
+
EmailComponent,
|
|
575
|
+
PasswordComponent
|
|
576
|
+
]);
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
### Batch Loading Relationships
|
|
580
|
+
|
|
581
|
+
For efficient relationship loading, use batch loaders:
|
|
582
|
+
|
|
583
|
+
```typescript
|
|
584
|
+
// Preload related entities
|
|
585
|
+
context.authors = await BatchLoader.loadRelatedEntitiesBatched(
|
|
586
|
+
posts,
|
|
587
|
+
AuthorComponent,
|
|
588
|
+
Entity.LoadMultiple
|
|
589
|
+
);
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
### Performance Optimization
|
|
593
|
+
|
|
594
|
+
#### Use eager loading for frequently accessed components
|
|
595
|
+
```typescript
|
|
596
|
+
const users = await new Query()
|
|
597
|
+
.with(UserTag)
|
|
598
|
+
.eagerLoadComponents([NameComponent, EmailComponent]) // Batch load
|
|
599
|
+
.exec();
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
#### Filter early to reduce data transfer
|
|
603
|
+
```typescript
|
|
604
|
+
const activeUsers = await new Query()
|
|
605
|
+
.with(UserTag)
|
|
606
|
+
.with(StatusComponent, Query.filters(
|
|
607
|
+
Query.filter("value", Query.filterOp.EQ, "active")
|
|
608
|
+
))
|
|
609
|
+
.exec();
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
#### Use pagination for large result sets
|
|
613
|
+
```typescript
|
|
614
|
+
const users = await new Query()
|
|
615
|
+
.with(UserTag)
|
|
616
|
+
.take(50)
|
|
617
|
+
.offset(0)
|
|
618
|
+
.exec();
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
## 🚀 Performance Optimization
|
|
622
|
+
|
|
623
|
+
### Indexing Strategy
|
|
624
|
+
|
|
625
|
+
Components are automatically indexed by entity_id and type_id. For optimal performance:
|
|
626
|
+
|
|
627
|
+
- Filter on component data fields that are frequently queried
|
|
628
|
+
- Use `eagerLoadComponents()` for components accessed together
|
|
629
|
+
- Prefer `populate()` only when you need all component data
|
|
630
|
+
|
|
631
|
+
### Query Optimization Tips
|
|
632
|
+
|
|
633
|
+
- **Use specific component requirements** - only include components you need with `.with()`
|
|
634
|
+
- **Eager load related components** - use `.eagerLoadComponents()` to batch load component data
|
|
635
|
+
- **Filter at the component level** - apply filters to specific components rather than post-processing
|
|
636
|
+
- **Use pagination** - always use `.take()` and `.offset()` for large datasets
|
|
637
|
+
- **Batch operations** - load related entities in batches using BatchLoader
|
|
638
|
+
|
|
639
|
+
### Execution Time Monitoring
|
|
640
|
+
|
|
641
|
+
```typescript
|
|
642
|
+
const startTime = Date.now();
|
|
643
|
+
|
|
644
|
+
const results = await new Query()
|
|
645
|
+
.with(UserTag)
|
|
646
|
+
.exec();
|
|
647
|
+
|
|
648
|
+
const executionTime = Date.now() - startTime;
|
|
649
|
+
console.log(`Query executed in ${executionTime}ms`);
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
## 📊 Query Statistics
|
|
653
|
+
|
|
654
|
+
### Result Analysis
|
|
655
|
+
|
|
656
|
+
```typescript
|
|
657
|
+
const query = new Query().with(UserTag);
|
|
658
|
+
|
|
659
|
+
const results = await query.take(10).exec();
|
|
660
|
+
console.log(`Found ${results.length} users (limited to 10)`);
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
### Memory Considerations
|
|
664
|
+
|
|
665
|
+
- `exec()` returns lightweight Entity objects by default
|
|
666
|
+
- Use `eagerLoadComponents()` to load component data efficiently
|
|
667
|
+
- Use `populate()` sparingly as it loads all components for each entity
|
|
668
|
+
|
|
669
|
+
## 🔗 Related APIs
|
|
670
|
+
|
|
671
|
+
- **[Entity API](entity.md)** - Entity operations and lifecycle
|
|
672
|
+
- **[Component API](components.md)** - Component management and data access
|
|
673
|
+
- **[Service API](service.md)** - Business logic layer using queries
|
|
674
|
+
- **[BatchLoader API](batch-loader.md)** - Efficient relationship loading
|
|
675
|
+
|
|
676
|
+
---
|
|
677
|
+
|
|
678
|
+
*Need more details? Check the [Service API](service.md) for real-world query usage patterns!* 🚀
|