interaqt 0.3.0 → 0.3.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/agent/.claude/agents/code-generation-handler.md +2 -0
- package/agent/.claude/agents/computation-generation-handler.md +1 -0
- package/agent/.claude/agents/implement-design-handler.md +4 -13
- package/agent/.claude/agents/requirements-analysis-handler.md +46 -14
- package/agent/agentspace/knowledge/generator/api-reference.md +3378 -0
- package/agent/agentspace/knowledge/generator/basic-interaction-generation.md +377 -0
- package/agent/agentspace/knowledge/generator/computation-analysis.md +307 -0
- package/agent/agentspace/knowledge/generator/computation-implementation.md +959 -0
- package/agent/agentspace/knowledge/generator/data-analysis.md +463 -0
- package/agent/agentspace/knowledge/generator/entity-relation-generation.md +395 -0
- package/agent/agentspace/knowledge/generator/permission-implementation.md +460 -0
- package/agent/agentspace/knowledge/generator/permission-test-implementation.md +870 -0
- package/agent/agentspace/knowledge/generator/test-implementation.md +674 -0
- package/agent/agentspace/knowledge/usage/00-mindset-shift.md +322 -0
- package/agent/agentspace/knowledge/usage/01-core-concepts.md +131 -0
- package/agent/agentspace/knowledge/usage/02-define-entities-properties.md +407 -0
- package/agent/agentspace/knowledge/usage/03-entity-relations.md +599 -0
- package/agent/agentspace/knowledge/usage/04-reactive-computations.md +2186 -0
- package/agent/agentspace/knowledge/usage/05-interactions.md +1411 -0
- package/agent/agentspace/knowledge/usage/06-attributive-permissions.md +10 -0
- package/agent/agentspace/knowledge/usage/07-payload-parameters.md +593 -0
- package/agent/agentspace/knowledge/usage/08-activities.md +863 -0
- package/agent/agentspace/knowledge/usage/09-filtered-entities.md +784 -0
- package/agent/agentspace/knowledge/usage/10-async-computations.md +734 -0
- package/agent/agentspace/knowledge/usage/11-global-dictionaries.md +942 -0
- package/agent/agentspace/knowledge/usage/12-data-querying.md +1033 -0
- package/agent/agentspace/knowledge/usage/13-testing.md +1201 -0
- package/agent/agentspace/knowledge/usage/14-api-reference.md +1606 -0
- package/agent/agentspace/knowledge/usage/15-entity-crud-patterns.md +1122 -0
- package/agent/agentspace/knowledge/usage/16-frontend-page-design-guide.md +485 -0
- package/agent/agentspace/knowledge/usage/17-performance-optimization.md +283 -0
- package/agent/agentspace/knowledge/usage/18-api-exports-reference.md +176 -0
- package/agent/agentspace/knowledge/usage/19-common-anti-patterns.md +563 -0
- package/agent/agentspace/knowledge/usage/README.md +148 -0
- package/package.json +1 -1
|
@@ -0,0 +1,1033 @@
|
|
|
1
|
+
# 11. How to Handle Data Querying
|
|
2
|
+
|
|
3
|
+
Data querying is one of the core functionalities in the interaqt framework, providing powerful and flexible data retrieval capabilities. The framework supports advanced features such as complex query conditions, relational queries, and deep queries, enabling developers to efficiently obtain the required data.
|
|
4
|
+
|
|
5
|
+
## 11.1 Basic Query Operations
|
|
6
|
+
|
|
7
|
+
### 11.1.1 findOne - Query Single Record
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { MatchExp } from 'interaqt';
|
|
11
|
+
|
|
12
|
+
// Basic single record query
|
|
13
|
+
const user = await system.storage.findOne(
|
|
14
|
+
'User', // Entity name
|
|
15
|
+
MatchExp.atom({ key: 'id', value: ['=', 123] }), // Query condition
|
|
16
|
+
{}, // Modifier (optional)
|
|
17
|
+
['name', 'email', 'age'] // Attribute query (optional)
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
// Query by username
|
|
21
|
+
const userByName = await system.storage.findOne(
|
|
22
|
+
'User',
|
|
23
|
+
MatchExp.atom({ key: 'username', value: ['=', 'alice'] }),
|
|
24
|
+
{},
|
|
25
|
+
['id', 'username', 'email', 'isActive']
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
// Query by email (case insensitive)
|
|
29
|
+
const userByEmail = await system.storage.findOne(
|
|
30
|
+
'User',
|
|
31
|
+
MatchExp.atom({ key: 'email', value: ['like', '%@example.com'] }),
|
|
32
|
+
{},
|
|
33
|
+
['*'] // Query all attributes
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
console.log('Found user:', user);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 11.1.2 find - Query Multiple Records
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
// Query all active users
|
|
43
|
+
const activeUsers = await system.storage.find(
|
|
44
|
+
'User',
|
|
45
|
+
MatchExp.atom({ key: 'isActive', value: ['=', true] }),
|
|
46
|
+
{},
|
|
47
|
+
['id', 'username', 'email']
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
// Query users older than 18
|
|
51
|
+
const adultUsers = await system.storage.find(
|
|
52
|
+
'User',
|
|
53
|
+
MatchExp.atom({ key: 'age', value: ['>', 18] }),
|
|
54
|
+
{},
|
|
55
|
+
['id', 'username', 'age']
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// Query all users (no conditions)
|
|
59
|
+
const allUsers = await system.storage.find(
|
|
60
|
+
'User',
|
|
61
|
+
undefined, // No query conditions
|
|
62
|
+
{},
|
|
63
|
+
['id', 'username', 'createdAt']
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
console.log(`Found ${activeUsers.length} active users`);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 11.1.3 Building Conditions with MatchExp
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
// Create basic match condition
|
|
73
|
+
const basicMatch = MatchExp.atom({
|
|
74
|
+
key: 'status',
|
|
75
|
+
value: ['=', 'active']
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Create match condition from object
|
|
79
|
+
const objectMatch = MatchExp.fromObject({
|
|
80
|
+
isActive: true,
|
|
81
|
+
role: 'admin'
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Using factory method
|
|
85
|
+
const factoryMatch = system.storage.createMatchFromAtom({
|
|
86
|
+
key: 'createdAt',
|
|
87
|
+
value: ['>', '2023-01-01']
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Chain complex conditions
|
|
91
|
+
const complexMatch = MatchExp.atom({ key: 'age', value: ['>=', 18] })
|
|
92
|
+
.and({ key: 'isActive', value: ['=', true] })
|
|
93
|
+
.and({ key: 'role', value: ['in', ['user', 'admin']] });
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## 11.2 Complex Query Conditions
|
|
97
|
+
|
|
98
|
+
### 11.2.1 Comparison Operators
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
// Equals
|
|
102
|
+
const equalMatch = MatchExp.atom({ key: 'status', value: ['=', 'active'] });
|
|
103
|
+
|
|
104
|
+
// Not equals
|
|
105
|
+
const notEqualMatch = MatchExp.atom({ key: 'status', value: ['!=', 'deleted'] });
|
|
106
|
+
|
|
107
|
+
// Greater than
|
|
108
|
+
const greaterMatch = MatchExp.atom({ key: 'age', value: ['>', 18] });
|
|
109
|
+
|
|
110
|
+
// Greater than or equal
|
|
111
|
+
const greaterEqualMatch = MatchExp.atom({ key: 'score', value: ['>=', 80] });
|
|
112
|
+
|
|
113
|
+
// Less than
|
|
114
|
+
const lessMatch = MatchExp.atom({ key: 'price', value: ['<', 100] });
|
|
115
|
+
|
|
116
|
+
// Less than or equal
|
|
117
|
+
const lessEqualMatch = MatchExp.atom({ key: 'discount', value: ['<=', 0.5] });
|
|
118
|
+
|
|
119
|
+
// Like pattern matching
|
|
120
|
+
const likeMatch = MatchExp.atom({ key: 'email', value: ['like', '%@gmail.com'] });
|
|
121
|
+
|
|
122
|
+
// Range query
|
|
123
|
+
const betweenMatch = MatchExp.atom({
|
|
124
|
+
key: 'createdAt',
|
|
125
|
+
value: ['between', ['2023-01-01', '2023-12-31']]
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// IN query
|
|
129
|
+
const inMatch = MatchExp.atom({
|
|
130
|
+
key: 'category',
|
|
131
|
+
value: ['in', ['electronics', 'books', 'clothing']]
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Not null check
|
|
135
|
+
const notNullMatch = MatchExp.atom({ key: 'email', value: ['not', null] });
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 11.2.2 Logical Combinations (AND/OR)
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
// AND logical combination
|
|
142
|
+
const andCondition = MatchExp.atom({ key: 'age', value: ['>=', 18] })
|
|
143
|
+
.and({ key: 'isActive', value: ['=', true] })
|
|
144
|
+
.and({ key: 'role', value: ['!=', 'guest'] });
|
|
145
|
+
|
|
146
|
+
const adultActiveUsers = await system.storage.find(
|
|
147
|
+
'User',
|
|
148
|
+
andCondition,
|
|
149
|
+
{},
|
|
150
|
+
['id', 'username', 'age', 'role']
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
// OR logical combination (implemented through multiple queries)
|
|
154
|
+
const adminUsers = await system.storage.find(
|
|
155
|
+
'User',
|
|
156
|
+
MatchExp.atom({ key: 'role', value: ['=', 'admin'] }),
|
|
157
|
+
{},
|
|
158
|
+
['id', 'username', 'role']
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
const moderatorUsers = await system.storage.find(
|
|
162
|
+
'User',
|
|
163
|
+
MatchExp.atom({ key: 'role', value: ['=', 'moderator'] }),
|
|
164
|
+
{},
|
|
165
|
+
['id', 'username', 'role']
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
// Merge results
|
|
169
|
+
const privilegedUsers = [...adminUsers, ...moderatorUsers];
|
|
170
|
+
|
|
171
|
+
// Complex nested conditions
|
|
172
|
+
const complexCondition = MatchExp.atom({ key: 'isActive', value: ['=', true] })
|
|
173
|
+
.and(
|
|
174
|
+
MatchExp.atom({ key: 'age', value: ['>=', 18] })
|
|
175
|
+
.and({ key: 'age', value: ['<=', 65] })
|
|
176
|
+
)
|
|
177
|
+
.and({ key: 'email', value: ['not', null] });
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### 11.2.3 Relational Queries
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
// Query through related entity properties
|
|
184
|
+
const usersWithGmailProfile = await system.storage.find(
|
|
185
|
+
'User',
|
|
186
|
+
MatchExp.atom({ key: 'profile.email', value: ['like', '%@gmail.com'] }),
|
|
187
|
+
{},
|
|
188
|
+
['id', 'username', ['profile', { attributeQuery: ['email', 'firstName'] }]]
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
// Query through multi-level related entity properties
|
|
192
|
+
const usersWithSpecificProfileTitle = await system.storage.find(
|
|
193
|
+
'User',
|
|
194
|
+
MatchExp.atom({ key: 'profile.settings.title', value: ['=', 'VIP'] }),
|
|
195
|
+
{},
|
|
196
|
+
['id', 'username']
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
// Query users with specific tags
|
|
200
|
+
const usersWithTags = await system.storage.find(
|
|
201
|
+
'User',
|
|
202
|
+
MatchExp.atom({ key: 'tags.name', value: ['=', 'premium'] }),
|
|
203
|
+
{},
|
|
204
|
+
['id', 'username', ['tags', { attributeQuery: ['name', 'category'] }]]
|
|
205
|
+
);
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### 11.2.4 Nested Conditions
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
// Existence query (EXIST)
|
|
212
|
+
const usersWithPosts = await system.storage.find(
|
|
213
|
+
'User',
|
|
214
|
+
MatchExp.atom({
|
|
215
|
+
key: 'posts',
|
|
216
|
+
value: ['exist', { key: 'status', value: ['=', 'published'] }]
|
|
217
|
+
}),
|
|
218
|
+
{},
|
|
219
|
+
['id', 'username']
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
// Complex existence query
|
|
223
|
+
const usersWithRecentPosts = await system.storage.find(
|
|
224
|
+
'User',
|
|
225
|
+
MatchExp.atom({
|
|
226
|
+
key: 'posts',
|
|
227
|
+
value: ['exist', MatchExp.atom({ key: 'publishedAt', value: ['>', '2023-01-01'] })
|
|
228
|
+
.and({ key: 'status', value: ['=', 'published'] })]
|
|
229
|
+
}),
|
|
230
|
+
{},
|
|
231
|
+
['id', 'username', 'email']
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
// Reference value query (compare different fields in same record)
|
|
235
|
+
const usersWithHighScore = await system.storage.find(
|
|
236
|
+
'User',
|
|
237
|
+
MatchExp.atom({
|
|
238
|
+
key: 'currentScore',
|
|
239
|
+
value: ['>', 'bestScore'],
|
|
240
|
+
isReferenceValue: true
|
|
241
|
+
}),
|
|
242
|
+
{},
|
|
243
|
+
['id', 'username', 'currentScore', 'bestScore']
|
|
244
|
+
);
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## 11.3 Modifiers and Sorting
|
|
248
|
+
|
|
249
|
+
### 11.3.1 Pagination
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
// Basic pagination
|
|
253
|
+
const firstPage = await system.storage.find(
|
|
254
|
+
'User',
|
|
255
|
+
MatchExp.atom({ key: 'isActive', value: ['=', true] }),
|
|
256
|
+
{
|
|
257
|
+
limit: 10, // 10 records per page
|
|
258
|
+
offset: 0 // Start from record 0
|
|
259
|
+
},
|
|
260
|
+
['id', 'username', 'email']
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
const secondPage = await system.storage.find(
|
|
264
|
+
'User',
|
|
265
|
+
MatchExp.atom({ key: 'isActive', value: ['=', true] }),
|
|
266
|
+
{
|
|
267
|
+
limit: 10,
|
|
268
|
+
offset: 10 // Start from record 10
|
|
269
|
+
},
|
|
270
|
+
['id', 'username', 'email']
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
// Pagination utility function
|
|
274
|
+
async function getPaginatedUsers(page: number, pageSize: number = 10) {
|
|
275
|
+
const offset = (page - 1) * pageSize;
|
|
276
|
+
|
|
277
|
+
const users = await system.storage.find(
|
|
278
|
+
'User',
|
|
279
|
+
undefined,
|
|
280
|
+
{ limit: pageSize, offset },
|
|
281
|
+
['id', 'username', 'email', 'createdAt']
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
// Get total count (requires separate query)
|
|
285
|
+
const totalUsers = await system.storage.find(
|
|
286
|
+
'User',
|
|
287
|
+
undefined,
|
|
288
|
+
{},
|
|
289
|
+
['id'] // Only query ID for performance
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
return {
|
|
293
|
+
data: users,
|
|
294
|
+
pagination: {
|
|
295
|
+
page,
|
|
296
|
+
pageSize,
|
|
297
|
+
total: totalUsers.length,
|
|
298
|
+
totalPages: Math.ceil(totalUsers.length / pageSize)
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Use pagination query
|
|
304
|
+
const result = await getPaginatedUsers(1, 20);
|
|
305
|
+
console.log(`Page 1 of ${result.pagination.totalPages}, showing ${result.data.length} users`);
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### 11.3.2 Sorting
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
// Single field sorting
|
|
312
|
+
const usersSortedByAge = await system.storage.find(
|
|
313
|
+
'User',
|
|
314
|
+
undefined,
|
|
315
|
+
{ orderBy: { age: 'ASC' } },
|
|
316
|
+
['id', 'username', 'age']
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
const usersSortedByNameDesc = await system.storage.find(
|
|
320
|
+
'User',
|
|
321
|
+
undefined,
|
|
322
|
+
{ orderBy: { username: 'DESC' } },
|
|
323
|
+
['id', 'username', 'createdAt']
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
// Multi-field sorting
|
|
327
|
+
const usersSortedMultiple = await system.storage.find(
|
|
328
|
+
'User',
|
|
329
|
+
undefined,
|
|
330
|
+
{
|
|
331
|
+
orderBy: {
|
|
332
|
+
isActive: 'DESC', // First by active status descending
|
|
333
|
+
age: 'ASC', // Then by age ascending
|
|
334
|
+
username: 'ASC' // Finally by username ascending
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
['id', 'username', 'age', 'isActive']
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
// Combine pagination and sorting
|
|
341
|
+
const topUsers = await system.storage.find(
|
|
342
|
+
'User',
|
|
343
|
+
MatchExp.atom({ key: 'score', value: ['>', 0] }),
|
|
344
|
+
{
|
|
345
|
+
orderBy: { score: 'DESC' },
|
|
346
|
+
limit: 10,
|
|
347
|
+
offset: 0
|
|
348
|
+
},
|
|
349
|
+
['id', 'username', 'score']
|
|
350
|
+
);
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### 11.3.3 Combined Modifiers
|
|
354
|
+
|
|
355
|
+
```typescript
|
|
356
|
+
// Complete query example
|
|
357
|
+
async function searchUsers(criteria: {
|
|
358
|
+
keyword?: string;
|
|
359
|
+
minAge?: number;
|
|
360
|
+
maxAge?: number;
|
|
361
|
+
isActive?: boolean;
|
|
362
|
+
roles?: string[];
|
|
363
|
+
page?: number;
|
|
364
|
+
pageSize?: number;
|
|
365
|
+
sortBy?: string;
|
|
366
|
+
sortOrder?: 'ASC' | 'DESC';
|
|
367
|
+
}) {
|
|
368
|
+
// Build query conditions
|
|
369
|
+
let matchCondition = undefined;
|
|
370
|
+
|
|
371
|
+
if (criteria.keyword) {
|
|
372
|
+
matchCondition = MatchExp.atom({
|
|
373
|
+
key: 'username',
|
|
374
|
+
value: ['like', `%${criteria.keyword}%`]
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (criteria.minAge !== undefined) {
|
|
379
|
+
const ageCondition = MatchExp.atom({ key: 'age', value: ['>=', criteria.minAge] });
|
|
380
|
+
matchCondition = matchCondition ? matchCondition.and(ageCondition) : ageCondition;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (criteria.maxAge !== undefined) {
|
|
384
|
+
const ageCondition = MatchExp.atom({ key: 'age', value: ['<=', criteria.maxAge] });
|
|
385
|
+
matchCondition = matchCondition ? matchCondition.and(ageCondition) : ageCondition;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (criteria.isActive !== undefined) {
|
|
389
|
+
const activeCondition = MatchExp.atom({ key: 'isActive', value: ['=', criteria.isActive] });
|
|
390
|
+
matchCondition = matchCondition ? matchCondition.and(activeCondition) : activeCondition;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (criteria.roles && criteria.roles.length > 0) {
|
|
394
|
+
const roleCondition = MatchExp.atom({ key: 'role', value: ['in', criteria.roles] });
|
|
395
|
+
matchCondition = matchCondition ? matchCondition.and(roleCondition) : roleCondition;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Build modifiers
|
|
399
|
+
const modifier: any = {};
|
|
400
|
+
|
|
401
|
+
if (criteria.sortBy) {
|
|
402
|
+
modifier.orderBy = { [criteria.sortBy]: criteria.sortOrder || 'ASC' };
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (criteria.page && criteria.pageSize) {
|
|
406
|
+
modifier.limit = criteria.pageSize;
|
|
407
|
+
modifier.offset = (criteria.page - 1) * criteria.pageSize;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Execute query
|
|
411
|
+
const users = await system.storage.find(
|
|
412
|
+
'User',
|
|
413
|
+
matchCondition,
|
|
414
|
+
modifier,
|
|
415
|
+
['id', 'username', 'email', 'age', 'role', 'isActive', 'createdAt']
|
|
416
|
+
);
|
|
417
|
+
|
|
418
|
+
return users;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Use search function
|
|
422
|
+
const searchResults = await searchUsers({
|
|
423
|
+
keyword: 'john',
|
|
424
|
+
minAge: 18,
|
|
425
|
+
maxAge: 65,
|
|
426
|
+
isActive: true,
|
|
427
|
+
roles: ['user', 'admin'],
|
|
428
|
+
page: 1,
|
|
429
|
+
pageSize: 20,
|
|
430
|
+
sortBy: 'createdAt',
|
|
431
|
+
sortOrder: 'DESC'
|
|
432
|
+
});
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
## 11.4 Attribute Queries
|
|
436
|
+
|
|
437
|
+
### 11.4.1 Basic Attribute Selection
|
|
438
|
+
|
|
439
|
+
```typescript
|
|
440
|
+
// Select specific attributes
|
|
441
|
+
const basicUsers = await system.storage.find(
|
|
442
|
+
'User',
|
|
443
|
+
undefined,
|
|
444
|
+
{},
|
|
445
|
+
['id', 'username', 'email'] // Only return these attributes
|
|
446
|
+
);
|
|
447
|
+
|
|
448
|
+
// Query all attributes
|
|
449
|
+
const fullUsers = await system.storage.find(
|
|
450
|
+
'User',
|
|
451
|
+
undefined,
|
|
452
|
+
{},
|
|
453
|
+
['*'] // Return all attributes
|
|
454
|
+
);
|
|
455
|
+
|
|
456
|
+
// Exclude sensitive attributes
|
|
457
|
+
const publicUsers = await system.storage.find(
|
|
458
|
+
'User',
|
|
459
|
+
undefined,
|
|
460
|
+
{},
|
|
461
|
+
['id', 'username', 'avatar', 'createdAt'] // Don't include email, password etc.
|
|
462
|
+
);
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### 11.4.2 Nested Attribute Selection
|
|
466
|
+
|
|
467
|
+
```typescript
|
|
468
|
+
// Query users with their profiles
|
|
469
|
+
const usersWithProfiles = await system.storage.find(
|
|
470
|
+
'User',
|
|
471
|
+
undefined,
|
|
472
|
+
{},
|
|
473
|
+
[
|
|
474
|
+
'id',
|
|
475
|
+
'username',
|
|
476
|
+
['profile', {
|
|
477
|
+
attributeQuery: ['firstName', 'lastName', 'avatar']
|
|
478
|
+
}]
|
|
479
|
+
]
|
|
480
|
+
);
|
|
481
|
+
|
|
482
|
+
// Multi-level nested queries
|
|
483
|
+
const usersWithDetailedInfo = await system.storage.find(
|
|
484
|
+
'User',
|
|
485
|
+
undefined,
|
|
486
|
+
{},
|
|
487
|
+
[
|
|
488
|
+
'id',
|
|
489
|
+
'username',
|
|
490
|
+
'email',
|
|
491
|
+
['profile', {
|
|
492
|
+
attributeQuery: [
|
|
493
|
+
'firstName',
|
|
494
|
+
'lastName',
|
|
495
|
+
['settings', {
|
|
496
|
+
attributeQuery: ['theme', 'language', 'notifications']
|
|
497
|
+
}]
|
|
498
|
+
]
|
|
499
|
+
}],
|
|
500
|
+
['posts', {
|
|
501
|
+
attributeQuery: ['id', 'title', 'publishedAt']
|
|
502
|
+
}]
|
|
503
|
+
]
|
|
504
|
+
);
|
|
505
|
+
|
|
506
|
+
// Conditional nested queries
|
|
507
|
+
const usersWithPublishedPosts = await system.storage.find(
|
|
508
|
+
'User',
|
|
509
|
+
undefined,
|
|
510
|
+
{},
|
|
511
|
+
[
|
|
512
|
+
'id',
|
|
513
|
+
'username',
|
|
514
|
+
['posts', {
|
|
515
|
+
attributeQuery: ['id', 'title', 'content', 'publishedAt'],
|
|
516
|
+
matchExpression: MatchExp.atom({ key: 'status', value: ['=', 'published'] })
|
|
517
|
+
}]
|
|
518
|
+
]
|
|
519
|
+
);
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
### 11.4.3 Relationship Attribute Queries
|
|
523
|
+
|
|
524
|
+
```typescript
|
|
525
|
+
// Query user friendship relations
|
|
526
|
+
const usersWithFriends = await system.storage.find(
|
|
527
|
+
'User',
|
|
528
|
+
undefined,
|
|
529
|
+
{},
|
|
530
|
+
[
|
|
531
|
+
'id',
|
|
532
|
+
'username',
|
|
533
|
+
['friends', {
|
|
534
|
+
attributeQuery: [
|
|
535
|
+
'id',
|
|
536
|
+
'username',
|
|
537
|
+
'avatar',
|
|
538
|
+
['&', { attributeQuery: ['since', 'closeness'] }] // Relationship attributes
|
|
539
|
+
]
|
|
540
|
+
}]
|
|
541
|
+
]
|
|
542
|
+
);
|
|
543
|
+
|
|
544
|
+
// Query orders with items
|
|
545
|
+
const ordersWithItems = await system.storage.find(
|
|
546
|
+
'Order',
|
|
547
|
+
undefined,
|
|
548
|
+
{},
|
|
549
|
+
[
|
|
550
|
+
'id',
|
|
551
|
+
'orderNumber',
|
|
552
|
+
'totalAmount',
|
|
553
|
+
'status',
|
|
554
|
+
['items', {
|
|
555
|
+
attributeQuery: [
|
|
556
|
+
['product', { attributeQuery: ['name', 'price', 'category'] }],
|
|
557
|
+
['&', { attributeQuery: ['quantity', 'unitPrice', 'discount'] }] // Order item attributes
|
|
558
|
+
]
|
|
559
|
+
}]
|
|
560
|
+
]
|
|
561
|
+
);
|
|
562
|
+
|
|
563
|
+
// Complex relationship queries
|
|
564
|
+
const teamProjectInfo = await system.storage.find(
|
|
565
|
+
'Team',
|
|
566
|
+
undefined,
|
|
567
|
+
{},
|
|
568
|
+
[
|
|
569
|
+
'id',
|
|
570
|
+
'name',
|
|
571
|
+
'description',
|
|
572
|
+
['members', {
|
|
573
|
+
attributeQuery: [
|
|
574
|
+
'id',
|
|
575
|
+
'username',
|
|
576
|
+
'email',
|
|
577
|
+
['&', { attributeQuery: ['role', 'joinedAt'] }], // Team member relationship attributes
|
|
578
|
+
['profile', { attributeQuery: ['firstName', 'lastName'] }] // Member profile
|
|
579
|
+
]
|
|
580
|
+
}],
|
|
581
|
+
['projects', {
|
|
582
|
+
attributeQuery: [
|
|
583
|
+
'id',
|
|
584
|
+
'name',
|
|
585
|
+
'status',
|
|
586
|
+
'deadline',
|
|
587
|
+
['&', { attributeQuery: ['priority', 'assignedAt'] }] // Project assignment relationship attributes
|
|
588
|
+
]
|
|
589
|
+
}]
|
|
590
|
+
]
|
|
591
|
+
);
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
## 11.5 Querying Relationship Data
|
|
595
|
+
|
|
596
|
+
### 11.5.1 Preloading Related Data
|
|
597
|
+
|
|
598
|
+
```typescript
|
|
599
|
+
// One-to-one relationship preloading
|
|
600
|
+
const usersWithProfiles = await system.storage.find(
|
|
601
|
+
'User',
|
|
602
|
+
MatchExp.atom({ key: 'isActive', value: ['=', true] }),
|
|
603
|
+
{},
|
|
604
|
+
[
|
|
605
|
+
'id',
|
|
606
|
+
'username',
|
|
607
|
+
'email',
|
|
608
|
+
['profile', {
|
|
609
|
+
attributeQuery: ['firstName', 'lastName', 'bio', 'avatar']
|
|
610
|
+
}]
|
|
611
|
+
]
|
|
612
|
+
);
|
|
613
|
+
|
|
614
|
+
// One-to-many relationship preloading
|
|
615
|
+
const usersWithPosts = await system.storage.find(
|
|
616
|
+
'User',
|
|
617
|
+
undefined,
|
|
618
|
+
{ limit: 10 },
|
|
619
|
+
[
|
|
620
|
+
'id',
|
|
621
|
+
'username',
|
|
622
|
+
['posts', {
|
|
623
|
+
attributeQuery: ['id', 'title', 'excerpt', 'publishedAt', 'status'],
|
|
624
|
+
// Can filter related data
|
|
625
|
+
matchExpression: MatchExp.atom({ key: 'status', value: ['=', 'published'] })
|
|
626
|
+
}]
|
|
627
|
+
]
|
|
628
|
+
);
|
|
629
|
+
|
|
630
|
+
// Many-to-many relationship preloading
|
|
631
|
+
const usersWithTeams = await system.storage.find(
|
|
632
|
+
'User',
|
|
633
|
+
undefined,
|
|
634
|
+
{},
|
|
635
|
+
[
|
|
636
|
+
'id',
|
|
637
|
+
'username',
|
|
638
|
+
['teams', {
|
|
639
|
+
attributeQuery: [
|
|
640
|
+
'id',
|
|
641
|
+
'name',
|
|
642
|
+
'description',
|
|
643
|
+
['&', { attributeQuery: ['role', 'joinedAt'] }] // Relationship attributes
|
|
644
|
+
]
|
|
645
|
+
}]
|
|
646
|
+
]
|
|
647
|
+
);
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
### 11.5.2 Deep Queries
|
|
651
|
+
|
|
652
|
+
```typescript
|
|
653
|
+
// Multi-level relationship queries
|
|
654
|
+
const deepRelationQuery = await system.storage.find(
|
|
655
|
+
'User',
|
|
656
|
+
undefined,
|
|
657
|
+
{ limit: 5 },
|
|
658
|
+
[
|
|
659
|
+
'id',
|
|
660
|
+
'username',
|
|
661
|
+
['posts', {
|
|
662
|
+
attributeQuery: [
|
|
663
|
+
'id',
|
|
664
|
+
'title',
|
|
665
|
+
'content',
|
|
666
|
+
['comments', {
|
|
667
|
+
attributeQuery: [
|
|
668
|
+
'id',
|
|
669
|
+
'content',
|
|
670
|
+
'createdAt',
|
|
671
|
+
['author', {
|
|
672
|
+
attributeQuery: ['id', 'username', 'avatar']
|
|
673
|
+
}],
|
|
674
|
+
['replies', {
|
|
675
|
+
attributeQuery: [
|
|
676
|
+
'id',
|
|
677
|
+
'content',
|
|
678
|
+
['author', { attributeQuery: ['username'] }]
|
|
679
|
+
]
|
|
680
|
+
}]
|
|
681
|
+
]
|
|
682
|
+
}],
|
|
683
|
+
['tags', {
|
|
684
|
+
attributeQuery: ['name', 'category']
|
|
685
|
+
}]
|
|
686
|
+
]
|
|
687
|
+
}]
|
|
688
|
+
]
|
|
689
|
+
);
|
|
690
|
+
|
|
691
|
+
// Complex business query: get user's complete social network info
|
|
692
|
+
const socialNetworkInfo = await system.storage.find(
|
|
693
|
+
'User',
|
|
694
|
+
MatchExp.atom({ key: 'id', value: ['=', userId] }),
|
|
695
|
+
{},
|
|
696
|
+
[
|
|
697
|
+
'id',
|
|
698
|
+
'username',
|
|
699
|
+
'email',
|
|
700
|
+
['profile', {
|
|
701
|
+
attributeQuery: ['firstName', 'lastName', 'bio', 'location']
|
|
702
|
+
}],
|
|
703
|
+
['friends', {
|
|
704
|
+
attributeQuery: [
|
|
705
|
+
'id',
|
|
706
|
+
'username',
|
|
707
|
+
['profile', { attributeQuery: ['firstName', 'lastName'] }],
|
|
708
|
+
['&', { attributeQuery: ['since', 'closeness'] }]
|
|
709
|
+
]
|
|
710
|
+
}],
|
|
711
|
+
['posts', {
|
|
712
|
+
attributeQuery: [
|
|
713
|
+
'id',
|
|
714
|
+
'title',
|
|
715
|
+
'content',
|
|
716
|
+
'publishedAt',
|
|
717
|
+
['likes', {
|
|
718
|
+
attributeQuery: [
|
|
719
|
+
['user', { attributeQuery: ['username'] }],
|
|
720
|
+
['&', { attributeQuery: ['likedAt'] }]
|
|
721
|
+
]
|
|
722
|
+
}],
|
|
723
|
+
['comments', {
|
|
724
|
+
attributeQuery: [
|
|
725
|
+
'content',
|
|
726
|
+
'createdAt',
|
|
727
|
+
['author', { attributeQuery: ['username'] }]
|
|
728
|
+
]
|
|
729
|
+
}]
|
|
730
|
+
],
|
|
731
|
+
matchExpression: MatchExp.atom({ key: 'status', value: ['=', 'published'] })
|
|
732
|
+
}],
|
|
733
|
+
['groups', {
|
|
734
|
+
attributeQuery: [
|
|
735
|
+
'id',
|
|
736
|
+
'name',
|
|
737
|
+
'description',
|
|
738
|
+
['&', { attributeQuery: ['role', 'joinedAt'] }],
|
|
739
|
+
['members', {
|
|
740
|
+
attributeQuery: ['username'],
|
|
741
|
+
// Limit returned member count
|
|
742
|
+
limit: 10
|
|
743
|
+
}]
|
|
744
|
+
]
|
|
745
|
+
}]
|
|
746
|
+
]
|
|
747
|
+
);
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
### 11.5.3 Handling Circular References
|
|
751
|
+
|
|
752
|
+
```typescript
|
|
753
|
+
// Handle potential circular references
|
|
754
|
+
const usersWithLimitedFriends = await system.storage.find(
|
|
755
|
+
'User',
|
|
756
|
+
undefined,
|
|
757
|
+
{ limit: 10 },
|
|
758
|
+
[
|
|
759
|
+
'id',
|
|
760
|
+
'username',
|
|
761
|
+
['friends', {
|
|
762
|
+
attributeQuery: [
|
|
763
|
+
'id',
|
|
764
|
+
'username',
|
|
765
|
+
// Don't query friends of friends to avoid circular references
|
|
766
|
+
['&', { attributeQuery: ['since', 'closeness'] }]
|
|
767
|
+
]
|
|
768
|
+
}]
|
|
769
|
+
]
|
|
770
|
+
);
|
|
771
|
+
|
|
772
|
+
// Use depth limits to avoid infinite recursion
|
|
773
|
+
const limitedDepthQuery = await system.storage.find(
|
|
774
|
+
'Category',
|
|
775
|
+
undefined,
|
|
776
|
+
{},
|
|
777
|
+
[
|
|
778
|
+
'id',
|
|
779
|
+
'name',
|
|
780
|
+
['parent', {
|
|
781
|
+
attributeQuery: [
|
|
782
|
+
'id',
|
|
783
|
+
'name',
|
|
784
|
+
// Only query one level of parent to avoid excessive depth
|
|
785
|
+
]
|
|
786
|
+
}],
|
|
787
|
+
['children', {
|
|
788
|
+
attributeQuery: [
|
|
789
|
+
'id',
|
|
790
|
+
'name',
|
|
791
|
+
// Only query one level of children
|
|
792
|
+
]
|
|
793
|
+
}]
|
|
794
|
+
]
|
|
795
|
+
);
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
## 11.6 Advanced Query Techniques
|
|
799
|
+
|
|
800
|
+
### 11.6.1 Dynamic Query Building
|
|
801
|
+
|
|
802
|
+
```typescript
|
|
803
|
+
// Dynamic query builder
|
|
804
|
+
class QueryBuilder {
|
|
805
|
+
private matchCondition?: MatchExpressionData;
|
|
806
|
+
private modifier: any = {};
|
|
807
|
+
private attributes: string[] = [];
|
|
808
|
+
|
|
809
|
+
constructor(private entityName: string) {}
|
|
810
|
+
|
|
811
|
+
where(key: string, operator: string, value: any): this {
|
|
812
|
+
const condition = MatchExp.atom({ key, value: [operator, value] });
|
|
813
|
+
this.matchCondition = this.matchCondition ? this.matchCondition.and(condition) : condition;
|
|
814
|
+
return this;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
whereIn(key: string, values: any[]): this {
|
|
818
|
+
return this.where(key, 'in', values);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
whereLike(key: string, pattern: string): this {
|
|
822
|
+
return this.where(key, 'like', pattern);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
whereBetween(key: string, min: any, max: any): this {
|
|
826
|
+
return this.where(key, 'between', [min, max]);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
orderBy(field: string, direction: 'ASC' | 'DESC' = 'ASC'): this {
|
|
830
|
+
this.modifier.orderBy = { ...this.modifier.orderBy, [field]: direction };
|
|
831
|
+
return this;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
limit(count: number): this {
|
|
835
|
+
this.modifier.limit = count;
|
|
836
|
+
return this;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
offset(count: number): this {
|
|
840
|
+
this.modifier.offset = count;
|
|
841
|
+
return this;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
select(...attributes: string[]): this {
|
|
845
|
+
this.attributes = attributes;
|
|
846
|
+
return this;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
async execute(): Promise<any[]> {
|
|
850
|
+
return await system.storage.find(
|
|
851
|
+
this.entityName,
|
|
852
|
+
this.matchCondition,
|
|
853
|
+
this.modifier,
|
|
854
|
+
this.attributes.length > 0 ? this.attributes : ['*']
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
async first(): Promise<any> {
|
|
859
|
+
this.limit(1);
|
|
860
|
+
const results = await this.execute();
|
|
861
|
+
return results[0] || null;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// Use query builder
|
|
866
|
+
const activeAdultUsers = await new QueryBuilder('User')
|
|
867
|
+
.where('isActive', '=', true)
|
|
868
|
+
.where('age', '>=', 18)
|
|
869
|
+
.whereIn('role', ['user', 'admin'])
|
|
870
|
+
.orderBy('createdAt', 'DESC')
|
|
871
|
+
.limit(20)
|
|
872
|
+
.select('id', 'username', 'email', 'age', 'role')
|
|
873
|
+
.execute();
|
|
874
|
+
|
|
875
|
+
// Complex dynamic query
|
|
876
|
+
const searchResults = await new QueryBuilder('Product')
|
|
877
|
+
.where('isActive', '=', true)
|
|
878
|
+
.whereLike('name', '%laptop%')
|
|
879
|
+
.whereBetween('price', 500, 2000)
|
|
880
|
+
.whereIn('category', ['electronics', 'computers'])
|
|
881
|
+
.orderBy('price', 'ASC')
|
|
882
|
+
.orderBy('rating', 'DESC')
|
|
883
|
+
.limit(50)
|
|
884
|
+
.select('id', 'name', 'price', 'category', 'rating', 'description')
|
|
885
|
+
.execute();
|
|
886
|
+
```
|
|
887
|
+
|
|
888
|
+
### 11.6.2 Query Optimization
|
|
889
|
+
|
|
890
|
+
```typescript
|
|
891
|
+
// Index-friendly queries
|
|
892
|
+
const optimizedQuery = await system.storage.find(
|
|
893
|
+
'User',
|
|
894
|
+
MatchExp.atom({ key: 'email', value: ['=', 'user@example.com'] }), // Assuming email has index
|
|
895
|
+
{},
|
|
896
|
+
['id', 'username', 'isActive'] // Only query needed fields
|
|
897
|
+
);
|
|
898
|
+
|
|
899
|
+
// Batch query optimization
|
|
900
|
+
async function getUsersByIds(userIds: string[]) {
|
|
901
|
+
// Use IN query instead of multiple individual queries
|
|
902
|
+
return await system.storage.find(
|
|
903
|
+
'User',
|
|
904
|
+
MatchExp.atom({ key: 'id', value: ['in', userIds] }),
|
|
905
|
+
{},
|
|
906
|
+
['id', 'username', 'email', 'avatar']
|
|
907
|
+
);
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
// Pagination query optimization
|
|
911
|
+
async function getOptimizedPaginatedUsers(page: number, pageSize: number) {
|
|
912
|
+
// Use cursor-based pagination instead of offset pagination (for large datasets)
|
|
913
|
+
const lastUserId = page > 1 ? await getLastUserIdFromPreviousPage(page - 1, pageSize) : null;
|
|
914
|
+
|
|
915
|
+
let matchCondition = undefined;
|
|
916
|
+
if (lastUserId) {
|
|
917
|
+
matchCondition = MatchExp.atom({ key: 'id', value: ['>', lastUserId] });
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
return await system.storage.find(
|
|
921
|
+
'User',
|
|
922
|
+
matchCondition,
|
|
923
|
+
{
|
|
924
|
+
orderBy: { id: 'ASC' },
|
|
925
|
+
limit: pageSize
|
|
926
|
+
},
|
|
927
|
+
['id', 'username', 'email', 'createdAt']
|
|
928
|
+
);
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
// Preloading optimization
|
|
932
|
+
async function getUsersWithOptimizedRelations(userIds: string[]) {
|
|
933
|
+
// Get users and related data in one go, avoid N+1 problem
|
|
934
|
+
return await system.storage.find(
|
|
935
|
+
'User',
|
|
936
|
+
MatchExp.atom({ key: 'id', value: ['in', userIds] }),
|
|
937
|
+
{},
|
|
938
|
+
[
|
|
939
|
+
'id',
|
|
940
|
+
'username',
|
|
941
|
+
'email',
|
|
942
|
+
['profile', {
|
|
943
|
+
attributeQuery: ['firstName', 'lastName', 'avatar']
|
|
944
|
+
}],
|
|
945
|
+
['posts', {
|
|
946
|
+
attributeQuery: ['id', 'title', 'publishedAt'],
|
|
947
|
+
matchExpression: MatchExp.atom({ key: 'status', value: ['=', 'published'] }),
|
|
948
|
+
limit: 5 // Limit related data count
|
|
949
|
+
}]
|
|
950
|
+
]
|
|
951
|
+
);
|
|
952
|
+
}
|
|
953
|
+
```
|
|
954
|
+
|
|
955
|
+
### 11.6.3 Caching Query Results
|
|
956
|
+
|
|
957
|
+
```typescript
|
|
958
|
+
// Query result caching
|
|
959
|
+
class CachedQueryService {
|
|
960
|
+
private cache = new Map<string, { data: any, timestamp: number }>();
|
|
961
|
+
private ttl = 300000; // 5-minute TTL
|
|
962
|
+
|
|
963
|
+
private generateCacheKey(entityName: string, matchCondition: any, modifier: any, attributes: any): string {
|
|
964
|
+
return JSON.stringify({ entityName, matchCondition, modifier, attributes });
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
private isCacheValid(cacheEntry: { data: any, timestamp: number }): boolean {
|
|
968
|
+
return Date.now() - cacheEntry.timestamp < this.ttl;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
async find(entityName: string, matchCondition?: any, modifier: any = {}, attributes?: any): Promise<any[]> {
|
|
972
|
+
const cacheKey = this.generateCacheKey(entityName, matchCondition, modifier, attributes);
|
|
973
|
+
|
|
974
|
+
// Check cache
|
|
975
|
+
const cached = this.cache.get(cacheKey);
|
|
976
|
+
if (cached && this.isCacheValid(cached)) {
|
|
977
|
+
console.log('Cache hit');
|
|
978
|
+
return cached.data;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
// Execute query
|
|
982
|
+
console.log('Cache miss, executing query');
|
|
983
|
+
const result = await system.storage.find(entityName, matchCondition, modifier, attributes);
|
|
984
|
+
|
|
985
|
+
// Store in cache
|
|
986
|
+
this.cache.set(cacheKey, {
|
|
987
|
+
data: result,
|
|
988
|
+
timestamp: Date.now()
|
|
989
|
+
});
|
|
990
|
+
|
|
991
|
+
return result;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
async findOne(entityName: string, matchCondition?: any, modifier: any = {}, attributes?: any): Promise<any> {
|
|
995
|
+
const results = await this.find(entityName, matchCondition, { ...modifier, limit: 1 }, attributes);
|
|
996
|
+
return results[0] || null;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
clearCache(): void {
|
|
1000
|
+
this.cache.clear();
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
invalidateCache(pattern?: string): void {
|
|
1004
|
+
if (!pattern) {
|
|
1005
|
+
this.clearCache();
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
for (const key of this.cache.keys()) {
|
|
1010
|
+
if (key.includes(pattern)) {
|
|
1011
|
+
this.cache.delete(key);
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
// Use cached query service
|
|
1018
|
+
const cachedQuery = new CachedQueryService();
|
|
1019
|
+
|
|
1020
|
+
// First query will execute actual query
|
|
1021
|
+
const users1 = await cachedQuery.find('User', undefined, {}, ['id', 'username']);
|
|
1022
|
+
|
|
1023
|
+
// Second query will use cache
|
|
1024
|
+
const users2 = await cachedQuery.find('User', undefined, {}, ['id', 'username']);
|
|
1025
|
+
|
|
1026
|
+
// When user data changes, clear related cache
|
|
1027
|
+
// Can be integrated into data change events
|
|
1028
|
+
async function onUserDataChanged() {
|
|
1029
|
+
cachedQuery.invalidateCache('User');
|
|
1030
|
+
}
|
|
1031
|
+
```
|
|
1032
|
+
|
|
1033
|
+
Data querying is key to building efficient applications. By mastering the query capabilities provided by the interaqt framework, developers can flexibly obtain required data while maintaining good performance. Proper use of query conditions, attribute selection, relationship preloading, and caching strategies can significantly improve application response speed and user experience.
|