Skip to main content

Chapter 6: Graph Operations and Relationship Modeling

Overview

Graph operations and relationship modeling are fundamental to modern applications, enabling complex data relationships, social networks, recommendation systems, and knowledge graphs. Vektagraf provides a comprehensive graph system that seamlessly integrates with its object-centric design, allowing you to model and traverse relationships naturally while maintaining performance and security. This chapter covers graph concepts, relationship modeling patterns, traversal operations, and advanced graph algorithms.

Learning Objectives

By the end of this chapter, you will be able to:

  • Model complex relationships using Vektagraf's graph system
  • Perform efficient graph traversals and pathfinding operations
  • Implement social network features with relationship-based queries
  • Build recommendation systems using graph algorithms
  • Optimize graph operations for performance and scalability
  • Secure graph operations with relationship-level access control
  • Analyze graph structures and extract insights from connected data

Prerequisites

  • Completed Chapter 4: Database Operations and Transactions
  • Completed Chapter 5: Vector Search and Similarity Operations
  • Basic understanding of graph theory concepts (nodes, edges, paths)
  • Familiarity with social network and recommendation system concepts

Core Concepts

Graph Model in Vektagraf

Vektagraf implements a property graph model where:

  • Nodes represent entities with properties and labels
  • Edges represent relationships with types and properties
  • Labels categorize nodes (e.g., "User", "Product", "Document")
  • Types categorize edges (e.g., "FRIENDS_WITH", "PURCHASED", "AUTHORED")
  • Properties store additional data on both nodes and edges

Object-Graph Integration

Unlike traditional graph databases, Vektagraf automatically creates graph nodes for your objects:

// When you save an object, a graph node is automatically created
final user = User(
  name: 'Alice Johnson',
  email: 'alice@example.com',
  interests: ['technology', 'music'],
);

final userId = await users.save(user);
// Graph node created automatically with labels: ['User']
// Properties: {name: 'Alice Johnson', email: 'alice@example.com', ...}

Relationship Types

Vektagraf supports various relationship patterns:

  1. One-to-One: User ↔ Profile
  2. One-to-Many: User → Posts
  3. Many-to-Many: Users ↔ Groups
  4. Hierarchical: Categories → Subcategories
  5. Temporal: Events → Timeline
  6. Weighted: Friendship strength, similarity scores

Practical Examples

Basic Graph Operations

Creating Nodes and Edges

import 'package:vektagraf/vektagraf.dart';

Future<void> basicGraphOperations() async {
  final database = VektagrafDatabaseImpl();
  await database.open('social_graph.db');
  
  try {
    final graph = database.graph;
    
    // Create nodes with labels and properties
    final aliceId = await graph.addNode({
      'name': 'Alice Johnson',
      'age': 30,
      'city': 'San Francisco',
      'occupation': 'Software Engineer',
    }, labels: {'User', 'Employee'});
    
    final bobId = await graph.addNode({
      'name': 'Bob Smith',
      'age': 28,
      'city': 'New York',
      'occupation': 'Data Scientist',
    }, labels: {'User', 'Employee'});
    
    final companyId = await graph.addNode({
      'name': 'TechCorp Inc',
      'industry': 'Technology',
      'founded': 2010,
      'employees': 5000,
    }, labels: {'Company', 'Organization'});
    
    print('Created nodes: Alice($aliceId), Bob($bobId), Company($companyId)');
    
    // Create relationships between nodes
    final friendshipId = await graph.addEdge(
      aliceId,
      bobId,
      type: 'FRIENDS_WITH',
      properties: {
        'since': DateTime(2020, 1, 15),
        'strength': 0.8,
        'met_through': 'college',
      },
    );
    
    final aliceEmploymentId = await graph.addEdge(
      aliceId,
      companyId,
      type: 'WORKS_FOR',
      properties: {
        'position': 'Senior Software Engineer',
        'start_date': DateTime(2021, 3, 1),
        'department': 'Engineering',
        'salary_band': 'L5',
      },
    );
    
    final bobEmploymentId = await graph.addEdge(
      bobId,
      companyId,
      type: 'WORKS_FOR',
      properties: {
        'position': 'Data Scientist',
        'start_date': DateTime(2022, 1, 15),
        'department': 'Analytics',
        'salary_band': 'L4',
      },
    );
    
    print('Created relationships:');
    print('  - Friendship: $friendshipId');
    print('  - Alice employment: $aliceEmploymentId');
    print('  - Bob employment: $bobEmploymentId');
    
    // Query nodes and edges
    final aliceNode = await graph.getNode(aliceId);
    print('\nAlice node:');
    print('  Labels: ${aliceNode?.labels}');
    print('  Properties: ${aliceNode?.properties}');
    
    final friendshipEdge = await graph.getEdge(friendshipId);
    print('\nFriendship edge:');
    print('  Type: ${friendshipEdge?.type}');
    print('  Properties: ${friendshipEdge?.properties}');
    
  } finally {
    await database.close();
  }
}

Graph Traversal Operations

Future<void> graphTraversalExample() async {
  final database = VektagrafDatabaseImpl();
  await database.open('social_graph.db');
  
  try {
    final graph = database.graph;
    
    // Create a more complex social network
    final users = <VektagrafId>[];
    final userNames = ['Alice', 'Bob', 'Carol', 'David', 'Eve', 'Frank'];
    
    // Create user nodes
    for (final name in userNames) {
      final userId = await graph.addNode({
        'name': name,
        'age': 25 + Random().nextInt(15),
        'interests': _generateInterests(),
        'join_date': DateTime.now().subtract(Duration(days: Random().nextInt(365))),
      }, labels: {'User'});
      
      users.add(userId);
    }
    
    // Create friendship network
    final friendships = <String>[];
    for (int i = 0; i < users.length; i++) {
      for (int j = i + 1; j < users.length; j++) {
        // Create some friendships (not everyone is friends with everyone)
        if (Random().nextDouble() < 0.4) {
          await graph.addEdge(
            users[i],
            users[j],
            type: 'FRIENDS_WITH',
            properties: {
              'since': DateTime.now().subtract(Duration(days: Random().nextInt(200))),
              'strength': Random().nextDouble(),
            },
          );
          friendships.add('${userNames[i]} ↔ ${userNames[j]}');
        }
      }
    }
    
    print('Created social network:');
    print('  Users: ${userNames.join(', ')}');
    print('  Friendships: ${friendships.join(', ')}');
    
    // Breadth-first traversal from Alice
    print('\n🔍 Breadth-first traversal from Alice:');
    final aliceId = users[0]; // Alice is first
    
    await for (final node in graph.traverse(aliceId)) {
      final name = node.getProperty<String>('name');
      final age = node.getProperty<int>('age');
      print('  - $name (age $age)');
    }
    
    // Find shortest path between two users
    print('\n🛤️  Shortest path from Alice to Frank:');
    final frankId = users[5]; // Frank is last
    final shortestPath = await graph.shortestPath(aliceId, frankId);
    
    if (shortestPath != null) {
      final pathNames = shortestPath
          .map((node) => node.getProperty<String>('name'))
          .join(' → ');
      print('  Path: $pathNames (${shortestPath.length} hops)');
    } else {
      print('  No path found between Alice and Frank');
    }
    
    // Find all users with specific labels
    print('\n👥 All users in the network:');
    final allUsers = await graph.getNodesByLabel('User');
    for (final user in allUsers) {
      final name = user.getProperty<String>('name');
      final interests = user.getProperty<List<dynamic>>('interests');
      print('  - $name: ${interests?.join(', ')}');
    }
    
    // Find all friendship edges
    print('\n🤝 All friendships:');
    final friendshipEdges = await graph.getEdgesByType('FRIENDS_WITH');
    for (final edge in friendshipEdges) {
      final fromNode = await edge.fromNode;
      final toNode = await edge.toNode;
      final strength = edge.getProperty<double>('strength');
      
      print('  - ${fromNode?.getProperty('name')} ↔ ${toNode?.getProperty('name')} '
            '(strength: ${strength?.toStringAsFixed(2)})');
    }
    
  } finally {
    await database.close();
  }
}

List<String> _generateInterests() {
  final allInterests = [
    'technology', 'music', 'sports', 'reading', 'travel', 'cooking',
    'photography', 'gaming', 'art', 'fitness', 'movies', 'science'
  ];
  
  final count = 2 + Random().nextInt(4); // 2-5 interests
  final selected = <String>[];
  
  while (selected.length < count) {
    final interest = allInterests[Random().nextInt(allInterests.length)];
    if (!selected.contains(interest)) {
      selected.add(interest);
    }
  }
  
  return selected;
}

Advanced Relationship Modeling

Social Network Implementation

class SocialNetworkGraph {
  final VektagrafDatabase database;
  late VektagrafGraph graph;
  
  SocialNetworkGraph(this.database) {
    graph = database.graph;
  }
  
  /// Creates a user profile with automatic graph node creation
  Future<VektagrafId> createUser({
    required String username,
    required String email,
    required String displayName,
    String? bio,
    List<String>? interests,
    Map<String, dynamic>? metadata,
  }) async {
    return await graph.addNode({
      'username': username,
      'email': email,
      'display_name': displayName,
      'bio': bio ?? '',
      'interests': interests ?? [],
      'created_at': DateTime.now().toIso8601String(),
      'last_active': DateTime.now().toIso8601String(),
      'follower_count': 0,
      'following_count': 0,
      'post_count': 0,
      ...?metadata,
    }, labels: {'User', 'Profile'});
  }
  
  /// Creates a follow relationship between users
  Future<VektagrafId> followUser(VektagrafId followerId, VektagrafId followeeId) async {
    // Check if relationship already exists
    final existingFollow = await _findExistingRelationship(
      followerId, followeeId, 'FOLLOWS'
    );
    
    if (existingFollow != null) {
      throw StateError('User is already following this person');
    }
    
    // Create follow relationship
    final followEdgeId = await graph.addEdge(
      followerId,
      followeeId,
      type: 'FOLLOWS',
      properties: {
        'created_at': DateTime.now().toIso8601String(),
        'notification_enabled': true,
      },
    );
    
    // Update follower/following counts
    await _updateFollowCounts(followerId, followeeId);
    
    return followEdgeId;
  }
  
  /// Creates a mutual friendship (bidirectional relationship)
  Future<List<VektagrafId>> createFriendship(
    VektagrafId user1Id, 
    VektagrafId user2Id,
  ) async {
    final friendshipData = {
      'created_at': DateTime.now().toIso8601String(),
      'strength': 1.0,
      'interaction_count': 0,
    };
    
    // Create bidirectional friendship edges
    final edge1 = await graph.addEdge(
      user1Id, user2Id,
      type: 'FRIENDS_WITH',
      properties: friendshipData,
    );
    
    final edge2 = await graph.addEdge(
      user2Id, user1Id,
      type: 'FRIENDS_WITH',
      properties: friendshipData,
    );
    
    return [edge1, edge2];
  }
  
  /// Gets a user's social network (friends and followers)
  Future<SocialNetwork> getUserSocialNetwork(VektagrafId userId) async {
    final user = await graph.getNode(userId);
    if (user == null) {
      throw ArgumentError('User not found: $userId');
    }
    
    // Get friends (bidirectional FRIENDS_WITH relationships)
    final friends = <VektagrafNode>[];
    await for (final friend in user.connectedNodesOfType('FRIENDS_WITH')) {
      friends.add(friend);
    }
    
    // Get followers (incoming FOLLOWS relationships)
    final followers = <VektagrafNode>[];
    final incomingFollows = await user.incomingOfType('FOLLOWS');
    for (final edge in incomingFollows) {
      final follower = await edge.fromNode;
      if (follower != null) followers.add(follower);
    }
    
    // Get following (outgoing FOLLOWS relationships)
    final following = <VektagrafNode>[];
    final outgoingFollows = await user.outgoingOfType('FOLLOWS');
    for (final edge in outgoingFollows) {
      final followee = await edge.toNode;
      if (followee != null) following.add(followee);
    }
    
    return SocialNetwork(
      user: user,
      friends: friends,
      followers: followers,
      following: following,
    );
  }
  
  /// Finds mutual friends between two users
  Future<List<VektagrafNode>> findMutualFriends(
    VektagrafId user1Id, 
    VektagrafId user2Id,
  ) async {
    final user1Network = await getUserSocialNetwork(user1Id);
    final user2Network = await getUserSocialNetwork(user2Id);
    
    final user1FriendIds = user1Network.friends.map((f) => f.id).toSet();
    final user2FriendIds = user2Network.friends.map((f) => f.id).toSet();
    
    final mutualFriendIds = user1FriendIds.intersection(user2FriendIds);
    
    final mutualFriends = <VektagrafNode>[];
    for (final friendId in mutualFriendIds) {
      final friend = await graph.getNode(friendId);
      if (friend != null) mutualFriends.add(friend);
    }
    
    return mutualFriends;
  }
  
  /// Suggests friends based on mutual connections and interests
  Future<List<FriendSuggestion>> suggestFriends(
    VektagrafId userId, {
    int limit = 10,
  }) async {
    final userNetwork = await getUserSocialNetwork(userId);
    final user = userNetwork.user;
    final userInterests = user.getProperty<List<dynamic>>('interests') ?? [];
    
    final suggestions = <FriendSuggestion>[];
    final processedUsers = <VektagrafId>{userId};
    
    // Add current friends to processed set
    for (final friend in userNetwork.friends) {
      processedUsers.add(friend.id);
    }
    
    // Find friends of friends
    for (final friend in userNetwork.friends) {
      final friendNetwork = await getUserSocialNetwork(friend.id);
      
      for (final friendOfFriend in friendNetwork.friends) {
        if (processedUsers.contains(friendOfFriend.id)) continue;
        
        processedUsers.add(friendOfFriend.id);
        
        // Calculate suggestion score
        final mutualFriends = await findMutualFriends(userId, friendOfFriend.id);
        final friendInterests = friendOfFriend.getProperty<List<dynamic>>('interests') ?? [];
        
        final commonInterests = userInterests
            .where((interest) => friendInterests.contains(interest))
            .length;
        
        final score = (mutualFriends.length * 0.7) + (commonInterests * 0.3);
        
        if (score > 0) {
          suggestions.add(FriendSuggestion(
            user: friendOfFriend,
            score: score,
            mutualFriends: mutualFriends,
            commonInterests: commonInterests,
            reason: _generateSuggestionReason(mutualFriends.length, commonInterests),
          ));
        }
      }
    }
    
    // Sort by score and return top suggestions
    suggestions.sort((a, b) => b.score.compareTo(a.score));
    return suggestions.take(limit).toList();
  }
  
  /// Analyzes the social graph structure
  Future<SocialGraphAnalytics> analyzeSocialGraph() async {
    final allUsers = await graph.getNodesByLabel('User');
    final allFriendships = await graph.getEdgesByType('FRIENDS_WITH');
    final allFollows = await graph.getEdgesByType('FOLLOWS');
    
    // Calculate network density
    final maxPossibleEdges = allUsers.length * (allUsers.length - 1);
    final actualEdges = allFriendships.length + allFollows.length;
    final density = maxPossibleEdges > 0 ? actualEdges / maxPossibleEdges : 0.0;
    
    // Find most connected users
    final userConnections = <VektagrafId, int>{};
    for (final user in allUsers) {
      final network = await getUserSocialNetwork(user.id);
      final totalConnections = network.friends.length + 
                              network.followers.length + 
                              network.following.length;
      userConnections[user.id] = totalConnections;
    }
    
    final sortedUsers = userConnections.entries.toList()
      ..sort((a, b) => b.value.compareTo(a.value));
    
    final topInfluencers = <UserInfluence>[];
    for (final entry in sortedUsers.take(10)) {
      final user = await graph.getNode(entry.key);
      if (user != null) {
        topInfluencers.add(UserInfluence(
          user: user,
          connectionCount: entry.value,
          influenceScore: _calculateInfluenceScore(user, entry.value),
        ));
      }
    }
    
    return SocialGraphAnalytics(
      totalUsers: allUsers.length,
      totalFriendships: allFriendships.length ~/ 2, // Bidirectional
      totalFollows: allFollows.length,
      networkDensity: density,
      averageConnections: userConnections.values.isEmpty 
          ? 0.0 
          : userConnections.values.reduce((a, b) => a + b) / userConnections.length,
      topInfluencers: topInfluencers,
    );
  }
  
  // Helper methods
  
  Future<VektagrafEdge?> _findExistingRelationship(
    VektagrafId fromId, 
    VektagrafId toId, 
    String type,
  ) async {
    final fromNode = await graph.getNode(fromId);
    if (fromNode == null) return null;
    
    final outgoingEdges = await fromNode.outgoingOfType(type);
    for (final edge in outgoingEdges) {
      if (edge.toId == toId) return edge;
    }
    
    return null;
  }
  
  Future<void> _updateFollowCounts(VektagrafId followerId, VektagrafId followeeId) async {
    final follower = await graph.getNode(followerId);
    final followee = await graph.getNode(followeeId);
    
    if (follower != null) {
      final currentCount = follower.getProperty<int>('following_count') ?? 0;
      await follower.setProperty('following_count', currentCount + 1);
    }
    
    if (followee != null) {
      final currentCount = followee.getProperty<int>('follower_count') ?? 0;
      await followee.setProperty('follower_count', currentCount + 1);
    }
  }
  
  String _generateSuggestionReason(int mutualFriends, int commonInterests) {
    if (mutualFriends > 0 && commonInterests > 0) {
      return '$mutualFriends mutual friends, $commonInterests shared interests';
    } else if (mutualFriends > 0) {
      return '$mutualFriends mutual friends';
    } else if (commonInterests > 0) {
      return '$commonInterests shared interests';
    } else {
      return 'Suggested based on network analysis';
    }
  }
  
  double _calculateInfluenceScore(VektagrafNode user, int connections) {
    // Simple influence calculation - could be more sophisticated
    final followerCount = user.getProperty<int>('follower_count') ?? 0;
    final postCount = user.getProperty<int>('post_count') ?? 0;
    
    return (followerCount * 0.5) + (connections * 0.3) + (postCount * 0.2);
  }
}

// Supporting classes

class SocialNetwork {
  final VektagrafNode user;
  final List<VektagrafNode> friends;
  final List<VektagrafNode> followers;
  final List<VektagrafNode> following;
  
  SocialNetwork({
    required this.user,
    required this.friends,
    required this.followers,
    required this.following,
  });
}

class FriendSuggestion {
  final VektagrafNode user;
  final double score;
  final List<VektagrafNode> mutualFriends;
  final int commonInterests;
  final String reason;
  
  FriendSuggestion({
    required this.user,
    required this.score,
    required this.mutualFriends,
    required this.commonInterests,
    required this.reason,
  });
}

class UserInfluence {
  final VektagrafNode user;
  final int connectionCount;
  final double influenceScore;
  
  UserInfluence({
    required this.user,
    required this.connectionCount,
    required this.influenceScore,
  });
}

class SocialGraphAnalytics {
  final int totalUsers;
  final int totalFriendships;
  final int totalFollows;
  final double networkDensity;
  final double averageConnections;
  final List<UserInfluence> topInfluencers;
  
  SocialGraphAnalytics({
    required this.totalUsers,
    required this.totalFriendships,
    required this.totalFollows,
    required this.networkDensity,
    required this.averageConnections,
    required this.topInfluencers,
  });
}
```#
### Content Recommendation System

```dart
class ContentRecommendationGraph {
  final VektagrafDatabase database;
  late VektagrafGraph graph;
  
  ContentRecommendationGraph(this.database) {
    graph = database.graph;
  }
  
  /// Creates a content item with automatic categorization
  Future<VektagrafId> createContent({
    required String title,
    required String content,
    required String contentType,
    required VektagrafId authorId,
    List<String>? tags,
    List<String>? categories,
    Map<String, dynamic>? metadata,
  }) async {
    final contentId = await graph.addNode({
      'title': title,
      'content': content,
      'content_type': contentType,
      'author_id': authorId.toString(),
      'tags': tags ?? [],
      'categories': categories ?? [],
      'created_at': DateTime.now().toIso8601String(),
      'view_count': 0,
      'like_count': 0,
      'share_count': 0,
      ...?metadata,
    }, labels: {'Content', contentType});
    
    // Create authorship relationship
    await graph.addEdge(
      authorId,
      contentId,
      type: 'AUTHORED',
      properties: {
        'created_at': DateTime.now().toIso8601String(),
        'role': 'primary_author',
      },
    );
    
    // Create category relationships
    if (categories != null) {
      for (final category in categories) {
        final categoryId = await _getOrCreateCategory(category);
        await graph.addEdge(
          contentId,
          categoryId,
          type: 'BELONGS_TO',
          properties: {'relevance_score': 1.0},
        );
      }
    }
    
    return contentId;
  }
  
  /// Records user interaction with content
  Future<void> recordInteraction({
    required VektagrafId userId,
    required VektagrafId contentId,
    required String interactionType,
    double? duration,
    double? rating,
    Map<String, dynamic>? metadata,
  }) async {
    // Create or update interaction edge
    final existingInteraction = await _findExistingInteraction(userId, contentId);
    
    if (existingInteraction != null) {
      // Update existing interaction
      final currentCount = existingInteraction.getProperty<int>('interaction_count') ?? 0;
      await existingInteraction.setProperty('interaction_count', currentCount + 1);
      await existingInteraction.setProperty('last_interaction', DateTime.now().toIso8601String());
      
      if (duration != null) {
        final totalDuration = existingInteraction.getProperty<double>('total_duration') ?? 0.0;
        await existingInteraction.setProperty('total_duration', totalDuration + duration);
      }
      
      if (rating != null) {
        await existingInteraction.setProperty('rating', rating);
      }
    } else {
      // Create new interaction
      await graph.addEdge(
        userId,
        contentId,
        type: 'INTERACTED_WITH',
        properties: {
          'interaction_type': interactionType,
          'first_interaction': DateTime.now().toIso8601String(),
          'last_interaction': DateTime.now().toIso8601String(),
          'interaction_count': 1,
          'total_duration': duration ?? 0.0,
          'rating': rating,
          ...?metadata,
        },
      );
    }
    
    // Update content engagement metrics
    await _updateContentMetrics(contentId, interactionType);
  }
  
  /// Generates personalized content recommendations
  Future<List<ContentRecommendation>> getPersonalizedRecommendations(
    VektagrafId userId, {
    int limit = 20,
    List<String>? contentTypes,
    double minScore = 0.1,
  }) async {
    final user = await graph.getNode(userId);
    if (user == null) {
      throw ArgumentError('User not found: $userId');
    }
    
    final recommendations = <ContentRecommendation>[];
    final processedContent = <VektagrafId>{};
    
    // Get user's interaction history
    final userInteractions = await user.outgoingOfType('INTERACTED_WITH');
    final interactedContentIds = userInteractions.map((e) => e.toId).toSet();
    
    // Strategy 1: Collaborative filtering - users with similar tastes
    final similarUsers = await _findSimilarUsers(userId);
    for (final similarUser in similarUsers.take(10)) {
      final similarUserInteractions = await similarUser.user.outgoingOfType('INTERACTED_WITH');
      
      for (final interaction in similarUserInteractions) {
        if (interactedContentIds.contains(interaction.toId) || 
            processedContent.contains(interaction.toId)) continue;
        
        final content = await interaction.toNode;
        if (content == null) continue;
        
        // Filter by content type if specified
        if (contentTypes != null && 
            !contentTypes.contains(content.getProperty<String>('content_type'))) continue;
        
        final rating = interaction.getProperty<double>('rating') ?? 0.5;
        final score = similarUser.similarity * rating * 0.7; // Collaborative filtering weight
        
        if (score >= minScore) {
          recommendations.add(ContentRecommendation(
            content: content,
            score: score,
            reason: 'Users with similar interests liked this',
            strategy: 'collaborative_filtering',
          ));
          processedContent.add(content.id);
        }
      }
    }
    
    // Strategy 2: Content-based filtering - similar content categories
    for (final interaction in userInteractions) {
      final content = await interaction.toNode;
      if (content == null) continue;
      
      final rating = interaction.getProperty<double>('rating') ?? 0.5;
      if (rating < 0.6) continue; // Only use positively rated content
      
      // Find similar content through categories
      final categoryEdges = await content.outgoingOfType('BELONGS_TO');
      for (final categoryEdge in categoryEdges) {
        final category = await categoryEdge.toNode;
        if (category == null) continue;
        
        // Find other content in the same category
        final categoryContent = await category.incomingOfType('BELONGS_TO');
        for (final contentEdge in categoryContent) {
          if (interactedContentIds.contains(contentEdge.fromId) || 
              processedContent.contains(contentEdge.fromId)) continue;
          
          final similarContent = await contentEdge.fromNode;
          if (similarContent == null) continue;
          
          // Filter by content type if specified
          if (contentTypes != null && 
              !contentTypes.contains(similarContent.getProperty<String>('content_type'))) continue;
          
          final relevanceScore = categoryEdge.getProperty<double>('relevance_score') ?? 1.0;
          final score = rating * relevanceScore * 0.5; // Content-based weight
          
          if (score >= minScore) {
            recommendations.add(ContentRecommendation(
              content: similarContent,
              score: score,
              reason: 'Similar to content you liked in ${category.getProperty<String>('name')}',
              strategy: 'content_based',
            ));
            processedContent.add(similarContent.id);
          }
        }
      }
    }
    
    // Strategy 3: Trending content - popular among similar users
    final trendingContent = await _getTrendingContent(userId, contentTypes);
    for (final trending in trendingContent) {
      if (interactedContentIds.contains(trending.content.id) || 
          processedContent.contains(trending.content.id)) continue;
      
      if (trending.score >= minScore) {
        recommendations.add(ContentRecommendation(
          content: trending.content,
          score: trending.score * 0.3, // Trending weight
          reason: 'Trending among users like you',
          strategy: 'trending',
        ));
        processedContent.add(trending.content.id);
      }
    }
    
    // Sort by score and return top recommendations
    recommendations.sort((a, b) => b.score.compareTo(a.score));
    return recommendations.take(limit).toList();
  }
  
  /// Finds content discovery paths through the graph
  Future<List<ContentPath>> discoverContentPaths(
    VektagrafId userId,
    VektagrafId targetContentId, {
    int maxDepth = 4,
  }) async {
    final paths = <ContentPath>[];
    
    // Find paths through different relationship types
    await _findPathsRecursive(
      userId,
      targetContentId,
      <VektagrafId>{},
      [userId],
      paths,
      maxDepth,
      ['INTERACTED_WITH', 'AUTHORED', 'BELONGS_TO', 'FRIENDS_WITH'],
    );
    
    // Score and rank paths
    for (final path in paths) {
      path.score = _calculatePathScore(path);
    }
    
    paths.sort((a, b) => b.score.compareTo(a.score));
    return paths;
  }
  
  /// Analyzes content performance and engagement patterns
  Future<ContentAnalytics> analyzeContentPerformance(VektagrafId contentId) async {
    final content = await graph.getNode(contentId);
    if (content == null) {
      throw ArgumentError('Content not found: $contentId');
    }
    
    // Get all interactions with this content
    final interactions = await content.incomingOfType('INTERACTED_WITH');
    
    final analytics = ContentAnalytics(
      contentId: contentId,
      title: content.getProperty<String>('title') ?? 'Unknown',
      totalInteractions: interactions.length,
      uniqueUsers: interactions.map((i) => i.fromId).toSet().length,
      averageRating: 0.0,
      totalDuration: 0.0,
      interactionTypes: {},
      engagementOverTime: [],
    );
    
    // Calculate metrics
    double totalRating = 0.0;
    int ratedInteractions = 0;
    
    for (final interaction in interactions) {
      final rating = interaction.getProperty<double>('rating');
      if (rating != null) {
        totalRating += rating;
        ratedInteractions++;
      }
      
      final duration = interaction.getProperty<double>('total_duration') ?? 0.0;
      analytics.totalDuration += duration;
      
      final interactionType = interaction.getProperty<String>('interaction_type') ?? 'unknown';
      analytics.interactionTypes[interactionType] = 
          (analytics.interactionTypes[interactionType] ?? 0) + 1;
    }
    
    analytics.averageRating = ratedInteractions > 0 ? totalRating / ratedInteractions : 0.0;
    
    return analytics;
  }
  
  // Helper methods
  
  Future<VektagrafId> _getOrCreateCategory(String categoryName) async {
    final existingCategories = await graph.getNodesByLabel('Category');
    for (final category in existingCategories) {
      if (category.getProperty<String>('name') == categoryName) {
        return category.id;
      }
    }
    
    // Create new category
    return await graph.addNode({
      'name': categoryName,
      'created_at': DateTime.now().toIso8601String(),
      'content_count': 0,
    }, labels: {'Category'});
  }
  
  Future<VektagrafEdge?> _findExistingInteraction(
    VektagrafId userId, 
    VektagrafId contentId,
  ) async {
    final user = await graph.getNode(userId);
    if (user == null) return null;
    
    final interactions = await user.outgoingOfType('INTERACTED_WITH');
    for (final interaction in interactions) {
      if (interaction.toId == contentId) return interaction;
    }
    
    return null;
  }
  
  Future<void> _updateContentMetrics(VektagrafId contentId, String interactionType) async {
    final content = await graph.getNode(contentId);
    if (content == null) return;
    
    switch (interactionType) {
      case 'view':
        final currentCount = content.getProperty<int>('view_count') ?? 0;
        await content.setProperty('view_count', currentCount + 1);
        break;
      case 'like':
        final currentCount = content.getProperty<int>('like_count') ?? 0;
        await content.setProperty('like_count', currentCount + 1);
        break;
      case 'share':
        final currentCount = content.getProperty<int>('share_count') ?? 0;
        await content.setProperty('share_count', currentCount + 1);
        break;
    }
  }
  
  Future<List<SimilarUser>> _findSimilarUsers(VektagrafId userId) async {
    // Simplified similarity calculation based on common interactions
    final user = await graph.getNode(userId);
    if (user == null) return [];
    
    final userInteractions = await user.outgoingOfType('INTERACTED_WITH');
    final userContentIds = userInteractions.map((i) => i.toId).toSet();
    
    final allUsers = await graph.getNodesByLabel('User');
    final similarUsers = <SimilarUser>[];
    
    for (final otherUser in allUsers) {
      if (otherUser.id == userId) continue;
      
      final otherInteractions = await otherUser.outgoingOfType('INTERACTED_WITH');
      final otherContentIds = otherInteractions.map((i) => i.toId).toSet();
      
      final commonContent = userContentIds.intersection(otherContentIds);
      final totalContent = userContentIds.union(otherContentIds);
      
      if (totalContent.isNotEmpty) {
        final similarity = commonContent.length / totalContent.length;
        if (similarity > 0.1) {
          similarUsers.add(SimilarUser(
            user: otherUser,
            similarity: similarity,
            commonInteractions: commonContent.length,
          ));
        }
      }
    }
    
    similarUsers.sort((a, b) => b.similarity.compareTo(a.similarity));
    return similarUsers;
  }
  
  Future<List<TrendingContent>> _getTrendingContent(
    VektagrafId userId,
    List<String>? contentTypes,
  ) async {
    final allContent = await graph.getNodesByLabel('Content');
    final trending = <TrendingContent>[];
    
    for (final content in allContent) {
      if (contentTypes != null && 
          !contentTypes.contains(content.getProperty<String>('content_type'))) continue;
      
      final viewCount = content.getProperty<int>('view_count') ?? 0;
      final likeCount = content.getProperty<int>('like_count') ?? 0;
      final shareCount = content.getProperty<int>('share_count') ?? 0;
      
      // Simple trending score calculation
      final score = (viewCount * 0.1) + (likeCount * 0.5) + (shareCount * 1.0);
      
      if (score > 0) {
        trending.add(TrendingContent(
          content: content,
          score: score,
          viewCount: viewCount,
          likeCount: likeCount,
          shareCount: shareCount,
        ));
      }
    }
    
    trending.sort((a, b) => b.score.compareTo(a.score));
    return trending;
  }
  
  Future<void> _findPathsRecursive(
    VektagrafId currentId,
    VektagrafId targetId,
    Set<VektagrafId> visited,
    List<VektagrafId> currentPath,
    List<ContentPath> allPaths,
    int maxDepth,
    List<String> allowedEdgeTypes,
  ) async {
    if (currentPath.length > maxDepth) return;
    
    if (currentId == targetId) {
      allPaths.add(ContentPath(
        path: List.from(currentPath),
        score: 0.0, // Will be calculated later
      ));
      return;
    }
    
    visited.add(currentId);
    final node = await graph.getNode(currentId);
    
    if (node != null) {
      for (final edgeType in allowedEdgeTypes) {
        final edges = await node.outgoingOfType(edgeType);
        for (final edge in edges) {
          if (!visited.contains(edge.toId)) {
            await _findPathsRecursive(
              edge.toId,
              targetId,
              Set.from(visited),
              [...currentPath, edge.toId],
              allPaths,
              maxDepth,
              allowedEdgeTypes,
            );
          }
        }
      }
    }
  }
  
  double _calculatePathScore(ContentPath path) {
    // Simple path scoring - shorter paths with more interactions score higher
    final lengthPenalty = 1.0 / path.path.length;
    return lengthPenalty; // Could be more sophisticated
  }
}

// Supporting classes for content recommendation

class ContentRecommendation {
  final VektagrafNode content;
  final double score;
  final String reason;
  final String strategy;
  
  ContentRecommendation({
    required this.content,
    required this.score,
    required this.reason,
    required this.strategy,
  });
}

class SimilarUser {
  final VektagrafNode user;
  final double similarity;
  final int commonInteractions;
  
  SimilarUser({
    required this.user,
    required this.similarity,
    required this.commonInteractions,
  });
}

class TrendingContent {
  final VektagrafNode content;
  final double score;
  final int viewCount;
  final int likeCount;
  final int shareCount;
  
  TrendingContent({
    required this.content,
    required this.score,
    required this.viewCount,
    required this.likeCount,
    required this.shareCount,
  });
}

class ContentPath {
  final List<VektagrafId> path;
  double score;
  
  ContentPath({
    required this.path,
    required this.score,
  });
}

class ContentAnalytics {
  final VektagrafId contentId;
  final String title;
  final int totalInteractions;
  final int uniqueUsers;
  double averageRating;
  double totalDuration;
  final Map<String, int> interactionTypes;
  final List<EngagementPoint> engagementOverTime;
  
  ContentAnalytics({
    required this.contentId,
    required this.title,
    required this.totalInteractions,
    required this.uniqueUsers,
    required this.averageRating,
    required this.totalDuration,
    required this.interactionTypes,
    required this.engagementOverTime,
  });
}

class EngagementPoint {
  final DateTime timestamp;
  final int interactions;
  final double averageRating;
  
  EngagementPoint({
    required this.timestamp,
    required this.interactions,
    required this.averageRating,
  });
}
```###
 Integration with VektagrafList

#### Object-Relationship Queries

```dart
Future<void> objectRelationshipQueries() async {
  final database = VektagrafDatabaseImpl();
  await database.open('integrated_graph.db');
  
  try {
    // Create objects that automatically become graph nodes
    final users = await database.objects<User>();
    final posts = await database.objects<Post>();
    final comments = await database.objects<Comment>();
    
    // Create users
    final alice = User(
      name: 'Alice Johnson',
      email: 'alice@example.com',
      bio: 'Software engineer passionate about technology',
      interests: ['programming', 'AI', 'music'],
    );
    
    final bob = User(
      name: 'Bob Smith', 
      email: 'bob@example.com',
      bio: 'Data scientist and machine learning enthusiast',
      interests: ['data science', 'AI', 'photography'],
    );
    
    final aliceId = await users.save(alice);
    final bobId = await users.save(bob);
    
    // Create posts
    final alicePost = Post(
      title: 'Introduction to Graph Databases',
      content: 'Graph databases are powerful for modeling relationships...',
      authorId: aliceId,
      tags: ['database', 'graph', 'technology'],
    );
    
    final bobPost = Post(
      title: 'Machine Learning with Graphs',
      content: 'Graph neural networks are revolutionizing ML...',
      authorId: bobId,
      tags: ['ML', 'graph', 'AI'],
    );
    
    final alicePostId = await posts.save(alicePost);
    final bobPostId = await posts.save(bobPost);
    
    // Create comments (relationships)
    final comment = Comment(
      content: 'Great post! Very informative.',
      authorId: bobId,
      postId: alicePostId,
    );
    
    await comments.save(comment);
    
    // Query using relationship traversal through VektagrafList
    print('=== Relationship-based Queries ===');
    
    // Find all posts by users interested in AI
    final aiPosts = await users
        .where((user) => user.interests.contains('AI'))
        .expandRelation<Post>('authored') // Traverse authorship relationship
        .toList();
    
    print('Posts by AI enthusiasts:');
    for (final post in aiPosts) {
      print('  - "${post.title}" by ${post.author?.name}');
    }
    
    // Find users who commented on Alice's posts
    final alicePostCommenters = await posts
        .where((post) => post.authorId == aliceId)
        .expandRelation<Comment>('has_comments') // Traverse to comments
        .expandRelation<User>('authored_by') // Traverse to comment authors
        .distinct()
        .toList();
    
    print('\nUsers who commented on Alice\'s posts:');
    for (final user in alicePostCommenters) {
      print('  - ${user.name}');
    }
    
    // Find posts with similar tags (graph-based similarity)
    final graphTaggedPosts = await posts
        .where((post) => post.tags.contains('graph'))
        .toList();
    
    print('\nPosts tagged with "graph":');
    for (final post in graphTaggedPosts) {
      print('  - "${post.title}" (tags: ${post.tags.join(', ')})');
    }
    
    // Complex multi-hop relationship query
    final relatedContent = await users
        .where((user) => user.name == 'Alice Johnson')
        .expandRelation<Post>('authored') // Alice's posts
        .expandRelation<Comment>('has_comments') // Comments on her posts
        .expandRelation<User>('authored_by') // Comment authors
        .expandRelation<Post>('authored') // Posts by comment authors
        .where((post) => post.authorId != aliceId) // Exclude Alice's own posts
        .distinct()
        .toList();
    
    print('\nContent by users who engaged with Alice:');
    for (final post in relatedContent) {
      print('  - "${post.title}" by ${post.author?.name}');
    }
    
  } finally {
    await database.close();
  }
}

// Example model classes with relationship annotations
class User {
  final String name;
  final String email;
  final String bio;
  final List<String> interests;
  
  // Relationships (would be defined in schema)
  final List<Post> authoredPosts;
  final List<Comment> authoredComments;
  final List<User> friends;
  final List<User> followers;
  
  User({
    required this.name,
    required this.email,
    required this.bio,
    required this.interests,
    this.authoredPosts = const [],
    this.authoredComments = const [],
    this.friends = const [],
    this.followers = const [],
  });
}

class Post {
  final String title;
  final String content;
  final VektagrafId authorId;
  final List<String> tags;
  
  // Relationships
  final User? author;
  final List<Comment> comments;
  final List<User> likedBy;
  
  Post({
    required this.title,
    required this.content,
    required this.authorId,
    required this.tags,
    this.author,
    this.comments = const [],
    this.likedBy = const [],
  });
}

class Comment {
  final String content;
  final VektagrafId authorId;
  final VektagrafId postId;
  
  // Relationships
  final User? author;
  final Post? post;
  
  Comment({
    required this.content,
    required this.authorId,
    required this.postId,
    this.author,
    this.post,
  });
}

Best Practices

Graph Schema Design

Relationship Modeling Patterns

class GraphSchemaDesigner {
  /// Designs optimal relationship structures for different use cases
  static Map<String, dynamic> designSocialNetworkSchema() {
    return {
      'nodes': {
        'User': {
          'labels': ['User', 'Person'],
          'properties': {
            'username': 'string',
            'email': 'string',
            'display_name': 'string',
            'bio': 'text',
            'created_at': 'datetime',
            'last_active': 'datetime',
          },
          'indexes': ['username', 'email', 'created_at'],
        },
        'Post': {
          'labels': ['Post', 'Content'],
          'properties': {
            'title': 'string',
            'content': 'text',
            'created_at': 'datetime',
            'view_count': 'integer',
            'like_count': 'integer',
          },
          'indexes': ['created_at', 'view_count'],
        },
        'Group': {
          'labels': ['Group', 'Community'],
          'properties': {
            'name': 'string',
            'description': 'text',
            'privacy_level': 'string',
            'member_count': 'integer',
          },
          'indexes': ['name', 'privacy_level'],
        },
      },
      'relationships': {
        'FRIENDS_WITH': {
          'description': 'Bidirectional friendship',
          'properties': {
            'since': 'datetime',
            'strength': 'float',
            'interaction_count': 'integer',
          },
          'constraints': ['bidirectional'],
        },
        'FOLLOWS': {
          'description': 'Unidirectional follow relationship',
          'properties': {
            'since': 'datetime',
            'notification_enabled': 'boolean',
          },
        },
        'AUTHORED': {
          'description': 'Content authorship',
          'properties': {
            'created_at': 'datetime',
            'role': 'string', // primary, co-author, editor
          },
        },
        'MEMBER_OF': {
          'description': 'Group membership',
          'properties': {
            'joined_at': 'datetime',
            'role': 'string', // member, admin, moderator
            'status': 'string', // active, inactive, banned
          },
        },
        'LIKED': {
          'description': 'Content appreciation',
          'properties': {
            'liked_at': 'datetime',
            'reaction_type': 'string', // like, love, laugh, etc.
          },
        },
      },
    };
  }
  
  /// Designs e-commerce recommendation schema
  static Map<String, dynamic> designEcommerceSchema() {
    return {
      'nodes': {
        'Customer': {
          'labels': ['Customer', 'User'],
          'properties': {
            'customer_id': 'string',
            'email': 'string',
            'registration_date': 'datetime',
            'total_orders': 'integer',
            'lifetime_value': 'float',
          },
        },
        'Product': {
          'labels': ['Product', 'Item'],
          'properties': {
            'sku': 'string',
            'name': 'string',
            'description': 'text',
            'price': 'float',
            'category': 'string',
            'brand': 'string',
            'rating': 'float',
          },
        },
        'Category': {
          'labels': ['Category'],
          'properties': {
            'name': 'string',
            'parent_category': 'string',
            'level': 'integer',
          },
        },
        'Brand': {
          'labels': ['Brand'],
          'properties': {
            'name': 'string',
            'country': 'string',
            'established': 'integer',
          },
        },
      },
      'relationships': {
        'PURCHASED': {
          'description': 'Product purchase',
          'properties': {
            'order_date': 'datetime',
            'quantity': 'integer',
            'unit_price': 'float',
            'total_amount': 'float',
            'rating': 'float',
            'review': 'text',
          },
        },
        'VIEWED': {
          'description': 'Product view/browse',
          'properties': {
            'viewed_at': 'datetime',
            'duration': 'integer',
            'source': 'string', // search, recommendation, category
          },
        },
        'BELONGS_TO': {
          'description': 'Product category membership',
          'properties': {
            'relevance_score': 'float',
          },
        },
        'MANUFACTURED_BY': {
          'description': 'Product brand relationship',
          'properties': {
            'since': 'datetime',
          },
        },
        'SIMILAR_TO': {
          'description': 'Product similarity',
          'properties': {
            'similarity_score': 'float',
            'similarity_type': 'string', // visual, functional, price
          },
        },
      },
    };
  }
}

Performance Optimization Strategies

class GraphPerformanceOptimizer {
  final VektagrafGraph graph;
  
  GraphPerformanceOptimizer(this.graph);
  
  /// Optimizes graph queries using indexes and caching
  Future<List<VektagrafNode>> optimizedTraversal({
    required VektagrafId startNodeId,
    required List<String> relationshipTypes,
    int maxDepth = 3,
    Map<String, dynamic>? nodeFilters,
    Map<String, dynamic>? edgeFilters,
  }) async {
    // Use breadth-first traversal with early termination
    final visited = <VektagrafId>{};
    final queue = <TraversalState>[];
    final results = <VektagrafNode>[];
    
    queue.add(TraversalState(nodeId: startNodeId, depth: 0));
    
    while (queue.isNotEmpty) {
      final current = queue.removeAt(0);
      
      if (current.depth > maxDepth || visited.contains(current.nodeId)) {
        continue;
      }
      
      visited.add(current.nodeId);
      final node = await graph.getNode(current.nodeId);
      
      if (node != null) {
        // Apply node filters
        if (nodeFilters == null || _matchesFilters(node, nodeFilters)) {
          results.add(node);
        }
        
        // Add connected nodes to queue
        if (current.depth < maxDepth) {
          for (final relationshipType in relationshipTypes) {
            final edges = await node.outgoingOfType(relationshipType);
            
            for (final edge in edges) {
              // Apply edge filters
              if (edgeFilters == null || _matchesEdgeFilters(edge, edgeFilters)) {
                queue.add(TraversalState(
                  nodeId: edge.toId,
                  depth: current.depth + 1,
                ));
              }
            }
          }
        }
      }
    }
    
    return results;
  }
  
  /// Implements efficient pathfinding with A* algorithm
  Future<List<VektagrafNode>?> findOptimalPath(
    VektagrafId startId,
    VektagrafId goalId, {
    double Function(VektagrafNode, VektagrafNode)? heuristic,
    double Function(VektagrafEdge)? edgeCost,
  }) async {
    final openSet = PriorityQueue<PathNode>((a, b) => a.fScore.compareTo(b.fScore));
    final closedSet = <VektagrafId>{};
    final gScore = <VektagrafId, double>{};
    final fScore = <VektagrafId, double>{};
    final cameFrom = <VektagrafId, VektagrafId>{};
    
    gScore[startId] = 0.0;
    fScore[startId] = 0.0;
    
    openSet.add(PathNode(nodeId: startId, fScore: 0.0));
    
    while (openSet.isNotEmpty) {
      final current = openSet.removeFirst();
      
      if (current.nodeId == goalId) {
        return _reconstructPath(cameFrom, goalId);
      }
      
      closedSet.add(current.nodeId);
      final currentNode = await graph.getNode(current.nodeId);
      
      if (currentNode != null) {
        final outgoingEdges = await currentNode.outgoing;
        
        for (final edge in outgoingEdges) {
          if (closedSet.contains(edge.toId)) continue;
          
          final tentativeGScore = (gScore[current.nodeId] ?? double.infinity) +
              (edgeCost?.call(edge) ?? 1.0);
          
          if (tentativeGScore < (gScore[edge.toId] ?? double.infinity)) {
            cameFrom[edge.toId] = current.nodeId;
            gScore[edge.toId] = tentativeGScore;
            
            final goalNode = await graph.getNode(goalId);
            final neighborNode = await graph.getNode(edge.toId);
            
            final h = (heuristic != null && goalNode != null && neighborNode != null)
                ? heuristic(neighborNode, goalNode)
                : 0.0;
            
            fScore[edge.toId] = tentativeGScore + h;
            
            openSet.add(PathNode(nodeId: edge.toId, fScore: fScore[edge.toId]!));
          }
        }
      }
    }
    
    return null; // No path found
  }
  
  /// Implements community detection using Louvain algorithm
  Future<Map<VektagrafId, int>> detectCommunities() async {
    final allNodes = await graph.getAllNodes();
    final communities = <VektagrafId, int>{};
    
    // Initialize each node in its own community
    for (int i = 0; i < allNodes.length; i++) {
      communities[allNodes[i].id] = i;
    }
    
    bool improved = true;
    int iteration = 0;
    
    while (improved && iteration < 100) {
      improved = false;
      iteration++;
      
      for (final node in allNodes) {
        final currentCommunity = communities[node.id]!;
        final neighborCommunities = <int, double>{};
        
        // Calculate modularity gain for each neighbor community
        final edges = await node.outgoing;
        for (final edge in edges) {
          final neighborCommunity = communities[edge.toId];
          if (neighborCommunity != null) {
            neighborCommunities[neighborCommunity] = 
                (neighborCommunities[neighborCommunity] ?? 0.0) + 1.0;
          }
        }
        
        // Find best community to move to
        int bestCommunity = currentCommunity;
        double bestGain = 0.0;
        
        for (final entry in neighborCommunities.entries) {
          final community = entry.key;
          final weight = entry.value;
          
          if (community != currentCommunity && weight > bestGain) {
            bestCommunity = community;
            bestGain = weight;
          }
        }
        
        // Move node to best community if beneficial
        if (bestCommunity != currentCommunity) {
          communities[node.id] = bestCommunity;
          improved = true;
        }
      }
    }
    
    return communities;
  }
  
  /// Calculates centrality measures for nodes
  Future<Map<VektagrafId, CentralityMeasures>> calculateCentrality() async {
    final allNodes = await graph.getAllNodes();
    final centrality = <VektagrafId, CentralityMeasures>{};
    
    for (final node in allNodes) {
      final incomingEdges = await node.incoming;
      final outgoingEdges = await node.outgoing;
      
      // Degree centrality
      final degreeCentrality = (incomingEdges.length + outgoingEdges.length) / 
                              (allNodes.length - 1);
      
      // Betweenness centrality (simplified calculation)
      final betweennessCentrality = await _calculateBetweennessCentrality(node.id);
      
      // Closeness centrality (simplified calculation)
      final closenessCentrality = await _calculateClosenessCentrality(node.id);
      
      centrality[node.id] = CentralityMeasures(
        degree: degreeCentrality,
        betweenness: betweennessCentrality,
        closeness: closenessCentrality,
      );
    }
    
    return centrality;
  }
  
  // Helper methods
  
  bool _matchesFilters(VektagrafNode node, Map<String, dynamic> filters) {
    for (final entry in filters.entries) {
      final property = node.getProperty(entry.key);
      if (property != entry.value) return false;
    }
    return true;
  }
  
  bool _matchesEdgeFilters(VektagrafEdge edge, Map<String, dynamic> filters) {
    for (final entry in filters.entries) {
      final property = edge.getProperty(entry.key);
      if (property != entry.value) return false;
    }
    return true;
  }
  
  Future<List<VektagrafNode>> _reconstructPath(
    Map<VektagrafId, VektagrafId> cameFrom,
    VektagrafId goalId,
  ) async {
    final path = <VektagrafNode>[];
    VektagrafId? current = goalId;
    
    while (current != null) {
      final node = await graph.getNode(current);
      if (node != null) path.insert(0, node);
      current = cameFrom[current];
    }
    
    return path;
  }
  
  Future<double> _calculateBetweennessCentrality(VektagrafId nodeId) async {
    // Simplified betweenness centrality calculation
    // In practice, this would use algorithms like Brandes' algorithm
    return 0.0;
  }
  
  Future<double> _calculateClosenessCentrality(VektagrafId nodeId) async {
    // Simplified closeness centrality calculation
    // In practice, this would calculate shortest paths to all other nodes
    return 0.0;
  }
}

// Supporting classes

class TraversalState {
  final VektagrafId nodeId;
  final int depth;
  
  TraversalState({required this.nodeId, required this.depth});
}

class PathNode {
  final VektagrafId nodeId;
  final double fScore;
  
  PathNode({required this.nodeId, required this.fScore});
}

class CentralityMeasures {
  final double degree;
  final double betweenness;
  final double closeness;
  
  CentralityMeasures({
    required this.degree,
    required this.betweenness,
    required this.closeness,
  });
}

// Simple priority queue implementation
class PriorityQueue<T> {
  final List<T> _items = [];
  final int Function(T, T) _compare;
  
  PriorityQueue(this._compare);
  
  void add(T item) {
    _items.add(item);
    _items.sort(_compare);
  }
  
  T removeFirst() => _items.removeAt(0);
  
  bool get isNotEmpty => _items.isNotEmpty;
}
```### Securit
y and Access Control

#### Relationship-Level Security

```dart
class SecureGraphOperations {
  final VektagrafGraph graph;
  final SecurityContext securityContext;
  
  SecureGraphOperations(this.graph, this.securityContext);
  
  /// Performs secure graph traversal with relationship-level access control
  Future<List<VektagrafNode>> secureTraversal({
    required VektagrafId startNodeId,
    required List<String> relationshipTypes,
    int maxDepth = 3,
    Map<String, dynamic>? filters,
  }) async {
    final results = <VektagrafNode>[];
    final visited = <VektagrafId>{};
    final queue = <TraversalState>[];
    
    // Check initial node access
    if (!await _canAccessNode(startNodeId)) {
      throw SecurityException('Access denied to start node: $startNodeId');
    }
    
    queue.add(TraversalState(nodeId: startNodeId, depth: 0));
    
    while (queue.isNotEmpty) {
      final current = queue.removeAt(0);
      
      if (current.depth > maxDepth || visited.contains(current.nodeId)) {
        continue;
      }
      
      visited.add(current.nodeId);
      
      // Check node access permissions
      if (await _canAccessNode(current.nodeId)) {
        final node = await graph.getNode(current.nodeId);
        if (node != null && _matchesFilters(node, filters)) {
          results.add(node);
        }
        
        // Traverse relationships with security checks
        if (current.depth < maxDepth) {
          for (final relationshipType in relationshipTypes) {
            if (await _canTraverseRelationship(current.nodeId, relationshipType)) {
              final edges = await node?.outgoingOfType(relationshipType) ?? [];
              
              for (final edge in edges) {
                if (await _canAccessRelationship(edge.id)) {
                  queue.add(TraversalState(
                    nodeId: edge.toId,
                    depth: current.depth + 1,
                  ));
                }
              }
            }
          }
        }
      }
    }
    
    return results;
  }
  
  /// Creates a relationship with security validation
  Future<VektagrafId> createSecureRelationship({
    required VektagrafId fromNodeId,
    required VektagrafId toNodeId,
    required String relationshipType,
    Map<String, dynamic>? properties,
  }) async {
    // Validate permissions
    if (!await _canAccessNode(fromNodeId)) {
      throw SecurityException('Access denied to source node: $fromNodeId');
    }
    
    if (!await _canAccessNode(toNodeId)) {
      throw SecurityException('Access denied to target node: $toNodeId');
    }
    
    if (!await _canCreateRelationship(relationshipType)) {
      throw SecurityException('Permission denied to create relationship: $relationshipType');
    }
    
    // Add security metadata to relationship
    final secureProperties = {
      ...?properties,
      'created_by': securityContext.userId.toString(),
      'created_at': DateTime.now().toIso8601String(),
      'security_level': _determineSecurityLevel(fromNodeId, toNodeId),
      'access_control': _generateAccessControl(relationshipType),
    };
    
    // Create relationship with audit logging
    final relationshipId = await graph.addEdge(
      fromNodeId,
      toNodeId,
      type: relationshipType,
      properties: secureProperties,
    );
    
    // Log the relationship creation
    await _auditLog('relationship_created', {
      'relationship_id': relationshipId.toString(),
      'from_node': fromNodeId.toString(),
      'to_node': toNodeId.toString(),
      'type': relationshipType,
      'user_id': securityContext.userId.toString(),
    });
    
    return relationshipId;
  }
  
  /// Implements privacy-preserving graph analytics
  Future<GraphAnalytics> getPrivacyPreservingAnalytics({
    double epsilon = 1.0,
    double delta = 1e-5,
  }) async {
    // Get accessible nodes only
    final accessibleNodes = <VektagrafNode>[];
    final allNodes = await graph.getAllNodes();
    
    for (final node in allNodes) {
      if (await _canAccessNode(node.id)) {
        accessibleNodes.add(node);
      }
    }
    
    // Calculate analytics with differential privacy
    final nodeCount = _addNoise(accessibleNodes.length.toDouble(), epsilon);
    
    // Calculate degree distribution with privacy
    final degreeDistribution = <int, int>{};
    for (final node in accessibleNodes) {
      final degree = await _getSecureNodeDegree(node.id);
      final noisyDegree = _addNoise(degree.toDouble(), epsilon).round();
      degreeDistribution[noisyDegree] = (degreeDistribution[noisyDegree] ?? 0) + 1;
    }
    
    // Calculate relationship type distribution
    final relationshipTypes = <String, int>{};
    for (final node in accessibleNodes) {
      final edges = await node.outgoing;
      for (final edge in edges) {
        if (await _canAccessRelationship(edge.id)) {
          final type = edge.type ?? 'unknown';
          relationshipTypes[type] = (relationshipTypes[type] ?? 0) + 1;
        }
      }
    }
    
    // Add noise to relationship counts
    final noisyRelationshipTypes = <String, int>{};
    for (final entry in relationshipTypes.entries) {
      final noisyCount = _addNoise(entry.value.toDouble(), epsilon).round();
      noisyRelationshipTypes[entry.key] = noisyCount;
    }
    
    return GraphAnalytics(
      nodeCount: nodeCount.round(),
      degreeDistribution: degreeDistribution,
      relationshipTypeDistribution: noisyRelationshipTypes,
      privacyParameters: PrivacyParameters(epsilon: epsilon, delta: delta),
    );
  }
  
  // Security validation methods
  
  Future<bool> _canAccessNode(VektagrafId nodeId) async {
    final node = await graph.getNode(nodeId);
    if (node == null) return false;
    
    // Check node-level permissions
    final securityLevel = node.getProperty<String>('security_level');
    final ownerId = node.getProperty<String>('owner_id');
    
    // System admin can access everything
    if (securityContext.roles.contains('system_admin')) return true;
    
    // Owner can access their own nodes
    if (ownerId == securityContext.userId.toString()) return true;
    
    // Check security clearance
    if (securityLevel != null) {
      return _hasSecurityClearance(securityLevel);
    }
    
    // Default to public access for nodes without security level
    return true;
  }
  
  Future<bool> _canAccessRelationship(VektagrafId relationshipId) async {
    final relationship = await graph.getEdge(relationshipId);
    if (relationship == null) return false;
    
    // Check relationship-level permissions
    final accessControl = relationship.getProperty<Map<String, dynamic>>('access_control');
    if (accessControl != null) {
      return _checkAccessControl(accessControl);
    }
    
    // Check if user can access both connected nodes
    return await _canAccessNode(relationship.fromId) && 
           await _canAccessNode(relationship.toId);
  }
  
  Future<bool> _canTraverseRelationship(VektagrafId nodeId, String relationshipType) async {
    // Check if user has permission to traverse this type of relationship
    final permissions = securityContext.permissions;
    
    return permissions.contains('traverse:*') || 
           permissions.contains('traverse:$relationshipType') ||
           permissions.contains('read:relationships');
  }
  
  Future<bool> _canCreateRelationship(String relationshipType) async {
    final permissions = securityContext.permissions;
    
    return permissions.contains('create:*') || 
           permissions.contains('create:relationships') ||
           permissions.contains('create:$relationshipType');
  }
  
  bool _hasSecurityClearance(String requiredLevel) {
    final userClearance = securityContext.clearanceLevel;
    if (userClearance == null) return false;
    
    // Define clearance hierarchy
    const clearanceLevels = {
      'public': 0,
      'internal': 1,
      'confidential': 2,
      'secret': 3,
      'top_secret': 4,
    };
    
    final userLevel = clearanceLevels[userClearance] ?? 0;
    final requiredLevelValue = clearanceLevels[requiredLevel] ?? 0;
    
    return userLevel >= requiredLevelValue;
  }
  
  bool _checkAccessControl(Map<String, dynamic> accessControl) {
    // Check role-based access
    final allowedRoles = accessControl['allowed_roles'] as List<dynamic>?;
    if (allowedRoles != null) {
      final hasRole = securityContext.roles.any((role) => allowedRoles.contains(role));
      if (!hasRole) return false;
    }
    
    // Check user-based access
    final allowedUsers = accessControl['allowed_users'] as List<dynamic>?;
    if (allowedUsers != null) {
      final hasAccess = allowedUsers.contains(securityContext.userId.toString());
      if (!hasAccess) return false;
    }
    
    // Check time-based access
    final validFrom = accessControl['valid_from'] as String?;
    final validTo = accessControl['valid_to'] as String?;
    
    if (validFrom != null || validTo != null) {
      final now = DateTime.now();
      
      if (validFrom != null && now.isBefore(DateTime.parse(validFrom))) {
        return false;
      }
      
      if (validTo != null && now.isAfter(DateTime.parse(validTo))) {
        return false;
      }
    }
    
    return true;
  }
  
  String _determineSecurityLevel(VektagrafId fromNodeId, VektagrafId toNodeId) {
    // Determine security level based on connected nodes
    // This is a simplified implementation
    return 'internal';
  }
  
  Map<String, dynamic> _generateAccessControl(String relationshipType) {
    // Generate access control rules based on relationship type
    switch (relationshipType) {
      case 'FRIENDS_WITH':
        return {
          'allowed_roles': ['user', 'admin'],
          'visibility': 'friends',
        };
      case 'WORKS_FOR':
        return {
          'allowed_roles': ['employee', 'hr', 'admin'],
          'visibility': 'internal',
        };
      default:
        return {
          'allowed_roles': ['user', 'admin'],
          'visibility': 'public',
        };
    }
  }
  
  Future<int> _getSecureNodeDegree(VektagrafId nodeId) async {
    final node = await graph.getNode(nodeId);
    if (node == null) return 0;
    
    int degree = 0;
    
    // Count accessible incoming edges
    final incomingEdges = await node.incoming;
    for (final edge in incomingEdges) {
      if (await _canAccessRelationship(edge.id)) {
        degree++;
      }
    }
    
    // Count accessible outgoing edges
    final outgoingEdges = await node.outgoing;
    for (final edge in outgoingEdges) {
      if (await _canAccessRelationship(edge.id)) {
        degree++;
      }
    }
    
    return degree;
  }
  
  double _addNoise(double value, double epsilon) {
    // Add Laplace noise for differential privacy
    final random = Random();
    final u = random.nextDouble() - 0.5;
    final noise = -1 / epsilon * (u >= 0 ? 1 : -1) * log(1 - 2 * u.abs());
    return value + noise;
  }
  
  bool _matchesFilters(VektagrafNode node, Map<String, dynamic>? filters) {
    if (filters == null) return true;
    
    for (final entry in filters.entries) {
      final property = node.getProperty(entry.key);
      if (property != entry.value) return false;
    }
    return true;
  }
  
  Future<void> _auditLog(String action, Map<String, dynamic> details) async {
    // Log security-relevant actions
    final logEntry = {
      'timestamp': DateTime.now().toIso8601String(),
      'user_id': securityContext.userId.toString(),
      'action': action,
      'details': details,
      'ip_address': securityContext.ipAddress,
      'user_agent': securityContext.userAgent,
    };
    
    // In practice, this would write to an audit log system
    print('AUDIT: $logEntry');
  }
}

// Supporting classes for security

class SecurityContext {
  final VektagrafId userId;
  final List<String> roles;
  final List<String> permissions;
  final String? clearanceLevel;
  final String? ipAddress;
  final String? userAgent;
  
  SecurityContext({
    required this.userId,
    required this.roles,
    required this.permissions,
    this.clearanceLevel,
    this.ipAddress,
    this.userAgent,
  });
}

class SecurityException implements Exception {
  final String message;
  
  SecurityException(this.message);
  
  @override
  String toString() => 'SecurityException: $message';
}

class GraphAnalytics {
  final int nodeCount;
  final Map<int, int> degreeDistribution;
  final Map<String, int> relationshipTypeDistribution;
  final PrivacyParameters privacyParameters;
  
  GraphAnalytics({
    required this.nodeCount,
    required this.degreeDistribution,
    required this.relationshipTypeDistribution,
    required this.privacyParameters,
  });
}

class PrivacyParameters {
  final double epsilon;
  final double delta;
  
  PrivacyParameters({required this.epsilon, required this.delta});
}

Advanced Topics

Graph Algorithms Implementation

class AdvancedGraphAlgorithms {
  final VektagrafGraph graph;
  
  AdvancedGraphAlgorithms(this.graph);
  
  /// Implements PageRank algorithm for node importance ranking
  Future<Map<VektagrafId, double>> calculatePageRank({
    double dampingFactor = 0.85,
    double tolerance = 1e-6,
    int maxIterations = 100,
  }) async {
    final allNodes = await graph.getAllNodes();
    final nodeIds = allNodes.map((n) => n.id).toList();
    final nodeCount = nodeIds.length;
    
    if (nodeCount == 0) return {};
    
    // Initialize PageRank values
    final pageRank = <VektagrafId, double>{};
    final newPageRank = <VektagrafId, double>{};
    final initialValue = 1.0 / nodeCount;
    
    for (final nodeId in nodeIds) {
      pageRank[nodeId] = initialValue;
    }
    
    // Build adjacency information
    final outgoingLinks = <VektagrafId, List<VektagrafId>>{};
    final incomingLinks = <VektagrafId, List<VektagrafId>>{};
    
    for (final node in allNodes) {
      final outgoing = await node.outgoing;
      outgoingLinks[node.id] = outgoing.map((e) => e.toId).toList();
      
      for (final edge in outgoing) {
        incomingLinks.putIfAbsent(edge.toId, () => []).add(node.id);
      }
    }
    
    // Iterative calculation
    for (int iteration = 0; iteration < maxIterations; iteration++) {
      double totalDiff = 0.0;
      
      for (final nodeId in nodeIds) {
        double sum = 0.0;
        
        // Sum contributions from incoming links
        final incoming = incomingLinks[nodeId] ?? [];
        for (final incomingNodeId in incoming) {
          final incomingPageRank = pageRank[incomingNodeId] ?? 0.0;
          final outgoingCount = outgoingLinks[incomingNodeId]?.length ?? 1;
          sum += incomingPageRank / outgoingCount;
        }
        
        final newValue = (1.0 - dampingFactor) / nodeCount + dampingFactor * sum;
        newPageRank[nodeId] = newValue;
        
        totalDiff += (newValue - (pageRank[nodeId] ?? 0.0)).abs();
      }
      
      // Update PageRank values
      pageRank.addAll(newPageRank);
      
      // Check for convergence
      if (totalDiff < tolerance) {
        break;
      }
    }
    
    return pageRank;
  }
  
  /// Implements community detection using Label Propagation Algorithm
  Future<Map<VektagrafId, String>> detectCommunitiesLPA({
    int maxIterations = 100,
  }) async {
    final allNodes = await graph.getAllNodes();
    final labels = <VektagrafId, String>{};
    
    // Initialize each node with unique label
    for (int i = 0; i < allNodes.length; i++) {
      labels[allNodes[i].id] = 'community_$i';
    }
    
    // Iterative label propagation
    for (int iteration = 0; iteration < maxIterations; iteration++) {
      bool changed = false;
      
      // Randomize node order to avoid bias
      final shuffledNodes = List<VektagrafNode>.from(allNodes)..shuffle();
      
      for (final node in shuffledNodes) {
        final neighborLabels = <String, int>{};
        
        // Count neighbor labels
        final edges = await node.outgoing;
        for (final edge in edges) {
          final neighborLabel = labels[edge.toId];
          if (neighborLabel != null) {
            neighborLabels[neighborLabel] = (neighborLabels[neighborLabel] ?? 0) + 1;
          }
        }
        
        // Also consider incoming edges
        final incomingEdges = await node.incoming;
        for (final edge in incomingEdges) {
          final neighborLabel = labels[edge.fromId];
          if (neighborLabel != null) {
            neighborLabels[neighborLabel] = (neighborLabels[neighborLabel] ?? 0) + 1;
          }
        }
        
        // Find most frequent label
        if (neighborLabels.isNotEmpty) {
          final mostFrequentLabel = neighborLabels.entries
              .reduce((a, b) => a.value > b.value ? a : b)
              .key;
          
          if (labels[node.id] != mostFrequentLabel) {
            labels[node.id] = mostFrequentLabel;
            changed = true;
          }
        }
      }
      
      // Stop if no changes occurred
      if (!changed) break;
    }
    
    return labels;
  }
  
  /// Implements graph clustering using Markov Clustering (MCL)
  Future<List<List<VektagrafId>>> clusterGraphMCL({
    double inflation = 2.0,
    double expansion = 2.0,
    double tolerance = 1e-6,
    int maxIterations = 100,
  }) async {
    final allNodes = await graph.getAllNodes();
    final nodeIds = allNodes.map((n) => n.id).toList();
    final nodeCount = nodeIds.length;
    
    if (nodeCount == 0) return [];
    
    // Build adjacency matrix
    final adjacencyMatrix = List.generate(
      nodeCount, 
      (_) => List.filled(nodeCount, 0.0),
    );
    
    // Add self-loops and normalize
    for (int i = 0; i < nodeCount; i++) {
      adjacencyMatrix[i][i] = 1.0;
      
      final node = allNodes[i];
      final outgoing = await node.outgoing;
      
      for (final edge in outgoing) {
        final targetIndex = nodeIds.indexOf(edge.toId);
        if (targetIndex >= 0) {
          adjacencyMatrix[i][targetIndex] = 1.0;
        }
      }
    }
    
    // Normalize columns
    for (int j = 0; j < nodeCount; j++) {
      double sum = 0.0;
      for (int i = 0; i < nodeCount; i++) {
        sum += adjacencyMatrix[i][j];
      }
      
      if (sum > 0) {
        for (int i = 0; i < nodeCount; i++) {
          adjacencyMatrix[i][j] /= sum;
        }
      }
    }
    
    // MCL iterations
    var currentMatrix = adjacencyMatrix;
    
    for (int iteration = 0; iteration < maxIterations; iteration++) {
      // Expansion (matrix multiplication)
      final expandedMatrix = _matrixMultiply(currentMatrix, currentMatrix);
      
      // Inflation (element-wise power and normalization)
      final inflatedMatrix = _inflateMatrix(expandedMatrix, inflation);
      
      // Check convergence
      if (_matrixConverged(currentMatrix, inflatedMatrix, tolerance)) {
        break;
      }
      
      currentMatrix = inflatedMatrix;
    }
    
    // Extract clusters from final matrix
    return _extractClusters(currentMatrix, nodeIds);
  }
  
  /// Finds strongly connected components using Tarjan's algorithm
  Future<List<List<VektagrafId>>> findStronglyConnectedComponents() async {
    final allNodes = await graph.getAllNodes();
    final components = <List<VektagrafId>>[];
    final visited = <VektagrafId>{};
    final stack = <VektagrafId>[];
    final indices = <VektagrafId, int>{};
    final lowLinks = <VektagrafId, int>{};
    final onStack = <VektagrafId, bool>{};
    int index = 0;
    
    Future<void> strongConnect(VektagrafId nodeId) async {
      indices[nodeId] = index;
      lowLinks[nodeId] = index;
      index++;
      stack.add(nodeId);
      onStack[nodeId] = true;
      
      final node = await graph.getNode(nodeId);
      if (node != null) {
        final outgoing = await node.outgoing;
        
        for (final edge in outgoing) {
          final targetId = edge.toId;
          
          if (!indices.containsKey(targetId)) {
            await strongConnect(targetId);
            lowLinks[nodeId] = min(lowLinks[nodeId]!, lowLinks[targetId]!);
          } else if (onStack[targetId] == true) {
            lowLinks[nodeId] = min(lowLinks[nodeId]!, indices[targetId]!);
          }
        }
      }
      
      // If nodeId is a root node, pop the stack and create component
      if (lowLinks[nodeId] == indices[nodeId]) {
        final component = <VektagrafId>[];
        VektagrafId? poppedId;
        
        do {
          poppedId = stack.removeLast();
          onStack[poppedId] = false;
          component.add(poppedId);
        } while (poppedId != nodeId);
        
        components.add(component);
      }
    }
    
    // Run Tarjan's algorithm on all unvisited nodes
    for (final node in allNodes) {
      if (!indices.containsKey(node.id)) {
        await strongConnect(node.id);
      }
    }
    
    return components;
  }
  
  // Helper methods for matrix operations
  
  List<List<double>> _matrixMultiply(
    List<List<double>> a, 
    List<List<double>> b,
  ) {
    final rows = a.length;
    final cols = b[0].length;
    final result = List.generate(rows, (_) => List.filled(cols, 0.0));
    
    for (int i = 0; i < rows; i++) {
      for (int j = 0; j < cols; j++) {
        for (int k = 0; k < a[0].length; k++) {
          result[i][j] += a[i][k] * b[k][j];
        }
      }
    }
    
    return result;
  }
  
  List<List<double>> _inflateMatrix(List<List<double>> matrix, double inflation) {
    final rows = matrix.length;
    final cols = matrix[0].length;
    final result = List.generate(rows, (_) => List.filled(cols, 0.0));
    
    // Apply inflation (element-wise power)
    for (int i = 0; i < rows; i++) {
      for (int j = 0; j < cols; j++) {
        result[i][j] = pow(matrix[i][j], inflation).toDouble();
      }
    }
    
    // Normalize columns
    for (int j = 0; j < cols; j++) {
      double sum = 0.0;
      for (int i = 0; i < rows; i++) {
        sum += result[i][j];
      }
      
      if (sum > 0) {
        for (int i = 0; i < rows; i++) {
          result[i][j] /= sum;
        }
      }
    }
    
    return result;
  }
  
  bool _matrixConverged(
    List<List<double>> a, 
    List<List<double>> b, 
    double tolerance,
  ) {
    for (int i = 0; i < a.length; i++) {
      for (int j = 0; j < a[0].length; j++) {
        if ((a[i][j] - b[i][j]).abs() > tolerance) {
          return false;
        }
      }
    }
    return true;
  }
  
  List<List<VektagrafId>> _extractClusters(
    List<List<double>> matrix, 
    List<VektagrafId> nodeIds,
  ) {
    final clusters = <List<VektagrafId>>[];
    final processed = <int>{};
    
    for (int i = 0; i < matrix.length; i++) {
      if (processed.contains(i)) continue;
      
      final cluster = <VektagrafId>[];
      
      // Find all nodes that belong to this cluster
      for (int j = 0; j < matrix[0].length; j++) {
        if (matrix[i][j] > 0.001) { // Threshold for cluster membership
          cluster.add(nodeIds[j]);
          processed.add(j);
        }
      }
      
      if (cluster.isNotEmpty) {
        clusters.add(cluster);
      }
    }
    
    return clusters;
  }
}

Reference

Graph API

Core Graph Interface

abstract class VektagrafGraph {
  // Node operations
  Future<VektagrafId> addNode(Map<String, dynamic> properties, {Set<String>? labels});
  Future<VektagrafNode?> getNode(VektagrafId id);
  Future<void> removeNode(VektagrafId id);
  Future<List<VektagrafNode>> getNodesByLabel(String label);
  
  // Edge operations
  Future<VektagrafId> addEdge(VektagrafId fromId, VektagrafId toId, 
    {String? type, Map<String, dynamic>? properties});
  Future<VektagrafEdge?> getEdge(VektagrafId id);
  Future<void> removeEdge(VektagrafId id);
  Future<List<VektagrafEdge>> getEdgesByType(String type);
  
  // Traversal operations
  Stream<VektagrafNode> traverse(VektagrafId startId);
  Future<List<VektagrafNode>?> shortestPath(VektagrafId fromId, VektagrafId toId);
}

Node and Edge Interfaces

abstract class VektagrafNode {
  VektagrafId get id;
  Set<String> get labels;
  Map<String, dynamic> get properties;
  
  bool hasLabel(String label);
  T? getProperty<T>(String key);
  Future<void> setProperty(String key, dynamic value);
  
  Future<List<VektagrafEdge>> get incoming;
  Future<List<VektagrafEdge>> get outgoing;
  Future<List<VektagrafEdge>> outgoingOfType(String type);
  Future<List<VektagrafEdge>> incomingOfType(String type);
  
  Stream<VektagrafNode> connectedNodes();
  Stream<VektagrafNode> connectedNodesOfType(String type);
}

abstract class VektagrafEdge {
  VektagrafId get id;
  VektagrafId get fromId;
  VektagrafId get toId;
  String? get type;
  Map<String, dynamic> get properties;
  
  Future<VektagrafNode?> get fromNode;
  Future<VektagrafNode?> get toNode;
  
  T? getProperty<T>(String key);
  Future<void> setProperty(String key, dynamic value);
}

Performance Characteristics

Time Complexity

Operation Memory Graph Optimized Graph
Add Node O(1) O(1)
Add Edge O(1) O(1)
Get Node O(1) O(1)
Get Edge O(1) O(1)
Traverse (BFS) O(V + E) O(V + E)
Shortest Path O(V + E) O(V log V + E)
Remove Node O(degree) O(degree)

Space Complexity

Component Memory Usage
Node ~200 bytes + properties
Edge ~150 bytes + properties
Index (per label) ~50 bytes per node
Index (per type) ~50 bytes per edge

Configuration Options

class GraphConfig {
  // Performance settings
  final int maxNodesInMemory;
  final int maxEdgesInMemory;
  final bool enableIndexing;
  final bool enableCaching;
  
  // Security settings
  final bool enableAccessControl;
  final bool auditTraversal;
  final bool encryptProperties;
  
  // Algorithm settings
  final int maxTraversalDepth;
  final Duration traversalTimeout;
  final bool enableParallelTraversal;
}

Summary

This chapter covered Vektagraf's comprehensive graph operations and relationship modeling capabilities. Key takeaways include:

  • Object-Graph Integration: Automatic graph node creation for objects with seamless relationship modeling
  • Flexible Relationships: Support for various relationship patterns with properties and types
  • Advanced Traversal: Efficient graph traversal with security controls and performance optimization
  • Social Features: Complete social network implementation with friend suggestions and analytics
  • Recommendation Systems: Content-based and collaborative filtering using graph relationships
  • Security: Relationship-level access control with privacy-preserving analytics
  • Advanced Algorithms: PageRank, community detection, and clustering implementations

Next Steps

  • Chapter 7: Learn about query optimization and performance tuning techniques
  • Chapter 21: Build complete social network applications using graph operations
  • Chapter 19: Implement recommendation systems with graph-based algorithms