This repository has been archived on 2025-08-03. You can view files and clone it, but cannot push or open issues or pull requests.
network-orbit/examples/relationship-examples.ts

511 lines
20 KiB
TypeScript

/**
* Comprehensive Relationship Examples for DebrosFramework
*
* This file demonstrates all the relationship loading capabilities implemented in Phase 4:
* - Lazy and eager loading
* - Relationship caching
* - Cross-database relationship resolution
* - Advanced loading with constraints
* - Performance optimization techniques
*/
import { SocialPlatformFramework, User, Post, Comment, Follow } from './framework-integration';
export class RelationshipExamples {
private framework: SocialPlatformFramework;
constructor(framework: SocialPlatformFramework) {
this.framework = framework;
}
async runAllExamples(): Promise<void> {
console.log('🔗 Running comprehensive relationship examples...\n');
await this.basicRelationshipLoading();
await this.eagerLoadingExamples();
await this.lazyLoadingExamples();
await this.constrainedLoadingExamples();
await this.cacheOptimizationExamples();
await this.crossDatabaseRelationships();
await this.performanceExamples();
console.log('✅ All relationship examples completed!\n');
}
async basicRelationshipLoading(): Promise<void> {
console.log('🔗 Basic Relationship Loading');
console.log('==============================\n');
// Get a post and load its author (BelongsTo)
const posts = await Post.where('isPublic', '=', true).limit(3).exec();
if (posts.length > 0) {
const post = posts[0];
console.log(`Loading author for post: ${post.title}`);
const author = await post.loadRelation('author');
console.log(`Author loaded: ${author?.username || 'Unknown'}`);
// Load comments for the post (HasMany)
console.log(`Loading comments for post: ${post.title}`);
const comments = await post.loadRelation('comments');
console.log(`Comments loaded: ${Array.isArray(comments) ? comments.length : 0} comment(s)`);
// Check what relationships are loaded
console.log(`Loaded relationships: ${post.getLoadedRelations().join(', ')}`);
}
// Get a user and load their posts (HasMany)
const users = await User.limit(2).exec();
if (users.length > 0) {
const user = users[0];
console.log(`\nLoading posts for user: ${user.username}`);
const userPosts = await user.loadRelation('posts');
console.log(`Posts loaded: ${Array.isArray(userPosts) ? userPosts.length : 0} post(s)`);
}
console.log('');
}
async eagerLoadingExamples(): Promise<void> {
console.log('⚡ Eager Loading Examples');
console.log('==========================\n');
// Load multiple posts with their authors and comments in one go
console.log('Loading posts with authors and comments (eager loading):');
const posts = await Post
.where('isPublic', '=', true)
.limit(5)
.exec();
if (posts.length > 0) {
// Eager load relationships for all posts at once
const startTime = Date.now();
await posts[0].load(['author', 'comments']);
const singleLoadTime = Date.now() - startTime;
// Now eager load for all posts
const eagerStartTime = Date.now();
await this.framework.relationshipManager.eagerLoadRelationships(
posts,
['author', 'comments']
);
const eagerLoadTime = Date.now() - eagerStartTime;
console.log(`Single post relationship loading: ${singleLoadTime}ms`);
console.log(`Eager loading for ${posts.length} posts: ${eagerLoadTime}ms`);
console.log(`Efficiency gain: ${((singleLoadTime * posts.length) / eagerLoadTime).toFixed(2)}x faster`);
// Verify relationships are loaded
let loadedCount = 0;
for (const post of posts) {
if (post.isRelationLoaded('author') && post.isRelationLoaded('comments')) {
loadedCount++;
}
}
console.log(`Successfully loaded relationships for ${loadedCount}/${posts.length} posts`);
}
// Load users with their posts
console.log('\nLoading users with their posts (eager loading):');
const users = await User.limit(3).exec();
if (users.length > 0) {
await this.framework.relationshipManager.eagerLoadRelationships(
users,
['posts', 'following']
);
for (const user of users) {
const posts = user.getRelation('posts') || [];
const following = user.getRelation('following') || [];
console.log(`User ${user.username}: ${posts.length} posts, ${following.length} following`);
}
}
console.log('');
}
async lazyLoadingExamples(): Promise<void> {
console.log('💤 Lazy Loading Examples');
console.log('=========================\n');
const posts = await Post.where('isPublic', '=', true).limit(2).exec();
if (posts.length > 0) {
const post = posts[0];
console.log('Demonstrating lazy loading behavior:');
console.log(`Post title: ${post.title}`);
console.log(`Author loaded initially: ${post.isRelationLoaded('author')}`);
// First access triggers loading
console.log('Accessing author (triggers lazy loading)...');
const author = await post.loadRelation('author');
console.log(`Author: ${author?.username || 'Unknown'}`);
console.log(`Author loaded after access: ${post.isRelationLoaded('author')}`);
// Second access uses cached value
console.log('Accessing author again (uses cache)...');
const authorAgain = post.getRelation('author');
console.log(`Author (cached): ${authorAgain?.username || 'Unknown'}`);
// Reload relationship (clears cache and reloads)
console.log('Reloading author relationship...');
const reloadedAuthor = await post.reloadRelation('author');
console.log(`Reloaded author: ${reloadedAuthor?.username || 'Unknown'}`);
}
console.log('');
}
async constrainedLoadingExamples(): Promise<void> {
console.log('🎯 Constrained Loading Examples');
console.log('=================================\n');
const posts = await Post.where('isPublic', '=', true).limit(3).exec();
if (posts.length > 0) {
const post = posts[0];
// Load only recent comments
console.log(`Loading recent comments for post: ${post.title}`);
const recentComments = await post.loadRelationWithConstraints('comments', (query) =>
query.where('createdAt', '>', Date.now() - 86400000) // Last 24 hours
.orderBy('createdAt', 'desc')
.limit(5)
);
console.log(`Recent comments loaded: ${Array.isArray(recentComments) ? recentComments.length : 0}`);
// Load comments with minimum length
console.log(`Loading substantive comments (>50 chars):`);
const substantiveComments = await post.loadRelationWithConstraints('comments', (query) =>
query.whereRaw('LENGTH(content) > ?', [50])
.orderBy('createdAt', 'desc')
.limit(3)
);
console.log(`Substantive comments: ${Array.isArray(substantiveComments) ? substantiveComments.length : 0}`);
}
// Load user posts with constraints
const users = await User.limit(2).exec();
if (users.length > 0) {
const user = users[0];
console.log(`\nLoading popular posts for user: ${user.username}`);
const popularPosts = await user.loadRelationWithConstraints('posts', (query) =>
query.where('likeCount', '>', 5)
.where('isPublic', '=', true)
.orderBy('likeCount', 'desc')
.limit(10)
);
console.log(`Popular posts: ${Array.isArray(popularPosts) ? popularPosts.length : 0}`);
}
console.log('');
}
async cacheOptimizationExamples(): Promise<void> {
console.log('🚀 Cache Optimization Examples');
console.log('===============================\n');
// Get cache stats before
const statsBefore = this.framework.relationshipManager.getRelationshipCacheStats();
console.log('Relationship cache stats before:');
console.log(`- Total entries: ${statsBefore.cache.totalEntries}`);
console.log(`- Hit rate: ${(statsBefore.cache.hitRate * 100).toFixed(2)}%`);
// Load relationships multiple times to demonstrate caching
const posts = await Post.where('isPublic', '=', true).limit(3).exec();
if (posts.length > 0) {
console.log('\nLoading relationships multiple times (should hit cache):');
for (let i = 0; i < 3; i++) {
const startTime = Date.now();
await posts[0].loadRelation('author');
await posts[0].loadRelation('comments');
const duration = Date.now() - startTime;
console.log(`Iteration ${i + 1}: ${duration}ms`);
}
}
// Warm up cache
console.log('\nWarming up relationship cache:');
const allPosts = await Post.limit(5).exec();
const allUsers = await User.limit(3).exec();
await this.framework.relationshipManager.warmupRelationshipCache(
allPosts,
['author', 'comments']
);
await this.framework.relationshipManager.warmupRelationshipCache(
allUsers,
['posts', 'following']
);
// Get cache stats after
const statsAfter = this.framework.relationshipManager.getRelationshipCacheStats();
console.log('\nRelationship cache stats after warmup:');
console.log(`- Total entries: ${statsAfter.cache.totalEntries}`);
console.log(`- Hit rate: ${(statsAfter.cache.hitRate * 100).toFixed(2)}%`);
console.log(`- Memory usage: ${(statsAfter.cache.memoryUsage / 1024).toFixed(2)} KB`);
// Show cache performance analysis
const performance = statsAfter.performance;
console.log('\nCache performance analysis:');
console.log(`- Average age: ${(performance.averageAge / 1000).toFixed(2)} seconds`);
console.log(`- Relationship types in cache:`);
performance.relationshipTypes.forEach((count, type) => {
console.log(` * ${type}: ${count} entries`);
});
console.log('');
}
async crossDatabaseRelationships(): Promise<void> {
console.log('🌐 Cross-Database Relationship Examples');
console.log('=========================================\n');
// This demonstrates relationships that span across user databases and global databases
// Get users (stored in global database)
const users = await User.limit(2).exec();
if (users.length >= 2) {
const user1 = users[0];
const user2 = users[1];
console.log(`Loading cross-database relationships:`);
console.log(`User 1: ${user1.username} (global DB)`);
console.log(`User 2: ${user2.username} (global DB)`);
// Load posts for user1 (stored in user1's database)
const user1Posts = await user1.loadRelation('posts');
console.log(`User 1 posts (from user DB): ${Array.isArray(user1Posts) ? user1Posts.length : 0}`);
// Load posts for user2 (stored in user2's database)
const user2Posts = await user2.loadRelation('posts');
console.log(`User 2 posts (from user DB): ${Array.isArray(user2Posts) ? user2Posts.length : 0}`);
// Load followers relationship (stored in global database)
const user1Following = await user1.loadRelation('following');
console.log(`User 1 following (from global DB): ${Array.isArray(user1Following) ? user1Following.length : 0}`);
// Demonstrate the complexity: Post (user DB) -> Author (global DB) -> Posts (back to user DB)
if (Array.isArray(user1Posts) && user1Posts.length > 0) {
const post = user1Posts[0];
console.log(`\nDemonstrating complex cross-DB relationship chain:`);
console.log(`Post: "${post.title}" (from user DB)`);
const author = await post.loadRelation('author');
console.log(`-> Author: ${author?.username || 'Unknown'} (from global DB)`);
if (author) {
const authorPosts = await author.loadRelation('posts');
console.log(`-> Author's posts: ${Array.isArray(authorPosts) ? authorPosts.length : 0} (back to user DB)`);
}
}
}
console.log('');
}
async performanceExamples(): Promise<void> {
console.log('📈 Performance Examples');
console.log('========================\n');
// Compare different loading strategies
const posts = await Post.where('isPublic', '=', true).limit(10).exec();
if (posts.length > 0) {
console.log(`Performance comparison for ${posts.length} posts:\n`);
// Strategy 1: Sequential loading (N+1 problem)
console.log('1. Sequential loading (N+1 queries):');
const sequentialStart = Date.now();
for (const post of posts) {
await post.loadRelation('author');
}
const sequentialTime = Date.now() - sequentialStart;
console.log(` Time: ${sequentialTime}ms (${(sequentialTime / posts.length).toFixed(2)}ms per post)`);
// Clear loaded relationships for fair comparison
posts.forEach(post => {
post._loadedRelations.clear();
});
// Strategy 2: Eager loading (optimal)
console.log('\n2. Eager loading (optimized):');
const eagerStart = Date.now();
await this.framework.relationshipManager.eagerLoadRelationships(posts, ['author']);
const eagerTime = Date.now() - eagerStart;
console.log(` Time: ${eagerTime}ms (${(eagerTime / posts.length).toFixed(2)}ms per post)`);
console.log(` Performance improvement: ${(sequentialTime / eagerTime).toFixed(2)}x faster`);
// Strategy 3: Cached loading (fastest for repeated access)
console.log('\n3. Cached loading (repeated access):');
const cachedStart = Date.now();
await this.framework.relationshipManager.eagerLoadRelationships(posts, ['author']);
const cachedTime = Date.now() - cachedStart;
console.log(` Time: ${cachedTime}ms (cache hit)`);
console.log(` Cache efficiency: ${(eagerTime / Math.max(cachedTime, 1)).toFixed(2)}x faster than first load`);
}
// Memory usage demonstration
console.log('\nMemory usage analysis:');
const memoryStats = this.framework.relationshipManager.getRelationshipCacheStats();
console.log(`- Cache entries: ${memoryStats.cache.totalEntries}`);
console.log(`- Memory usage: ${(memoryStats.cache.memoryUsage / 1024).toFixed(2)} KB`);
console.log(`- Average per entry: ${memoryStats.cache.totalEntries > 0 ? (memoryStats.cache.memoryUsage / memoryStats.cache.totalEntries).toFixed(2) : 0} bytes`);
// Cache cleanup demonstration
console.log('\nCache cleanup:');
const expiredCount = this.framework.relationshipManager.cleanupExpiredCache();
console.log(`- Cleaned up ${expiredCount} expired entries`);
// Model-based invalidation
const invalidatedCount = this.framework.relationshipManager.invalidateModelCache('User');
console.log(`- Invalidated ${invalidatedCount} User-related cache entries`);
console.log('');
}
async demonstrateAdvancedFeatures(): Promise<void> {
console.log('🔬 Advanced Relationship Features');
console.log('==================================\n');
const posts = await Post.where('isPublic', '=', true).limit(3).exec();
if (posts.length > 0) {
const post = posts[0];
// Demonstrate conditional loading
console.log('Conditional relationship loading:');
if (!post.isRelationLoaded('author')) {
console.log('- Author not loaded, loading now...');
await post.loadRelation('author');
} else {
console.log('- Author already loaded, using cached version');
}
// Demonstrate partial loading with pagination
console.log('\nPaginated relationship loading:');
const page1Comments = await post.loadRelationWithConstraints('comments', (query) =>
query.orderBy('createdAt', 'desc').limit(5).offset(0)
);
console.log(`- Page 1: ${Array.isArray(page1Comments) ? page1Comments.length : 0} comments`);
const page2Comments = await post.loadRelationWithConstraints('comments', (query) =>
query.orderBy('createdAt', 'desc').limit(5).offset(5)
);
console.log(`- Page 2: ${Array.isArray(page2Comments) ? page2Comments.length : 0} comments`);
// Demonstrate relationship statistics
console.log('\nRelationship loading statistics:');
const modelClass = post.constructor as any;
const relationships = Array.from(modelClass.relationships?.keys() || []);
console.log(`- Available relationships: ${relationships.join(', ')}`);
console.log(`- Currently loaded: ${post.getLoadedRelations().join(', ')}`);
}
console.log('');
}
}
// Usage example
export async function runRelationshipExamples(
orbitDBService: any,
ipfsService: any
): Promise<void> {
const framework = new SocialPlatformFramework();
try {
await framework.initialize(orbitDBService, ipfsService, 'development');
// Ensure we have sample data
await createSampleDataForRelationships(framework);
// Run relationship examples
const examples = new RelationshipExamples(framework);
await examples.runAllExamples();
await examples.demonstrateAdvancedFeatures();
// Show final relationship cache statistics
const finalStats = framework.relationshipManager.getRelationshipCacheStats();
console.log('📊 Final Relationship Cache Statistics:');
console.log(JSON.stringify(finalStats, null, 2));
} catch (error) {
console.error('❌ Relationship examples failed:', error);
} finally {
await framework.stop();
}
}
async function createSampleDataForRelationships(framework: SocialPlatformFramework): Promise<void> {
console.log('🗄️ Creating sample data for relationship examples...\n');
try {
// Create users
const alice = await framework.createUser({
username: 'alice',
email: 'alice@example.com',
bio: 'Framework developer and relationship expert'
});
const bob = await framework.createUser({
username: 'bob',
email: 'bob@example.com',
bio: 'Database architect'
});
const charlie = await framework.createUser({
username: 'charlie',
email: 'charlie@example.com',
bio: 'Performance optimization specialist'
});
// Create posts with relationships
const post1 = await framework.createPost(alice.id, {
title: 'Understanding Relationships in Distributed Databases',
content: 'Relationships across distributed databases present unique challenges...',
tags: ['relationships', 'distributed', 'databases'],
isPublic: true
});
const post2 = await framework.createPost(bob.id, {
title: 'Optimizing Cross-Database Queries',
content: 'When data spans multiple databases, query optimization becomes crucial...',
tags: ['optimization', 'queries', 'performance'],
isPublic: true
});
const post3 = await framework.createPost(alice.id, {
title: 'Caching Strategies for Relationships',
content: 'Effective caching can dramatically improve relationship loading performance...',
tags: ['caching', 'performance', 'relationships'],
isPublic: true
});
// Create comments to establish relationships
await framework.createComment(bob.id, post1.id, 'Great explanation of the distributed relationship challenges!');
await framework.createComment(charlie.id, post1.id, 'This helped me understand the complexity involved.');
await framework.createComment(alice.id, post2.id, 'Excellent optimization techniques, Bob!');
await framework.createComment(charlie.id, post2.id, 'These optimizations improved our app performance by 3x.');
await framework.createComment(bob.id, post3.id, 'Caching relationships was a game-changer for our system.');
// Create follow relationships
await framework.followUser(alice.id, bob.id);
await framework.followUser(bob.id, charlie.id);
await framework.followUser(charlie.id, alice.id);
await framework.followUser(alice.id, charlie.id);
console.log('✅ Sample relationship data created successfully!\n');
} catch (error) {
console.warn('⚠️ Some sample data creation failed:', error);
}
}