aifastdb 3.7.6 → 3.8.6-mac.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.
@@ -1,997 +0,0 @@
1
- "use strict";
2
- /**
3
- * SocialGraph - High-level TypeScript API for social network operations
4
- *
5
- * Provides a friendly API wrapper around the native Rust social graph implementation.
6
- */
7
- Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.SocialGraph = void 0;
9
- /**
10
- * SocialGraph provides high-level APIs for social network operations.
11
- *
12
- * @example
13
- * ```typescript
14
- * const db = new AiFastDb({ path: './data', dimension: 768 });
15
- * const social = new SocialGraph(db);
16
- *
17
- * // Add persons
18
- * const alice = social.addPerson({ name: 'Alice' });
19
- * const bob = social.addPerson({ name: 'Bob' });
20
- *
21
- * // Create friendship
22
- * social.addFriend(alice.id, bob.id);
23
- *
24
- * // Find path between people
25
- * const path = social.findPath(alice.id, someoneElse.id);
26
- * ```
27
- */
28
- class SocialGraph {
29
- constructor(db) {
30
- // Access the native social graph through AiFastDb
31
- this.native = db.socialGraph();
32
- }
33
- // ========================================================================
34
- // Person Management
35
- // ========================================================================
36
- /**
37
- * Add a new person to the social graph
38
- */
39
- addPerson(input) {
40
- return this.native.addPerson(input);
41
- }
42
- /**
43
- * Get a person by ID
44
- */
45
- getPerson(id) {
46
- return this.native.getPerson(id) ?? null;
47
- }
48
- /**
49
- * Update a person's information
50
- */
51
- updatePerson(id, updates) {
52
- return this.native.updatePerson(id, updates) ?? null;
53
- }
54
- /**
55
- * Delete a person from the social graph
56
- */
57
- deletePerson(id) {
58
- return this.native.deletePerson(id);
59
- }
60
- /**
61
- * List all persons with optional filter
62
- */
63
- listPersons(filter) {
64
- return this.native.listPersons(filter);
65
- }
66
- // ========================================================================
67
- // Friend Relationships
68
- // ========================================================================
69
- /**
70
- * Add a friend relationship between two persons
71
- */
72
- addFriend(personA, personB, config) {
73
- return this.native.addFriend(personA, personB, config) ?? null;
74
- }
75
- /**
76
- * Remove a friend relationship
77
- */
78
- removeFriend(personA, personB) {
79
- return this.native.removeFriend(personA, personB);
80
- }
81
- /**
82
- * Get all friends of a person
83
- */
84
- getFriends(personId) {
85
- return this.native.getFriends(personId);
86
- }
87
- /**
88
- * Check if two persons are friends
89
- */
90
- areFriends(personA, personB) {
91
- return this.native.areFriends(personA, personB);
92
- }
93
- /**
94
- * List all relations with optional filter
95
- *
96
- * Returns all relation edges including friend relations, person-tag relations, etc.
97
- * Each relation includes full metadata (relation_type, weight, bidirectional, metadata).
98
- *
99
- * @param filter - Optional filter criteria
100
- * @returns Array of relations
101
- *
102
- * @example
103
- * ```typescript
104
- * // Get all relations
105
- * const allRelations = social.listRelations();
106
- *
107
- * // Get only friend relations
108
- * const friendRelations = social.listRelations({ relationType: 'friend' });
109
- *
110
- * // Get relations for a specific person
111
- * const personRelations = social.listRelations({ sourceId: alice.id });
112
- * ```
113
- */
114
- listRelations(filter) {
115
- // Try native method first
116
- if (typeof this.native.listRelations === 'function') {
117
- return this.native.listRelations(filter);
118
- }
119
- // Fallback: reconstruct relations from API calls
120
- // Note: This fallback has limitations - it may not capture all metadata
121
- const relations = [];
122
- const seenEdges = new Set();
123
- const now = Date.now();
124
- const persons = this.listPersons();
125
- for (const person of persons) {
126
- // Get friend relations
127
- const friends = this.getFriends(person.id);
128
- for (const friend of friends) {
129
- // Create unique edge ID (sorted to avoid duplicates)
130
- const edgeId = [person.id, friend.id].sort().join('-');
131
- if (!seenEdges.has(edgeId)) {
132
- seenEdges.add(edgeId);
133
- relations.push({
134
- id: edgeId,
135
- source: person.id,
136
- target: friend.id,
137
- relation_type: 'friend', // Default - actual type not available in fallback
138
- weight: 1.0, // Default - actual weight not available in fallback
139
- bidirectional: true,
140
- metadata: {},
141
- created_at: now,
142
- });
143
- }
144
- }
145
- // Get person-tag relations
146
- const tags = this.getPersonTags(person.id);
147
- for (const tag of tags) {
148
- const edgeId = `${person.id}-tag-${tag.id}`;
149
- if (!seenEdges.has(edgeId)) {
150
- seenEdges.add(edgeId);
151
- const props = tag.properties;
152
- const isCompany = props?.tagType === 'company' || props?.category === 'company';
153
- relations.push({
154
- id: edgeId,
155
- source: person.id,
156
- target: tag.id,
157
- relation_type: isCompany ? 'work_for' : 'tagged',
158
- weight: 1.0,
159
- bidirectional: false,
160
- metadata: { tagType: props?.tagType },
161
- created_at: now,
162
- });
163
- }
164
- }
165
- }
166
- // Apply filters
167
- let results = relations;
168
- if (filter) {
169
- if (filter.relationType) {
170
- results = results.filter(r => r.relation_type === filter.relationType);
171
- }
172
- if (filter.sourceId) {
173
- results = results.filter(r => r.source === filter.sourceId || r.target === filter.sourceId);
174
- }
175
- if (filter.targetId) {
176
- results = results.filter(r => r.target === filter.targetId || r.source === filter.targetId);
177
- }
178
- if (filter.limit && filter.limit > 0) {
179
- results = results.slice(0, filter.limit);
180
- }
181
- }
182
- return results;
183
- }
184
- /**
185
- * Get a specific relation by ID
186
- *
187
- * @param relationId - The relation ID
188
- * @returns The relation if found, null otherwise
189
- */
190
- getRelation(relationId) {
191
- // Try native method first
192
- if (typeof this.native.getRelation === 'function') {
193
- return this.native.getRelation(relationId) ?? null;
194
- }
195
- // Fallback: search in listRelations
196
- const relations = this.listRelations();
197
- return relations.find(r => r.id === relationId) ?? null;
198
- }
199
- /**
200
- * Get relation between two specific entities
201
- *
202
- * @param entityA - First entity ID
203
- * @param entityB - Second entity ID
204
- * @returns The relation if found, null otherwise
205
- */
206
- getRelationBetween(entityA, entityB) {
207
- // Try native method first
208
- if (typeof this.native.getRelationBetween === 'function') {
209
- return this.native.getRelationBetween(entityA, entityB) ?? null;
210
- }
211
- // Fallback: search in listRelations
212
- const relations = this.listRelations();
213
- return relations.find(r => (r.source === entityA && r.target === entityB) ||
214
- (r.source === entityB && r.target === entityA)) ?? null;
215
- }
216
- // ========================================================================
217
- // Tag Management
218
- // ========================================================================
219
- /**
220
- * Add a new tag
221
- */
222
- addTag(input) {
223
- return this.native.addTag(input);
224
- }
225
- /**
226
- * Tag a person (add person to a tag/organization)
227
- */
228
- tagPerson(personId, tagId) {
229
- return this.native.tagPerson(personId, tagId) ?? null;
230
- }
231
- /**
232
- * Remove tag from person
233
- */
234
- untagPerson(personId, tagId) {
235
- return this.native.untagPerson(personId, tagId);
236
- }
237
- /**
238
- * Get all tags of a person
239
- */
240
- getPersonTags(personId) {
241
- return this.native.getPersonTags(personId);
242
- }
243
- /**
244
- * Get all members of a tag
245
- */
246
- getTagMembers(tagId) {
247
- return this.native.getTagMembers(tagId);
248
- }
249
- /**
250
- * List all tags with optional filter
251
- *
252
- * Returns all tag entities (including company, organization, skill, interest types).
253
- * Use the filter to narrow down results by tagType or name.
254
- *
255
- * @param filter - Optional filter criteria
256
- * @returns Array of tag entities
257
- *
258
- * @example
259
- * ```typescript
260
- * // Get all tags
261
- * const allTags = social.listTags();
262
- *
263
- * // Get only companies
264
- * const companies = social.listTags({ tagType: 'company' });
265
- *
266
- * // Get tags with name containing 'tech'
267
- * const techTags = social.listTags({ name: 'tech' });
268
- * ```
269
- */
270
- listTags(filter) {
271
- // Try native method first
272
- if (typeof this.native.listTags === 'function') {
273
- return this.native.listTags(filter);
274
- }
275
- // Fallback: collect tags from all persons (may miss orphan tags)
276
- const tagMap = new Map();
277
- const persons = this.listPersons();
278
- for (const person of persons) {
279
- const tags = this.getPersonTags(person.id);
280
- for (const tag of tags) {
281
- if (!tagMap.has(tag.id)) {
282
- tagMap.set(tag.id, tag);
283
- }
284
- }
285
- }
286
- let results = Array.from(tagMap.values());
287
- // Apply filters
288
- if (filter) {
289
- if (filter.tagType) {
290
- results = results.filter(t => {
291
- const props = t.properties;
292
- return props?.tagType === filter.tagType;
293
- });
294
- }
295
- if (filter.name) {
296
- const nameLower = filter.name.toLowerCase();
297
- results = results.filter(t => t.name.toLowerCase().includes(nameLower));
298
- }
299
- if (filter.limit && filter.limit > 0) {
300
- results = results.slice(0, filter.limit);
301
- }
302
- }
303
- return results;
304
- }
305
- /**
306
- * Get a tag by ID
307
- */
308
- getTag(tagId) {
309
- // Try native method first
310
- if (typeof this.native.getTag === 'function') {
311
- return this.native.getTag(tagId) ?? null;
312
- }
313
- // Fallback: search in listTags
314
- const tags = this.listTags();
315
- return tags.find(t => t.id === tagId) ?? null;
316
- }
317
- // ========================================================================
318
- // Item Management
319
- // ========================================================================
320
- /**
321
- * Add a new item
322
- */
323
- addItem(input) {
324
- return this.native.addItem(input);
325
- }
326
- /**
327
- * Add item to person (ownership)
328
- */
329
- addItemToPerson(personId, itemId, relation) {
330
- return this.native.addItemToPerson(personId, itemId, relation) ?? null;
331
- }
332
- /**
333
- * Get all items owned by a person
334
- */
335
- getPersonItems(personId) {
336
- return this.native.getPersonItems(personId);
337
- }
338
- /**
339
- * Get all owners of an item
340
- */
341
- getItemOwners(itemId) {
342
- return this.native.getItemOwners(itemId);
343
- }
344
- // ========================================================================
345
- // Attribute Management
346
- // ========================================================================
347
- /**
348
- * Add a new attribute
349
- */
350
- addAttribute(input) {
351
- return this.native.addAttribute(input);
352
- }
353
- /**
354
- * Find or create an attribute by category and value
355
- */
356
- findOrCreateAttribute(category, value) {
357
- return this.native.findOrCreateAttribute(category, value);
358
- }
359
- /**
360
- * Set a person's attribute
361
- */
362
- setPersonAttribute(personId, attrId, relation) {
363
- return this.native.setPersonAttribute(personId, attrId, relation) ?? null;
364
- }
365
- /**
366
- * Get all attributes of a person
367
- */
368
- getPersonAttributes(personId) {
369
- return this.native.getPersonAttributes(personId);
370
- }
371
- /**
372
- * Get all persons with a specific attribute
373
- */
374
- getPersonsByAttribute(attrId) {
375
- return this.native.getPersonsByAttribute(attrId);
376
- }
377
- // ========================================================================
378
- // Path Finding (Person-to-Person)
379
- // ========================================================================
380
- /**
381
- * Find the shortest path between two persons
382
- *
383
- * @example
384
- * ```typescript
385
- * const path = social.findPath(alice.id, bob.id);
386
- * if (path) {
387
- * console.log(`Found path with ${path.hops} hops`);
388
- * console.log('Intermediaries:', path.intermediaries.map(p => p.name));
389
- * }
390
- * ```
391
- */
392
- findPath(fromId, toId, options) {
393
- return this.native.findPath(fromId, toId, options) ?? null;
394
- }
395
- /**
396
- * Find all paths between two persons
397
- */
398
- findAllPaths(fromId, toId, maxHops, maxPaths) {
399
- return this.native.findAllPaths(fromId, toId, maxHops, maxPaths);
400
- }
401
- /**
402
- * Find all intermediaries who can introduce two persons
403
- *
404
- * @example
405
- * ```typescript
406
- * const intermediaries = social.findIntermediaries(alice.id, bob.id);
407
- * console.log('Can be introduced via:', intermediaries.map(p => p.name));
408
- * ```
409
- */
410
- findIntermediaries(personA, personB, maxHops) {
411
- return this.native.findIntermediaries(personA, personB, maxHops);
412
- }
413
- // ========================================================================
414
- // Network Analysis
415
- // ========================================================================
416
- /**
417
- * Get N-degree contacts of a person
418
- *
419
- * @example
420
- * ```typescript
421
- * const contacts = social.getNthDegreeContacts(alice.id, 3);
422
- * console.log('Direct friends:', contacts.degree1.length);
423
- * console.log('Friends of friends:', contacts.degree2.length);
424
- * console.log('3rd degree:', contacts.degree3.length);
425
- * ```
426
- */
427
- getNthDegreeContacts(personId, maxDegree) {
428
- return this.native.getNthDegreeContacts(personId, maxDegree);
429
- }
430
- /**
431
- * Get mutual friends between two persons
432
- */
433
- getMutualFriends(personA, personB) {
434
- return this.native.getMutualFriends(personA, personB);
435
- }
436
- /**
437
- * Get mutual tags between two persons
438
- */
439
- getMutualTags(personA, personB) {
440
- return this.native.getMutualTags(personA, personB);
441
- }
442
- /**
443
- * Calculate relationship strength between two persons
444
- */
445
- getRelationshipStrength(personA, personB) {
446
- return this.native.getRelationshipStrength(personA, personB);
447
- }
448
- /**
449
- * Get connection suggestions for a person
450
- *
451
- * @example
452
- * ```typescript
453
- * const suggestions = social.suggestConnections(alice.id, {
454
- * maxResults: 10,
455
- * minScore: 0.3,
456
- * });
457
- *
458
- * for (const s of suggestions) {
459
- * console.log(`Suggest: ${s.person.name} (score: ${s.score})`);
460
- * for (const reason of s.reasons) {
461
- * console.log(` - ${reason.type}`);
462
- * }
463
- * }
464
- * ```
465
- */
466
- suggestConnections(personId, config) {
467
- return this.native.suggestConnections(personId, config);
468
- }
469
- /**
470
- * Get network statistics for a person
471
- */
472
- getNetworkStats(personId) {
473
- return this.native.getNetworkStats(personId);
474
- }
475
- // ========================================================================
476
- // Item Matching
477
- // ========================================================================
478
- /**
479
- * Find shared items between two people
480
- */
481
- findSharedItems(personA, personB) {
482
- return this.native.findSharedItems(personA, personB);
483
- }
484
- /**
485
- * Find people who share items with a given person
486
- *
487
- * @example
488
- * ```typescript
489
- * const carFriends = social.findPeopleBySharedItems(alice.id, 'vehicle');
490
- * for (const match of carFriends) {
491
- * console.log(`${match.person.name} shares ${match.sharedItems.length} items`);
492
- * }
493
- * ```
494
- */
495
- findPeopleBySharedItems(personId, category) {
496
- return this.native.findPeopleBySharedItems(personId, category);
497
- }
498
- /**
499
- * Find people who own items of a specific brand
500
- */
501
- findPeopleByBrand(brand, category) {
502
- return this.native.findPeopleByBrand(brand, category);
503
- }
504
- /**
505
- * Get popular items (most owned)
506
- */
507
- getPopularItems(category, limit) {
508
- return this.native.getPopularItems(category, limit);
509
- }
510
- // ========================================================================
511
- // Attribute Matching
512
- // ========================================================================
513
- /**
514
- * Find shared attributes between two people
515
- */
516
- findSharedAttributes(personA, personB) {
517
- return this.native.findSharedAttributes(personA, personB);
518
- }
519
- /**
520
- * Find people who share attributes with a given person
521
- */
522
- findPeopleBySharedAttributes(personId, config) {
523
- return this.native.findPeopleBySharedAttributes(personId, config);
524
- }
525
- /**
526
- * Find alumni (people who went to the same school)
527
- */
528
- findAlumni(personId) {
529
- return this.native.findAlumni(personId);
530
- }
531
- /**
532
- * Find people in the same city
533
- */
534
- findSameCity(personId) {
535
- return this.native.findSameCity(personId);
536
- }
537
- /**
538
- * Find people with the same zodiac sign
539
- */
540
- findSameZodiac(personId) {
541
- return this.native.findSameZodiac(personId);
542
- }
543
- /**
544
- * Analyze compatibility between two people
545
- *
546
- * @example
547
- * ```typescript
548
- * const compat = social.analyzeCompatibility(alice.id, bob.id);
549
- * console.log(`Compatibility: ${(compat.overallScore * 100).toFixed(0)}%`);
550
- * console.log(compat.analysis);
551
- * ```
552
- */
553
- analyzeCompatibility(personA, personB) {
554
- return this.native.analyzeCompatibility(personA, personB);
555
- }
556
- /**
557
- * Get popular attributes in a category
558
- */
559
- getPopularAttributes(category, limit) {
560
- return this.native.getPopularAttributes(category, limit);
561
- }
562
- // ========================================================================
563
- // Birthday Management
564
- // ========================================================================
565
- /**
566
- * Set a person's birthday (solar calendar, YYYY-MM-DD format)
567
- *
568
- * @param personId - The person's ID
569
- * @param birthday - Birthday in YYYY-MM-DD format (e.g., "1990-05-15")
570
- * @returns true if set successfully, false if person not found or invalid format
571
- *
572
- * @example
573
- * ```typescript
574
- * social.setBirthday(alice.id, '1990-05-15');
575
- * ```
576
- */
577
- setBirthday(personId, birthday) {
578
- return this.native.setBirthday(personId, birthday);
579
- }
580
- /**
581
- * Get a person's birthday
582
- *
583
- * @returns Birthday in YYYY-MM-DD format, or null if not set
584
- */
585
- getBirthday(personId) {
586
- return this.native.getBirthday(personId) ?? null;
587
- }
588
- /**
589
- * Remove a person's birthday
590
- */
591
- removeBirthday(personId) {
592
- return this.native.removeBirthday(personId);
593
- }
594
- /**
595
- * Find people with exact same birthday (same year-month-day)
596
- *
597
- * @param birthday - Birthday in YYYY-MM-DD format
598
- *
599
- * @example
600
- * ```typescript
601
- * const sameBirthday = social.findSameBirthdayExact('1990-05-15');
602
- * // Returns people born on May 15, 1990
603
- * ```
604
- */
605
- findSameBirthdayExact(birthday) {
606
- return this.native.findSameBirthdayExact(birthday);
607
- }
608
- /**
609
- * Find people with same month-day birthday (different years allowed)
610
- *
611
- * @param birthday - Birthday in YYYY-MM-DD or MM-DD format
612
- *
613
- * @example
614
- * ```typescript
615
- * const sameDayMonth = social.findSameBirthdayDayMonth('05-15');
616
- * // Returns all people born on May 15 (any year)
617
- * ```
618
- */
619
- findSameBirthdayDayMonth(birthday) {
620
- return this.native.findSameBirthdayDayMonth(birthday);
621
- }
622
- /**
623
- * Find people with same birthday as a given person (exact match)
624
- *
625
- * @example
626
- * ```typescript
627
- * const twins = social.findSameBirthdayAsPersonExact(alice.id);
628
- * // Returns people born on the exact same date as Alice
629
- * ```
630
- */
631
- findSameBirthdayAsPersonExact(personId) {
632
- return this.native.findSameBirthdayAsPersonExact(personId);
633
- }
634
- /**
635
- * Find people with same month-day birthday as a given person
636
- *
637
- * @example
638
- * ```typescript
639
- * const sameDayPeople = social.findSameBirthdayAsPerson(alice.id);
640
- * // Returns people who share Alice's birthday month and day
641
- * ```
642
- */
643
- findSameBirthdayAsPerson(personId) {
644
- return this.native.findSameBirthdayAsPerson(personId);
645
- }
646
- /**
647
- * Recommend birthday matches with priority: exact > same_day_month
648
- *
649
- * @example
650
- * ```typescript
651
- * const recommendations = social.recommendBirthdayMatches(alice.id, 10);
652
- * for (const match of recommendations) {
653
- * console.log(`${match.person.name}: ${match.birthday} (${match.matchType})`);
654
- * }
655
- * ```
656
- */
657
- recommendBirthdayMatches(personId, limit) {
658
- return this.native.recommendBirthdayMatches(personId, limit);
659
- }
660
- /**
661
- * Find people with birthdays in the next N days
662
- *
663
- * @example
664
- * ```typescript
665
- * const upcoming = social.findUpcomingBirthdays(7);
666
- * // Returns people with birthdays in the next 7 days
667
- * ```
668
- */
669
- findUpcomingBirthdays(days) {
670
- return this.native.findUpcomingBirthdays(days);
671
- }
672
- /**
673
- * Find people with birthdays today
674
- */
675
- findTodayBirthdays() {
676
- return this.native.findTodayBirthdays();
677
- }
678
- /**
679
- * Find people with birthdays in a specific month
680
- *
681
- * @param month - Month number (1-12)
682
- */
683
- findBirthdaysInMonth(month) {
684
- return this.native.findBirthdaysInMonth(month);
685
- }
686
- /**
687
- * Get birthday statistics
688
- *
689
- * @example
690
- * ```typescript
691
- * const stats = social.getBirthdayStats();
692
- * console.log(`Total: ${stats.totalWithBirthday}`);
693
- * console.log('Most common dates:', stats.mostCommonDates);
694
- * ```
695
- */
696
- getBirthdayStats() {
697
- return this.native.getBirthdayStats();
698
- }
699
- /**
700
- * Check if two people have the same birthday
701
- *
702
- * @param exact - If true, checks for same year-month-day. If false, same month-day only.
703
- *
704
- * @example
705
- * ```typescript
706
- * // Check if same year-month-day
707
- * const exactSame = social.isSameBirthday(alice.id, bob.id, true);
708
- *
709
- * // Check if same month-day (different years allowed)
710
- * const sameDayMonth = social.isSameBirthday(alice.id, bob.id, false);
711
- * ```
712
- */
713
- isSameBirthday(personId1, personId2, exact) {
714
- return this.native.isSameBirthday(personId1, personId2, exact);
715
- }
716
- /**
717
- * Rebuild birthday index from all persons
718
- *
719
- * Call this after loading data to rebuild the in-memory index.
720
- */
721
- rebuildBirthdayIndex() {
722
- this.native.rebuildBirthdayIndex();
723
- }
724
- // ========================================================================
725
- // Graph Export (for visualization)
726
- // ========================================================================
727
- /**
728
- * Export the entire social graph for visualization
729
- *
730
- * Returns nodes (persons, companies, tags) and edges (friend relations, person-tag relations)
731
- * in a format suitable for vis-network or similar graph visualization libraries.
732
- *
733
- * @param options - Export options for filtering and limiting results
734
- * @returns Exported graph data with nodes, edges, and statistics
735
- *
736
- * @example
737
- * ```typescript
738
- * const graph = social.exportGraph();
739
- * console.log(`Nodes: ${graph.nodes.length}, Edges: ${graph.edges.length}`);
740
- * console.log(`Stats:`, graph.stats);
741
- *
742
- * // With options
743
- * const filteredGraph = social.exportGraph({
744
- * maxNodes: 100,
745
- * includeTags: true,
746
- * includeCompanies: true,
747
- * });
748
- * ```
749
- */
750
- exportGraph(options) {
751
- // Try native method first (high-performance Rust implementation)
752
- if (typeof this.native.exportGraph === 'function') {
753
- const nativeOptions = options ? {
754
- max_nodes: options.maxNodes,
755
- max_edges: options.maxEdges,
756
- include_tags: options.includeTags,
757
- include_companies: options.includeCompanies,
758
- include_edge_meta: options.includeEdgeMeta,
759
- include_node_degree: options.includeNodeDegree,
760
- relation_types: options.relationTypes,
761
- } : undefined;
762
- const result = this.native.exportGraph(nativeOptions);
763
- // Transform from snake_case to camelCase if needed
764
- return {
765
- nodes: result.nodes.map((n) => ({
766
- id: n.id,
767
- label: n.label,
768
- group: n.group,
769
- title: n.title,
770
- degree: n.degree,
771
- data: n.data,
772
- })),
773
- edges: result.edges.map((e) => ({
774
- id: e.id,
775
- from: e.from,
776
- to: e.to,
777
- label: e.label,
778
- relationType: e.relation_type,
779
- weight: e.weight,
780
- data: e.data,
781
- })),
782
- stats: {
783
- persons: result.stats.persons,
784
- companies: result.stats.companies,
785
- tags: result.stats.tags,
786
- relations: result.stats.relations,
787
- },
788
- };
789
- }
790
- // Fallback: TypeScript implementation
791
- const opts = {
792
- maxNodes: options?.maxNodes ?? 500,
793
- includeTags: options?.includeTags ?? true,
794
- includeCompanies: options?.includeCompanies ?? true,
795
- includeEdgeMeta: options?.includeEdgeMeta ?? true,
796
- relationTypes: options?.relationTypes,
797
- };
798
- const nodes = [];
799
- const edges = [];
800
- const personIds = new Set();
801
- const tagIds = new Set();
802
- const edgeIds = new Set();
803
- // Helper: determine if a tag is a company
804
- const isCompanyTag = (tag) => {
805
- const props = tag.properties;
806
- return props?.tagType === 'company' || props?.category === 'company';
807
- };
808
- // Helper: get node group for a tag
809
- const getTagGroup = (tag) => {
810
- return isCompanyTag(tag) ? 'company' : 'tag';
811
- };
812
- // Helper: get edge label based on relation type
813
- const getEdgeLabel = (relationType) => {
814
- const labels = {
815
- friend: '好友',
816
- knows: '认识',
817
- colleague: '同事',
818
- classmate: '同学',
819
- family: '家人',
820
- mentor: '导师',
821
- tagged: '标签',
822
- work_for: '就职于',
823
- };
824
- return labels[relationType] || relationType;
825
- };
826
- let nodeCount = 0;
827
- // 1. Get all persons and add person nodes
828
- const persons = this.listPersons();
829
- for (const person of persons) {
830
- if (opts.maxNodes && nodeCount >= opts.maxNodes)
831
- break;
832
- const props = person.properties;
833
- nodes.push({
834
- id: person.id,
835
- label: person.name,
836
- group: 'person',
837
- title: props?.description || person.name,
838
- data: {
839
- type: 'person',
840
- name: person.name,
841
- avatar: props?.avatar,
842
- phone: props?.phone,
843
- email: props?.email,
844
- wechat: props?.wechat,
845
- position: props?.position,
846
- description: props?.description,
847
- ...props,
848
- },
849
- });
850
- personIds.add(person.id);
851
- nodeCount++;
852
- }
853
- // 2. Get all tags (using listTags to include orphan tags)
854
- const allTags = this.listTags();
855
- for (const tag of allTags) {
856
- if (opts.maxNodes && nodeCount >= opts.maxNodes)
857
- break;
858
- const tagGroup = getTagGroup(tag);
859
- // Check if we should include this tag type
860
- if (tagGroup === 'company' && !opts.includeCompanies)
861
- continue;
862
- if (tagGroup === 'tag' && !opts.includeTags)
863
- continue;
864
- const props = tag.properties;
865
- nodes.push({
866
- id: tag.id,
867
- label: tag.name,
868
- group: tagGroup,
869
- title: props?.description || tag.name,
870
- data: {
871
- type: tagGroup,
872
- name: tag.name,
873
- tagType: props?.tagType,
874
- category: props?.category,
875
- color: props?.color,
876
- industry: props?.industry,
877
- description: props?.description,
878
- ...props,
879
- },
880
- });
881
- tagIds.add(tag.id);
882
- nodeCount++;
883
- }
884
- // 3. Get all relations (using listRelations to get full metadata)
885
- const allRelations = this.listRelations();
886
- for (const relation of allRelations) {
887
- // Check relation type filter
888
- if (opts.relationTypes && !opts.relationTypes.includes(relation.relation_type))
889
- continue;
890
- // Skip edges to/from nodes not in our node set
891
- const sourceInNodes = personIds.has(relation.source) || tagIds.has(relation.source);
892
- const targetInNodes = personIds.has(relation.target) || tagIds.has(relation.target);
893
- if (!sourceInNodes || !targetInNodes)
894
- continue;
895
- // Skip duplicate edges (for bidirectional relations)
896
- const edgeKey = [relation.source, relation.target].sort().join('-');
897
- if (edgeIds.has(edgeKey))
898
- continue;
899
- edgeIds.add(edgeKey);
900
- edges.push({
901
- id: relation.id,
902
- from: relation.source,
903
- to: relation.target,
904
- label: getEdgeLabel(relation.relation_type),
905
- relationType: relation.relation_type,
906
- weight: relation.weight,
907
- data: opts.includeEdgeMeta ? {
908
- bidirectional: relation.bidirectional,
909
- ...relation.metadata,
910
- } : undefined,
911
- });
912
- }
913
- // 4. Calculate statistics
914
- const companyCount = allTags.filter(isCompanyTag).length;
915
- const tagCount = allTags.length - companyCount;
916
- const stats = {
917
- persons: persons.length,
918
- companies: opts.includeCompanies ? companyCount : 0,
919
- tags: opts.includeTags ? tagCount : 0,
920
- relations: edges.length,
921
- };
922
- return { nodes, edges, stats };
923
- }
924
- // ========================================================================
925
- // Persistence
926
- // ========================================================================
927
- /**
928
- * Save changes to disk
929
- */
930
- save() {
931
- this.native.save();
932
- }
933
- // ========================================================================
934
- // Diagnostics
935
- // ========================================================================
936
- /**
937
- * Diagnose WAL recovery and database state
938
- *
939
- * Returns diagnostic information about the database including:
940
- * - Current entity/relation counts
941
- * - Shard information
942
- * - Recovery status
943
- *
944
- * @example
945
- * ```typescript
946
- * const diag = social.diagnose();
947
- * console.log('Counts:', diag.counts);
948
- * console.log('Message:', diag.message);
949
- * ```
950
- */
951
- diagnose() {
952
- if (typeof this.native.diagnose === 'function') {
953
- return this.native.diagnose();
954
- }
955
- // Fallback for older versions
956
- const persons = this.listPersons();
957
- const tags = this.listTags();
958
- const relations = this.listRelations();
959
- return {
960
- status: 'ok',
961
- counts: {
962
- persons: persons.length,
963
- tags: tags.length,
964
- relations: relations.length,
965
- },
966
- shards: { count: 0 },
967
- message: `Fallback diagnose: ${persons.length} persons, ${tags.length} tags, ${relations.length} relations`,
968
- };
969
- }
970
- /**
971
- * Force re-recover from WAL files
972
- *
973
- * This can be used to reload data from WAL if the initial recovery failed.
974
- *
975
- * @example
976
- * ```typescript
977
- * const result = social.recover();
978
- * if (result.success) {
979
- * console.log('Recovered:', result.counts);
980
- * } else {
981
- * console.error('Recovery failed:', result.error);
982
- * }
983
- * ```
984
- */
985
- recover() {
986
- if (typeof this.native.recover === 'function') {
987
- return this.native.recover();
988
- }
989
- return {
990
- success: false,
991
- error: 'Native recover method not available',
992
- };
993
- }
994
- }
995
- exports.SocialGraph = SocialGraph;
996
- exports.default = SocialGraph;
997
- //# sourceMappingURL=social-graph.js.map