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,599 @@
|
|
|
1
|
+
# How to Establish Entity Relations
|
|
2
|
+
|
|
3
|
+
Relations are bridges that connect different entities, defining how entities are associated with each other. interaqt supports various types of relations, including one-to-one, one-to-many, many-to-many, and symmetric relations.
|
|
4
|
+
|
|
5
|
+
## Important: Relation Names are Auto-Generated
|
|
6
|
+
|
|
7
|
+
When creating relations, you **DO NOT** need to specify a `name` property. The framework automatically generates the relation name based on the source and target entities. For example:
|
|
8
|
+
- A relation between `User` and `Post` will automatically be named `UserPost`
|
|
9
|
+
- A relation between `Post` and `Comment` will automatically be named `PostComment`
|
|
10
|
+
|
|
11
|
+
```javascript
|
|
12
|
+
// ✅ Correct: No name specified
|
|
13
|
+
const UserPosts = Relation.create({
|
|
14
|
+
source: User,
|
|
15
|
+
sourceProperty: 'posts',
|
|
16
|
+
target: Post,
|
|
17
|
+
targetProperty: 'author',
|
|
18
|
+
type: 'n:1'
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// ❌ Wrong: Do not specify name
|
|
22
|
+
const UserPosts = Relation.create({
|
|
23
|
+
name: 'UserPost', // DON'T do this - name is auto-generated
|
|
24
|
+
source: User,
|
|
25
|
+
sourceProperty: 'posts',
|
|
26
|
+
target: Post,
|
|
27
|
+
targetProperty: 'author',
|
|
28
|
+
type: 'n:1'
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Creating One-to-One Relations
|
|
33
|
+
|
|
34
|
+
One-to-one relations represent unique correspondences between two entities, such as the relationship between a user and their profile.
|
|
35
|
+
|
|
36
|
+
### Basic One-to-One Relation
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
import { Entity, Property, Relation } from 'interaqt';
|
|
40
|
+
|
|
41
|
+
// Define user entity
|
|
42
|
+
const User = Entity.create({
|
|
43
|
+
name: 'User',
|
|
44
|
+
properties: [
|
|
45
|
+
Property.create({ name: 'email', type: 'string', unique: true }),
|
|
46
|
+
Property.create({ name: 'name', type: 'string' })
|
|
47
|
+
]
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Define profile entity
|
|
51
|
+
const Profile = Entity.create({
|
|
52
|
+
name: 'Profile',
|
|
53
|
+
properties: [
|
|
54
|
+
Property.create({ name: 'bio', type: 'string' }),
|
|
55
|
+
Property.create({ name: 'avatar', type: 'string' }),
|
|
56
|
+
Property.create({ name: 'website', type: 'string' })
|
|
57
|
+
]
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Create one-to-one relation
|
|
61
|
+
const UserProfile = Relation.create({
|
|
62
|
+
source: User,
|
|
63
|
+
sourceProperty: 'profile',
|
|
64
|
+
target: Profile,
|
|
65
|
+
targetProperty: 'user',
|
|
66
|
+
type: '1:1'
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Bidirectional Access
|
|
71
|
+
|
|
72
|
+
In one-to-one relations, you can access either end from the other:
|
|
73
|
+
|
|
74
|
+
```javascript
|
|
75
|
+
// Access profile from user
|
|
76
|
+
const user = await controller.findOne('User', { email: 'john@example.com' });
|
|
77
|
+
const profile = user.profile; // Get associated profile
|
|
78
|
+
|
|
79
|
+
// Access user from profile
|
|
80
|
+
const profile = await controller.findOne('Profile', { id: profileId });
|
|
81
|
+
const user = profile.user; // Get associated user
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Creating and Managing One-to-One Relations
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
// Create user and profile, establishing the relation
|
|
88
|
+
const createUserWithProfile = async (userData, profileData) => {
|
|
89
|
+
// First create the user
|
|
90
|
+
const user = await controller.create('User', userData);
|
|
91
|
+
|
|
92
|
+
// Create profile and link to user
|
|
93
|
+
const profile = await controller.create('Profile', {
|
|
94
|
+
...profileData,
|
|
95
|
+
user: user.id // Establish relation
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
return { user, profile };
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// Usage example
|
|
102
|
+
const { user, profile } = await createUserWithProfile(
|
|
103
|
+
{ email: 'john@example.com', name: 'John Doe' },
|
|
104
|
+
{ bio: 'Software developer', website: 'https://johndoe.dev' }
|
|
105
|
+
);
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Creating One-to-Many Relations
|
|
109
|
+
|
|
110
|
+
One-to-many relations represent that one entity can be associated with multiple instances of another entity, such as the relationship between users and posts.
|
|
111
|
+
|
|
112
|
+
### Basic One-to-Many Relation
|
|
113
|
+
|
|
114
|
+
```javascript
|
|
115
|
+
// User entity (already defined)
|
|
116
|
+
const User = Entity.create({
|
|
117
|
+
name: 'User',
|
|
118
|
+
properties: [
|
|
119
|
+
Property.create({ name: 'email', type: 'string', unique: true }),
|
|
120
|
+
Property.create({ name: 'name', type: 'string' })
|
|
121
|
+
]
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// Post entity
|
|
125
|
+
const Post = Entity.create({
|
|
126
|
+
name: 'Post',
|
|
127
|
+
properties: [
|
|
128
|
+
Property.create({ name: 'title', type: 'string' }),
|
|
129
|
+
Property.create({ name: 'content', type: 'string' }),
|
|
130
|
+
Property.create({ name: 'createdAt', type: 'string', defaultValue: () => new Date().toISOString() })
|
|
131
|
+
]
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Create one-to-many relation
|
|
135
|
+
const UserPosts = Relation.create({
|
|
136
|
+
source: User,
|
|
137
|
+
sourceProperty: 'posts',
|
|
138
|
+
target: Post,
|
|
139
|
+
targetProperty: 'author',
|
|
140
|
+
type: 'n:1'
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Accessing "Many" from "One"
|
|
145
|
+
|
|
146
|
+
```javascript
|
|
147
|
+
// Get all posts for a user
|
|
148
|
+
const user = await controller.findOne('User', { email: 'john@example.com' });
|
|
149
|
+
const posts = user.posts; // Get all user's posts
|
|
150
|
+
|
|
151
|
+
// Also accessible through queries
|
|
152
|
+
const userPosts = await controller.find('Post', {
|
|
153
|
+
author: user.id
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Accessing "One" from "Many"
|
|
158
|
+
|
|
159
|
+
```javascript
|
|
160
|
+
// Get post's author
|
|
161
|
+
const post = await controller.findOne('Post', { id: postId });
|
|
162
|
+
const author = post.author; // Get post's author
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Creating and Managing One-to-Many Relations
|
|
166
|
+
|
|
167
|
+
```javascript
|
|
168
|
+
// Create new post for user
|
|
169
|
+
const createPost = async (userId, postData) => {
|
|
170
|
+
return await controller.create('Post', {
|
|
171
|
+
...postData,
|
|
172
|
+
author: userId // Establish relation with user
|
|
173
|
+
});
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// Usage example
|
|
177
|
+
const user = await controller.findOne('User', { email: 'john@example.com' });
|
|
178
|
+
const post = await createPost(user.id, {
|
|
179
|
+
title: 'My First Post',
|
|
180
|
+
content: 'This is the content of my first post.'
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Get all user's posts (including newly created)
|
|
184
|
+
const allUserPosts = await controller.find('Post', { author: user.id });
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Creating Many-to-Many Relations
|
|
188
|
+
|
|
189
|
+
Many-to-many relations represent multiple associations between two entities, such as the relationship between users and tags.
|
|
190
|
+
|
|
191
|
+
### Basic Many-to-Many Relation
|
|
192
|
+
|
|
193
|
+
```javascript
|
|
194
|
+
// Tag entity
|
|
195
|
+
const Tag = Entity.create({
|
|
196
|
+
name: 'Tag',
|
|
197
|
+
properties: [
|
|
198
|
+
Property.create({ name: 'name', type: 'string', unique: true }),
|
|
199
|
+
Property.create({ name: 'color', type: 'string', defaultValue: '#666666' })
|
|
200
|
+
]
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// Create many-to-many relation
|
|
204
|
+
const UserTags = Relation.create({
|
|
205
|
+
source: User,
|
|
206
|
+
sourceProperty: 'tags',
|
|
207
|
+
target: Tag,
|
|
208
|
+
targetProperty: 'users',
|
|
209
|
+
type: 'n:n'
|
|
210
|
+
});
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Bidirectional Access
|
|
214
|
+
|
|
215
|
+
```javascript
|
|
216
|
+
// Access tags from user
|
|
217
|
+
const user = await controller.findOne('User', { email: 'john@example.com' });
|
|
218
|
+
const userTags = user.tags; // Get all user's tags
|
|
219
|
+
|
|
220
|
+
// Access users from tag
|
|
221
|
+
const tag = await controller.findOne('Tag', { name: 'javascript' });
|
|
222
|
+
const tagUsers = tag.users; // Get all users with this tag
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Managing Many-to-Many Relations
|
|
226
|
+
|
|
227
|
+
```javascript
|
|
228
|
+
// Add tag to user
|
|
229
|
+
const addTagToUser = async (userId, tagName) => {
|
|
230
|
+
// Find or create tag
|
|
231
|
+
let tag = await controller.findOne('Tag', { name: tagName });
|
|
232
|
+
if (!tag) {
|
|
233
|
+
tag = await controller.create('Tag', { name: tagName });
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Establish relation (specific implementation depends on framework's relation management)
|
|
237
|
+
await controller.createRelation('UserTags', {
|
|
238
|
+
source: userId,
|
|
239
|
+
target: tag.id
|
|
240
|
+
});
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
// Remove tag from user
|
|
244
|
+
const removeTagFromUser = async (userId, tagId) => {
|
|
245
|
+
await controller.removeRelation('UserTags', {
|
|
246
|
+
source: userId,
|
|
247
|
+
target: tagId
|
|
248
|
+
});
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// Usage example
|
|
252
|
+
const user = await controller.findOne('User', { email: 'john@example.com' });
|
|
253
|
+
await addTagToUser(user.id, 'javascript');
|
|
254
|
+
await addTagToUser(user.id, 'react');
|
|
255
|
+
await addTagToUser(user.id, 'nodejs');
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Adding Relation Properties
|
|
259
|
+
|
|
260
|
+
Many-to-many relations can include additional properties:
|
|
261
|
+
|
|
262
|
+
```javascript
|
|
263
|
+
// User-Post like relation (including like timestamp)
|
|
264
|
+
const Like = Relation.create({
|
|
265
|
+
source: User,
|
|
266
|
+
sourceProperty: 'likedPosts',
|
|
267
|
+
target: Post,
|
|
268
|
+
targetProperty: 'likers',
|
|
269
|
+
type: 'n:n',
|
|
270
|
+
properties: [
|
|
271
|
+
Property.create({
|
|
272
|
+
name: 'likedAt',
|
|
273
|
+
type: 'string',
|
|
274
|
+
defaultValue: () => new Date().toISOString()
|
|
275
|
+
}),
|
|
276
|
+
Property.create({
|
|
277
|
+
name: 'type',
|
|
278
|
+
type: 'string',
|
|
279
|
+
defaultValue: () => 'like' // Can be 'like', 'love', 'laugh', etc.
|
|
280
|
+
})
|
|
281
|
+
]
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// Create relation with properties
|
|
285
|
+
const likePost = async (userId, postId, likeType = 'like') => {
|
|
286
|
+
await controller.createRelation('Like', {
|
|
287
|
+
source: userId,
|
|
288
|
+
target: postId,
|
|
289
|
+
properties: {
|
|
290
|
+
type: likeType,
|
|
291
|
+
likedAt: new Date().toISOString()
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
};
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## Using Symmetric Relations
|
|
298
|
+
|
|
299
|
+
Symmetric relations are a special type of many-to-many relation where both ends are the same entity type, such as friend relationships.
|
|
300
|
+
|
|
301
|
+
### Creating Symmetric Relations
|
|
302
|
+
|
|
303
|
+
```javascript
|
|
304
|
+
// Friend relation
|
|
305
|
+
// Note: The system automatically detects symmetric relations when:
|
|
306
|
+
// source === target (both User) AND sourceProperty === targetProperty (both 'friends')
|
|
307
|
+
const Friendship = Relation.create({
|
|
308
|
+
source: User,
|
|
309
|
+
sourceProperty: 'friends',
|
|
310
|
+
target: User,
|
|
311
|
+
targetProperty: 'friends', // Same as sourceProperty - automatically symmetric
|
|
312
|
+
type: 'n:n',
|
|
313
|
+
properties: [
|
|
314
|
+
Property.create({
|
|
315
|
+
name: 'createdAt',
|
|
316
|
+
type: 'string',
|
|
317
|
+
defaultValue: () => new Date().toISOString()
|
|
318
|
+
}),
|
|
319
|
+
Property.create({
|
|
320
|
+
name: 'status',
|
|
321
|
+
type: 'string',
|
|
322
|
+
defaultValue: 'pending' // pending, accepted, blocked
|
|
323
|
+
})
|
|
324
|
+
]
|
|
325
|
+
});
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Special Properties of Symmetric Relations
|
|
329
|
+
|
|
330
|
+
Symmetric relations have the following characteristics:
|
|
331
|
+
|
|
332
|
+
1. **Automatic bidirectional sync**: When A adds B as a friend, A is automatically included in B's friend list
|
|
333
|
+
2. **Single relation record**: The system stores only one relation record, but both ends can access it
|
|
334
|
+
3. **Status consistency**: The relation status is consistent for both parties
|
|
335
|
+
|
|
336
|
+
```javascript
|
|
337
|
+
// Send friend request
|
|
338
|
+
const sendFriendRequest = async (fromUserId, toUserId) => {
|
|
339
|
+
await controller.createRelation('Friendship', {
|
|
340
|
+
source: fromUserId,
|
|
341
|
+
target: toUserId,
|
|
342
|
+
properties: {
|
|
343
|
+
status: 'pending'
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
// Accept friend request
|
|
349
|
+
const acceptFriendRequest = async (userId, friendId) => {
|
|
350
|
+
await controller.updateRelation('Friendship', {
|
|
351
|
+
source: userId,
|
|
352
|
+
target: friendId
|
|
353
|
+
}, {
|
|
354
|
+
status: 'accepted'
|
|
355
|
+
});
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
// Get user's friend list
|
|
359
|
+
const getUserFriends = async (userId) => {
|
|
360
|
+
const user = await controller.findOne('User', { id: userId });
|
|
361
|
+
return user.friends.filter(friend => friend.status === 'accepted');
|
|
362
|
+
};
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### Automatic Bidirectional Sync Example
|
|
366
|
+
|
|
367
|
+
```javascript
|
|
368
|
+
// User A adds User B as friend
|
|
369
|
+
const userA = await controller.findOne('User', { email: 'alice@example.com' });
|
|
370
|
+
const userB = await controller.findOne('User', { email: 'bob@example.com' });
|
|
371
|
+
|
|
372
|
+
await sendFriendRequest(userA.id, userB.id);
|
|
373
|
+
|
|
374
|
+
// Now both users can see this relation
|
|
375
|
+
const aliceFriends = await getUserFriends(userA.id); // Contains Bob (status: pending)
|
|
376
|
+
const bobFriends = await getUserFriends(userB.id); // Contains Alice (status: pending)
|
|
377
|
+
|
|
378
|
+
// Bob accepts friend request
|
|
379
|
+
await acceptFriendRequest(userB.id, userA.id);
|
|
380
|
+
|
|
381
|
+
// Now both users have accepted friend status
|
|
382
|
+
const aliceFriendsAccepted = await getUserFriends(userA.id); // Bob (status: accepted)
|
|
383
|
+
const bobFriendsAccepted = await getUserFriends(userB.id); // Alice (status: accepted)
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
## Relation Queries and Optimization
|
|
387
|
+
|
|
388
|
+
### Preloading Related Data
|
|
389
|
+
|
|
390
|
+
To avoid N+1 query problems, you can preload related data:
|
|
391
|
+
|
|
392
|
+
```javascript
|
|
393
|
+
// Query users with their posts loaded
|
|
394
|
+
const usersWithPosts = await controller.find('User', {}, {
|
|
395
|
+
include: ['posts']
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// Query posts with author and tags loaded
|
|
399
|
+
const postsWithDetails = await controller.find('Post', {}, {
|
|
400
|
+
include: ['author', 'tags']
|
|
401
|
+
});
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### Deep Queries
|
|
405
|
+
|
|
406
|
+
You can perform multi-level relational queries:
|
|
407
|
+
|
|
408
|
+
```javascript
|
|
409
|
+
// Query users, including their posts and post comments
|
|
410
|
+
const usersWithPostsAndComments = await controller.find('User', {}, {
|
|
411
|
+
include: [
|
|
412
|
+
{
|
|
413
|
+
relation: 'posts',
|
|
414
|
+
include: ['comments']
|
|
415
|
+
}
|
|
416
|
+
]
|
|
417
|
+
});
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Conditional Queries on Relations
|
|
421
|
+
|
|
422
|
+
You can query based on related data:
|
|
423
|
+
|
|
424
|
+
```javascript
|
|
425
|
+
// Find users with more than 10 posts
|
|
426
|
+
const activeUsers = await controller.find('User', {
|
|
427
|
+
'posts.count': { $gt: 10 }
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
// Find posts liked by a specific user
|
|
431
|
+
const likedPosts = await controller.find('Post', {
|
|
432
|
+
'likers.id': userId
|
|
433
|
+
});
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
## Relation Management Best Practices
|
|
437
|
+
|
|
438
|
+
### 1. Design Relations with Clear Direction
|
|
439
|
+
|
|
440
|
+
```javascript
|
|
441
|
+
// ✅ Good design: From owner to owned
|
|
442
|
+
const UserPosts = Relation.create({
|
|
443
|
+
source: User, // User owns posts
|
|
444
|
+
target: Post,
|
|
445
|
+
type: '1:n'
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
// ❌ Avoid: Unclear direction
|
|
449
|
+
const PostUser = Relation.create({
|
|
450
|
+
source: Post, // Post belongs to user (less intuitive direction)
|
|
451
|
+
target: User,
|
|
452
|
+
type: 'n:1'
|
|
453
|
+
});
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### 2. Use Meaningful Property Names
|
|
457
|
+
|
|
458
|
+
```javascript
|
|
459
|
+
// ✅ Clear property names
|
|
460
|
+
const UserPosts = Relation.create({
|
|
461
|
+
source: User,
|
|
462
|
+
sourceProperty: 'posts', // User's posts
|
|
463
|
+
target: Post,
|
|
464
|
+
targetProperty: 'author', // Post's author
|
|
465
|
+
type: '1:n'
|
|
466
|
+
});
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
### 3. Use Relation Properties Appropriately
|
|
470
|
+
|
|
471
|
+
```javascript
|
|
472
|
+
// ✅ Add useful metadata to relations
|
|
473
|
+
const Membership = Relation.create({
|
|
474
|
+
source: User,
|
|
475
|
+
target: Organization,
|
|
476
|
+
type: 'n:n',
|
|
477
|
+
properties: [
|
|
478
|
+
Property.create({ name: 'role', type: 'string' }), // Role
|
|
479
|
+
Property.create({ name: 'joinedAt', type: 'string' }), // Join time
|
|
480
|
+
Property.create({ name: 'isActive', type: 'boolean' }) // Active status
|
|
481
|
+
]
|
|
482
|
+
});
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
### 4. Consider Performance Impact
|
|
486
|
+
|
|
487
|
+
```javascript
|
|
488
|
+
// Relation definition example (indexing should be configured at database level)
|
|
489
|
+
const UserPosts = Relation.create({
|
|
490
|
+
source: User,
|
|
491
|
+
sourceProperty: 'posts',
|
|
492
|
+
target: Post,
|
|
493
|
+
targetProperty: 'author',
|
|
494
|
+
type: 'n:1'
|
|
495
|
+
// Index configuration should be done at database level
|
|
496
|
+
});
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
## Complete Example: Blog System Relation Design
|
|
500
|
+
|
|
501
|
+
```javascript
|
|
502
|
+
import { Entity, Property, Relation } from 'interaqt';
|
|
503
|
+
|
|
504
|
+
// Entity definitions
|
|
505
|
+
const User = Entity.create({
|
|
506
|
+
name: 'User',
|
|
507
|
+
properties: [
|
|
508
|
+
Property.create({ name: 'email', type: 'string', unique: true }),
|
|
509
|
+
Property.create({ name: 'name', type: 'string' }),
|
|
510
|
+
Property.create({ name: 'avatar', type: 'string' })
|
|
511
|
+
]
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
const Post = Entity.create({
|
|
515
|
+
name: 'Post',
|
|
516
|
+
properties: [
|
|
517
|
+
Property.create({ name: 'title', type: 'string' }),
|
|
518
|
+
Property.create({ name: 'content', type: 'string' }),
|
|
519
|
+
Property.create({ name: 'status', type: 'string', defaultValue: 'draft' }),
|
|
520
|
+
Property.create({ name: 'createdAt', type: 'string', defaultValue: () => new Date().toISOString() })
|
|
521
|
+
]
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
const Comment = Entity.create({
|
|
525
|
+
name: 'Comment',
|
|
526
|
+
properties: [
|
|
527
|
+
Property.create({ name: 'content', type: 'string' }),
|
|
528
|
+
Property.create({ name: 'createdAt', type: 'string', defaultValue: () => new Date().toISOString() })
|
|
529
|
+
]
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
const Tag = Entity.create({
|
|
533
|
+
name: 'Tag',
|
|
534
|
+
properties: [
|
|
535
|
+
Property.create({ name: 'name', type: 'string', unique: true }),
|
|
536
|
+
Property.create({ name: 'color', type: 'string', defaultValue: '#666666' })
|
|
537
|
+
]
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
// Relation definitions
|
|
541
|
+
const UserPosts = Relation.create({
|
|
542
|
+
source: User,
|
|
543
|
+
sourceProperty: 'posts',
|
|
544
|
+
target: Post,
|
|
545
|
+
targetProperty: 'author',
|
|
546
|
+
type: 'n:1'
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
const PostComments = Relation.create({
|
|
550
|
+
source: Post,
|
|
551
|
+
sourceProperty: 'comments',
|
|
552
|
+
target: Comment,
|
|
553
|
+
targetProperty: 'post',
|
|
554
|
+
type: 'n:1'
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
const UserComments = Relation.create({
|
|
558
|
+
source: User,
|
|
559
|
+
sourceProperty: 'comments',
|
|
560
|
+
target: Comment,
|
|
561
|
+
targetProperty: 'author',
|
|
562
|
+
type: 'n:1'
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
const PostTags = Relation.create({
|
|
566
|
+
source: Post,
|
|
567
|
+
sourceProperty: 'tags',
|
|
568
|
+
target: Tag,
|
|
569
|
+
targetProperty: 'posts',
|
|
570
|
+
type: 'n:n'
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
const Like = Relation.create({
|
|
574
|
+
source: User,
|
|
575
|
+
sourceProperty: 'likedPosts',
|
|
576
|
+
target: Post,
|
|
577
|
+
targetProperty: 'likers',
|
|
578
|
+
type: 'n:n',
|
|
579
|
+
properties: [
|
|
580
|
+
Property.create({ name: 'likedAt', type: 'string', defaultValue: () => new Date().toISOString() })
|
|
581
|
+
]
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
const Follow = Relation.create({
|
|
585
|
+
source: User,
|
|
586
|
+
sourceProperty: 'following',
|
|
587
|
+
target: User,
|
|
588
|
+
targetProperty: 'followers',
|
|
589
|
+
type: 'n:n',
|
|
590
|
+
properties: [
|
|
591
|
+
Property.create({ name: 'followedAt', type: 'string', defaultValue: () => new Date().toISOString() })
|
|
592
|
+
]
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
export {
|
|
596
|
+
User, Post, Comment, Tag,
|
|
597
|
+
UserPosts, PostComments, UserComments, PostTags, Like, Follow
|
|
598
|
+
};
|
|
599
|
+
```
|