Chapter 4: Database Operations and Transactions
Overview
Vektagraf provides a comprehensive set of database operations built around an object-centric design philosophy. This chapter covers all database operations, from basic CRUD operations to advanced transaction management, with complete API coverage and practical examples. You'll learn how to leverage Vektagraf's fluent method chaining for complex queries, implement robust transaction handling, and optimize batch operations for maximum performance.
Learning Objectives
By the end of this chapter, you will be able to:
- Perform all basic database operations (Create, Read, Update, Delete) using Vektagraf's native API
- Implement robust transaction handling with proper isolation levels and error handling
- Use fluent method chaining to build complex queries efficiently
- Optimize batch operations for high-performance data processing
- Handle concurrency conflicts and implement optimistic locking strategies
- Monitor and optimize database operation performance
Prerequisites
- Completed Chapter 3: Schema Design and Code Generation
- Basic understanding of database concepts and ACID properties
- Familiarity with Dart async/await patterns
Core Concepts
Object-Centric Operations
Vektagraf treats all data as objects with automatic metadata management. Every object stored in the database receives:
- Unique ID: Generated using VektagrafId with timestamp, machine ID, and sequence
- Revision Number: For optimistic concurrency control
- Timestamps: Creation and last update times
- Type Information: Automatically derived from Dart types
- Relationship Metadata: Automatic graph edge creation
VektagrafList: The Core Interface
The VektagrafList<T> class extends Dart's ListBase<T> while adding database-specific operations. This design provides natural Dart collection behavior with powerful database capabilities:
// Get a typed list of objects from the database
final users = await database.objects<User>();
// Use familiar Dart list operations
print('Total users: ${users.length}');
for (final user in users) {
print('User: ${user.name}');
}
// Chain database operations fluently
final activeEngineers = await users
.where((u) => u.isActive)
.whereProperty('department', 'Engineering')
.orderByProperty('createdAt', descending: true)
.take(10);
Transaction System Architecture
Vektagraf implements ACID transactions with the following characteristics:
- Atomicity: All operations within a transaction succeed or fail together
- Consistency: Database constraints are maintained across transactions
- Isolation: Concurrent transactions don't interfere with each other
- Durability: Committed changes survive system failures
Practical Examples
Basic CRUD Operations
Creating and Saving Objects
import 'package:vektagraf/vektagraf.dart';
// Define your data model
class User {
final String name;
final String email;
final int age;
final List<double> profileVector;
final DateTime createdAt;
User({
required this.name,
required this.email,
required this.age,
required this.profileVector,
DateTime? createdAt,
}) : createdAt = createdAt ?? DateTime.now();
}
Future<void> basicCrudExample() async {
// Open database connection
final database = VektagrafDatabaseImpl();
await database.open('users.db');
try {
// Get typed list for User objects
final users = await database.objects<User>();
// Create new user
final newUser = User(
name: 'Alice Johnson',
email: 'alice@example.com',
age: 30,
profileVector: [0.1, 0.2, 0.3, 0.4, 0.5],
);
// Save user and get generated ID
final userId = await users.save(newUser);
print('Created user with ID: $userId');
// The user is now automatically added to the list
print('Total users: ${users.length}');
// Access metadata
print('User revision: ${users.revisionOf(newUser)}');
print('Created at: ${users.createdAt(newUser)}');
} finally {
await database.close();
}
}
Reading and Querying Objects
Future<void> queryingExample() async {
final database = VektagrafDatabaseImpl();
await database.open('users.db');
try {
final users = await database.objects<User>();
// Basic filtering with Dart predicates
final youngUsers = users.where((user) => user.age < 30);
print('Young users: ${youngUsers.length}');
// Optimized property queries (uses indexes when available)
final engineers = await users.whereProperty('department', 'Engineering');
print('Engineers: ${engineers.length}');
// Range queries
final millennials = await users.wherePropertyRange<int>(
'age', 25, 40
);
print('Millennials: ${millennials.length}');
// Sorted queries with limits
final recentUsers = await users.orderByProperty<DateTime>(
'createdAt',
descending: true,
limit: 10,
);
print('Recent users: ${recentUsers.length}');
// Complex chained queries
final activeRecentEngineers = await users
.whereProperty('department', 'Engineering')
.whereProperty('isActive', true)
.orderByProperty('createdAt', descending: true)
.take(5);
print('Active recent engineers: ${activeRecentEngineers.length}');
} finally {
await database.close();
}
}
Updating Objects with Concurrency Control
Future<void> updateExample() async {
final database = VektagrafDatabaseImpl();
await database.open('users.db');
try {
final users = await database.objects<User>();
// Find user to update
final user = users.firstWhere((u) => u.email == 'alice@example.com');
final currentRevision = users.currentRevision(user)!;
// Create updated user object
final updatedUser = User(
name: user.name,
email: user.email,
age: user.age + 1, // Birthday!
profileVector: user.profileVector,
createdAt: user.createdAt,
);
try {
// Update with concurrency control
final newId = await users.update(updatedUser, currentRevision);
print('Updated user, new revision: ${users.revisionOf(updatedUser)}');
} on ConcurrencyConflictException catch (e) {
print('Concurrency conflict: ${e.message}');
print('Expected revision: ${e.expectedRevision}');
print('Actual revision: ${e.actualRevision}');
// Handle conflict - could retry, merge, or ask user
await handleConcurrencyConflict(e, user, updatedUser);
}
} finally {
await database.close();
}
}
Future<void> handleConcurrencyConflict(
ConcurrencyConflictException conflict,
User originalUser,
User updatedUser,
) async {
// Example conflict resolution strategy
print('Resolving conflict for user: ${originalUser.email}');
// Could implement:
// 1. Last-writer-wins
// 2. Field-level merging
// 3. User intervention
// 4. Automatic retry with exponential backoff
// For this example, we'll retry once
await Future.delayed(Duration(milliseconds: 100));
// ... retry logic here
}
Deleting Objects
Future<void> deleteExample() async {
final database = VektagrafDatabaseImpl();
await database.open('users.db');
try {
final users = await database.objects<User>();
// Find user to delete
final userToDelete = users.firstWhere((u) => u.email == 'old@example.com');
// Remove from both list and database
await users.removeObject(userToDelete);
print('User deleted successfully');
// Batch deletion with predicate
await users.removeWhere((user) => user.age > 65);
print('Removed users over 65');
// The list automatically reflects the changes
print('Remaining users: ${users.length}');
} finally {
await database.close();
}
}
Advanced Transaction Management
Basic Transaction Usage
Future<void> basicTransactionExample() async {
final database = VektagrafDatabaseImpl();
await database.open('ecommerce.db');
try {
// Execute operations within a transaction
await database.transaction((txn) async {
// All operations use the same transaction context
final orders = await txn.objects<Order>();
final inventory = await txn.objects<InventoryItem>();
// Create new order
final order = Order(
customerId: 'customer123',
items: [
OrderItem(productId: 'product456', quantity: 2),
OrderItem(productId: 'product789', quantity: 1),
],
total: 299.99,
);
final orderId = await txn.save(order);
// Update inventory for each item
for (final item in order.items) {
final product = await txn.getById<InventoryItem>(
VektagrafId.fromString(item.productId)
);
if (product == null) {
throw Exception('Product not found: ${item.productId}');
}
if (product.quantity < item.quantity) {
throw Exception('Insufficient inventory for ${item.productId}');
}
// Update inventory
final updatedProduct = product.copyWith(
quantity: product.quantity - item.quantity,
);
await txn.update(
product.id,
updatedProduct,
product.revision,
);
}
print('Order created successfully: $orderId');
// Transaction automatically commits if no exceptions are thrown
});
} catch (e) {
print('Transaction failed: $e');
// Transaction automatically rolls back on exception
} finally {
await database.close();
}
}
Complex Transaction with Error Handling
Future<void> complexTransactionExample() async {
final database = VektagrafDatabaseImpl();
await database.open('banking.db');
try {
final result = await database.transaction((txn) async {
final accounts = await txn.objects<Account>();
// Find source and destination accounts
final sourceAccount = accounts.firstWhere(
(a) => a.accountNumber == 'ACC001'
);
final destAccount = accounts.firstWhere(
(a) => a.accountNumber == 'ACC002'
);
final transferAmount = 1000.0;
// Validate sufficient funds
if (sourceAccount.balance < transferAmount) {
throw InsufficientFundsException(
'Account ${sourceAccount.accountNumber} has insufficient funds'
);
}
// Create transaction record
final transaction = BankTransaction(
id: VektagrafId.generate(),
fromAccount: sourceAccount.accountNumber,
toAccount: destAccount.accountNumber,
amount: transferAmount,
type: TransactionType.transfer,
timestamp: DateTime.now(),
);
await txn.save(transaction);
// Update account balances
final updatedSource = sourceAccount.copyWith(
balance: sourceAccount.balance - transferAmount,
lastTransactionId: transaction.id,
);
final updatedDest = destAccount.copyWith(
balance: destAccount.balance + transferAmount,
lastTransactionId: transaction.id,
);
await txn.update(
sourceAccount.id,
updatedSource,
sourceAccount.revision,
);
await txn.update(
destAccount.id,
updatedDest,
destAccount.revision,
);
return TransferResult(
transactionId: transaction.id,
sourceBalance: updatedSource.balance,
destBalance: updatedDest.balance,
);
});
print('Transfer completed: ${result.transactionId}');
print('New balances - Source: ${result.sourceBalance}, Dest: ${result.destBalance}');
} on InsufficientFundsException catch (e) {
print('Transfer failed: ${e.message}');
} on ConcurrencyConflictException catch (e) {
print('Concurrency conflict during transfer: ${e.message}');
// Could implement retry logic here
} catch (e) {
print('Unexpected error during transfer: $e');
} finally {
await database.close();
}
}
Batch Operations and Performance Optimization
High-Performance Batch Inserts
Future<void> batchInsertExample() async {
final database = VektagrafDatabaseImpl();
await database.open('analytics.db');
try {
final events = await database.objects<AnalyticsEvent>();
// Generate sample data
final batchData = List.generate(10000, (index) => AnalyticsEvent(
userId: 'user_${index % 1000}',
eventType: EventType.values[index % EventType.values.length],
timestamp: DateTime.now().subtract(Duration(minutes: index)),
properties: {
'session_id': 'session_${index ~/ 100}',
'page': '/page${index % 50}',
'duration': index * 1.5,
},
));
print('Inserting ${batchData.length} events...');
final stopwatch = Stopwatch()..start();
// Method 1: Transaction-based batch insert (recommended)
await events.saveAllInTransaction(batchData);
stopwatch.stop();
print('Batch insert completed in ${stopwatch.elapsedMilliseconds}ms');
print('Rate: ${(batchData.length / stopwatch.elapsedMilliseconds * 1000).toInt()} events/second');
// Method 2: Chunked batch processing for very large datasets
await chunkedBatchInsert(events, batchData, chunkSize: 1000);
} finally {
await database.close();
}
}
Future<void> chunkedBatchInsert<T>(
VektagrafList<T> list,
List<T> data,
{int chunkSize = 1000}
) async {
print('Processing ${data.length} items in chunks of $chunkSize...');
for (int i = 0; i < data.length; i += chunkSize) {
final chunk = data.skip(i).take(chunkSize);
await list.saveAllInTransaction(chunk);
print('Processed chunk ${(i ~/ chunkSize) + 1}/${(data.length / chunkSize).ceil()}');
// Optional: Add small delay to prevent overwhelming the system
if (i + chunkSize < data.length) {
await Future.delayed(Duration(milliseconds: 10));
}
}
print('Chunked batch insert completed');
}
Batch Updates with Concurrency Handling
Future<void> batchUpdateExample() async {
final database = VektagrafDatabaseImpl();
await database.open('users.db');
try {
final users = await database.objects<User>();
// Find users to update
final inactiveUsers = users.where((u) => !u.isActive).toList();
print('Found ${inactiveUsers.length} inactive users to update');
// Prepare updates with current revisions
final updatesWithRevisions = <User, int>{};
for (final user in inactiveUsers) {
final currentRevision = users.currentRevision(user)!;
final updatedUser = user.copyWith(
isActive: true,
lastLoginAt: DateTime.now(),
);
updatesWithRevisions[updatedUser] = currentRevision;
}
// Perform batch update with retry logic
await batchUpdateWithRetry(users, updatesWithRevisions);
} finally {
await database.close();
}
}
Future<void> batchUpdateWithRetry<T>(
VektagrafList<T> list,
Map<T, int> updatesWithRevisions,
{int maxRetries = 3}
) async {
var remainingUpdates = Map<T, int>.from(updatesWithRevisions);
int retryCount = 0;
while (remainingUpdates.isNotEmpty && retryCount < maxRetries) {
final failedUpdates = <T, int>{};
try {
await list.updateAll(remainingUpdates);
print('Batch update completed successfully');
break;
} on ConcurrencyConflictException catch (e) {
print('Concurrency conflicts detected, retrying...');
// Refresh revisions for failed objects and retry
for (final entry in remainingUpdates.entries) {
final object = entry.key;
final currentRevision = list.currentRevision(object);
if (currentRevision != null) {
failedUpdates[object] = currentRevision;
}
}
remainingUpdates = failedUpdates;
retryCount++;
// Exponential backoff
await Future.delayed(Duration(milliseconds: 100 * (1 << retryCount)));
}
}
if (remainingUpdates.isNotEmpty) {
print('Warning: ${remainingUpdates.length} updates failed after $maxRetries retries');
}
}
Fluent Method Chaining for Complex Queries
Building Complex Query Chains
Future<void> fluentQueryExample() async {
final database = VektagrafDatabaseImpl();
await database.open('social_network.db');
try {
final users = await database.objects<User>();
final posts = await database.objects<Post>();
// Complex query: Find active users who posted recently and have high engagement
final influencers = await users
.whereProperty('isActive', true)
.whereProperty('isVerified', true)
.wherePropertyRange<int>('followerCount', 1000, null)
.whereAsync((user) async {
// Check if user posted in last 7 days
final recentPosts = await posts
.whereProperty('authorId', user.id.toString())
.wherePropertyRange<DateTime>(
'createdAt',
DateTime.now().subtract(Duration(days: 7)),
null,
);
return recentPosts.isNotEmpty;
})
.orderByProperty<int>('followerCount', descending: true)
.take(50);
print('Found ${influencers.length} influencers');
// Relationship-based queries
final engagedUsers = await users
.whereHasRelation('posts')
.whereCountRelation('followers', 100)
.expandRelation<Post>('posts')
.whereProperty('likeCount', 50)
.distinct();
print('Found ${engagedUsers.length} highly engaged users');
// Vector similarity queries (covered in detail in Chapter 5)
final queryVector = [0.1, 0.2, 0.3, 0.4, 0.5];
final similarUsers = await users
.whereSimilar('profileVector', queryVector, limit: 20)
.whereProperty('isActive', true);
print('Found ${similarUsers.length} similar active users');
} finally {
await database.close();
}
}
Optimized Query Execution
Future<void> queryOptimizationExample() async {
final database = VektagrafDatabaseImpl();
await database.open('ecommerce.db');
try {
final products = await database.objects<Product>();
// The query optimizer automatically reorders operations for efficiency
final optimizedQuery = await products.executeChainedQuery([
QueryOperation(
type: QueryOperationType.where,
parameters: {'property': 'category', 'value': 'Electronics'},
),
QueryOperation(
type: QueryOperationType.where,
parameters: {'property': 'inStock', 'value': true},
),
QueryOperation(
type: QueryOperationType.orderBy,
parameters: {
'property': 'price',
'descending': false,
},
),
QueryOperation(
type: QueryOperationType.take,
parameters: {'count': 20},
),
]);
print('Optimized query returned ${optimizedQuery.length} products');
// Monitor query performance
final optimizer = database.queryOptimizer;
if (optimizer != null) {
final stats = optimizer.queryStats;
for (final entry in stats.entries) {
final stat = entry.value;
print('Query ${entry.key}:');
print(' Executions: ${stat.totalExecutions}');
print(' Avg time: ${stat.averageExecutionTime.inMilliseconds}ms');
print(' Avg results: ${stat.averageResultCount.toInt()}');
}
}
} finally {
await database.close();
}
}
Best Practices
Transaction Design Patterns
1. Keep Transactions Short and Focused
// ✅ Good: Short, focused transaction
Future<void> goodTransactionPattern() async {
await database.transaction((txn) async {
final account = await txn.getById<Account>(accountId);
final updatedAccount = account.copyWith(
balance: account.balance + amount,
);
await txn.update(account.id, updatedAccount, account.revision);
});
}
// ❌ Bad: Long-running transaction with external calls
Future<void> badTransactionPattern() async {
await database.transaction((txn) async {
final account = await txn.getById<Account>(accountId);
// Don't do this: external API call within transaction
final exchangeRate = await fetchExchangeRateFromAPI();
// Don't do this: complex calculations
final convertedAmount = performComplexCalculation(amount, exchangeRate);
final updatedAccount = account.copyWith(
balance: account.balance + convertedAmount,
);
await txn.update(account.id, updatedAccount, account.revision);
});
}
2. Handle Concurrency Conflicts Gracefully
Future<T> withRetryOnConflict<T>(
Future<T> Function() operation,
{int maxRetries = 3}
) async {
int attempts = 0;
while (attempts < maxRetries) {
try {
return await operation();
} on ConcurrencyConflictException catch (e) {
attempts++;
if (attempts >= maxRetries) {
rethrow;
}
// Exponential backoff with jitter
final delay = Duration(
milliseconds: (100 * (1 << attempts)) + Random().nextInt(50)
);
await Future.delayed(delay);
}
}
throw StateError('Should not reach here');
}
// Usage
final result = await withRetryOnConflict(() async {
return await database.transaction((txn) async {
// Your transaction logic here
});
});
3. Use Batch Operations for Better Performance
// ✅ Good: Batch operations within transaction
Future<void> efficientBatchOperation() async {
await database.transaction((txn) async {
final users = await txn.objects<User>();
// Process in batches
final batchSize = 1000;
for (int i = 0; i < newUsers.length; i += batchSize) {
final batch = newUsers.skip(i).take(batchSize);
await users.saveAll(batch);
}
});
}
// ❌ Bad: Individual transactions for each operation
Future<void> inefficientOperation() async {
for (final user in newUsers) {
await database.transaction((txn) async {
await txn.save(user);
});
}
}
Query Optimization Strategies
1. Use Property Queries for Indexed Fields
// ✅ Good: Uses indexes when available
final engineers = await users.whereProperty('department', 'Engineering');
// ❌ Less efficient: Full scan with predicate
final engineers = users.where((u) => u.department == 'Engineering');
2. Order Operations for Maximum Efficiency
// ✅ Good: Filter first, then sort and limit
final results = await users
.whereProperty('isActive', true) // Filter early
.wherePropertyRange('age', 25, 65) // Further filter
.orderByProperty('createdAt') // Sort reduced dataset
.take(10); // Limit final results
// ❌ Less efficient: Sort everything first
final results = await users
.orderByProperty('createdAt') // Sorts all users
.whereProperty('isActive', true) // Filters sorted results
.take(10);
3. Monitor and Analyze Query Performance
Future<void> monitorQueryPerformance() async {
final stopwatch = Stopwatch()..start();
final results = await users.whereProperty('department', 'Engineering');
stopwatch.stop();
if (stopwatch.elapsedMilliseconds > 100) {
print('Slow query detected: ${stopwatch.elapsedMilliseconds}ms');
print('Consider adding index for department field');
}
// Check query optimizer statistics
final optimizer = database.queryOptimizer;
if (optimizer != null) {
final stats = optimizer.queryStats['property_department'];
if (stats != null && stats.averageExecutionTime.inMilliseconds > 50) {
print('Department queries are consistently slow');
print('Average execution time: ${stats.averageExecutionTime.inMilliseconds}ms');
}
}
}
Error Handling and Recovery
1. Comprehensive Error Handling
Future<void> robustDatabaseOperation() async {
try {
await database.transaction((txn) async {
// Your transaction logic here
});
} on ConcurrencyConflictException catch (e) {
// Handle concurrency conflicts
print('Concurrency conflict: ${e.message}');
// Implement retry logic or conflict resolution
} on StorageException catch (e) {
// Handle storage-related errors
print('Storage error: ${e.message}');
// Implement recovery or fallback logic
} on ValidationException catch (e) {
// Handle validation errors
print('Validation error: ${e.message}');
// Return user-friendly error message
} catch (e, stackTrace) {
// Handle unexpected errors
print('Unexpected error: $e');
print('Stack trace: $stackTrace');
// Log error for debugging
}
}
2. Graceful Degradation
Future<List<User>> getUsersWithFallback() async {
try {
// Try optimized query first
return await users.whereProperty('isActive', true);
} on Exception catch (e) {
print('Optimized query failed, falling back to simple query: $e');
// Fallback to basic filtering
return users.where((u) => u.isActive).toList();
}
}
Advanced Topics
Custom Transaction Isolation
// Custom transaction wrapper with specific isolation behavior
class CustomTransactionManager {
final VektagrafDatabase _database;
CustomTransactionManager(this._database);
Future<T> withReadCommitted<T>(
Future<T> Function(VektagrafTransaction) operation
) async {
// Implement read-committed isolation level
return await _database.transaction((txn) async {
// Custom isolation logic here
return await operation(txn);
});
}
Future<T> withSerializable<T>(
Future<T> Function(VektagrafTransaction) operation
) async {
// Implement serializable isolation level
return await _database.transaction((txn) async {
// Custom serialization logic here
return await operation(txn);
});
}
}
Performance Monitoring and Metrics
class DatabaseMetrics {
final Map<String, OperationStats> _operationStats = {};
Future<T> measureOperation<T>(
String operationName,
Future<T> Function() operation,
) async {
final stopwatch = Stopwatch()..start();
try {
final result = await operation();
stopwatch.stop();
_recordSuccess(operationName, stopwatch.elapsed);
return result;
} catch (e) {
stopwatch.stop();
_recordFailure(operationName, stopwatch.elapsed, e);
rethrow;
}
}
void _recordSuccess(String operation, Duration duration) {
final stats = _operationStats.putIfAbsent(
operation,
() => OperationStats(operation)
);
stats.recordSuccess(duration);
}
void _recordFailure(String operation, Duration duration, dynamic error) {
final stats = _operationStats.putIfAbsent(
operation,
() => OperationStats(operation)
);
stats.recordFailure(duration, error);
}
Map<String, OperationStats> get stats => Map.unmodifiable(_operationStats);
}
class OperationStats {
final String operationName;
int successCount = 0;
int failureCount = 0;
Duration totalDuration = Duration.zero;
Duration maxDuration = Duration.zero;
Duration minDuration = Duration(days: 1);
OperationStats(this.operationName);
void recordSuccess(Duration duration) {
successCount++;
totalDuration += duration;
if (duration > maxDuration) maxDuration = duration;
if (duration < minDuration) minDuration = duration;
}
void recordFailure(Duration duration, dynamic error) {
failureCount++;
totalDuration += duration;
}
double get successRate =>
(successCount + failureCount) > 0
? successCount / (successCount + failureCount)
: 0.0;
Duration get averageDuration =>
(successCount + failureCount) > 0
? Duration(microseconds: totalDuration.inMicroseconds ~/ (successCount + failureCount))
: Duration.zero;
}
Reference
Core Database Operations API
VektagrafDatabase Methods
abstract class VektagrafDatabase {
// Connection management
Future<void> open(String path, {VektagrafConfig? config});
Future<void> close();
// Object access
Future<VektagrafList<T>> objects<T>();
// Transaction management
Future<R> transaction<R>(Future<R> Function(VektagrafTransaction) operation);
// Graph and vector access
VektagrafGraph get graph;
VektagrafVectorSpace vectorSpace(String name, int dimensions);
// Status and configuration
bool get isOpen;
VektagrafConfig? get config;
}
VektagrafTransaction Methods
abstract class VektagrafTransaction {
// Object operations
Future<List<T>> objects<T>();
Future<VektagrafId> save<T>(T object);
Future<VektagrafId> update<T>(VektagrafId id, T object, int expectedRevision);
Future<List<VektagrafId>> saveAll<T>(Iterable<T> objects);
Future<void> remove(VektagrafId id);
Future<void> removeAll(Iterable<VektagrafId> ids);
Future<T?> getById<T>(VektagrafId id);
// Transaction control
Future<void> commit();
Future<void> rollback();
// Status
bool get isActive;
bool get isCommitted;
bool get isRolledBack;
}
VektagrafList Methods
class VektagrafList<T> extends ListBase<T> {
// Database operations
Future<VektagrafId> save(T object);
Future<void> saveAll(Iterable<T> objects);
Future<void> saveAllInTransaction(Iterable<T> objects);
Future<VektagrafId> update(T object, int expectedRevision);
Future<void> updateAll(Map<T, int> objectsWithRevisions);
Future<void> removeObject(T object);
Future<void> removeAllInTransaction(Iterable<T> objects);
// Query operations
Future<VektagrafList<T>> whereProperty(String propertyName, dynamic value);
Future<VektagrafList<T>> wherePropertyRange<V extends Comparable<V>>(
String propertyName, V? minValue, V? maxValue);
Future<VektagrafList<T>> orderByProperty<V extends Comparable<V>>(
String propertyName, {bool descending = false, int? limit});
Future<VektagrafList<T>> executeChainedQuery(List<QueryOperation> operations);
// Async operations
Future<VektagrafList<T>> whereAsync(Future<bool> Function(T) test);
Future<VektagrafList<R>> expandAsync<R>(Future<Iterable<R>> Function(T) f);
Future<VektagrafList<T>> orderByAsync<K extends Comparable<dynamic>>(
Future<K> Function(T) keyExtractor, {bool descending = false});
// Relationship operations
Future<VektagrafList<R>> expandRelation<R>(String relationName);
Future<VektagrafList<T>> whereRelated<R>(String relationName, bool Function(R) relationTest);
Future<VektagrafList<T>> whereHasRelation(String relationName);
Future<VektagrafList<T>> whereCountRelation(String relationName, int count);
// Vector operations
Future<VektagrafList<T>> whereSimilar(String vectorField, List<double> queryVector,
{int limit = 10, double? threshold});
Future<VektagrafList<T>> whereSimilarTo(T referenceObject, String vectorField,
{int limit = 10});
// Metadata access
VektagrafId? idOf(T object);
int? revisionOf(T object);
DateTime? createdAt(T object);
DateTime? updatedAt(T object);
int? currentRevision(T object);
// Transaction support
Future<R> withTransaction<R>(Future<R> Function(VektagrafList<T>) operation);
}
Configuration Options
class VektagrafConfig {
// Performance settings
final int maxMemoryBytes;
final int maxConcurrentTransactions;
final Duration transactionTimeout;
// Concurrency settings
final int maxRetryAttempts;
final Duration baseRetryDelay;
final bool enableAutoConflictResolution;
// Query optimization
final bool enableQueryCache;
final int maxCacheSize;
final Duration defaultCacheTtl;
final bool enableQueryOptimization;
// Storage settings
final bool enableCompression;
final bool enableEncryption;
final Duration syncInterval;
}
Error Types and Handling
// Concurrency-related exceptions
class ConcurrencyConflictException implements Exception {
final VektagrafId objectId;
final int expectedRevision;
final int actualRevision;
final String message;
}
// Storage-related exceptions
class StorageException implements Exception {
final String message;
final dynamic cause;
}
// Validation exceptions
class ValidationException implements Exception {
final String message;
final Map<String, String> fieldErrors;
}
// Transaction exceptions
class TransactionException implements Exception {
final String message;
final TransactionState state;
}
Summary
This chapter covered the comprehensive database operations and transaction system in Vektagraf. Key takeaways include:
- Object-Centric Design: All operations work with native Dart objects while providing automatic metadata management
- Fluent API: VektagrafList provides natural Dart collection behavior with powerful database operations
- ACID Transactions: Full transaction support with proper isolation, concurrency control, and error handling
- Performance Optimization: Built-in query optimization, caching, and batch operation support
- Concurrency Control: Optimistic locking with automatic conflict detection and resolution strategies
Next Steps
- Chapter 5: Learn about vector search and similarity operations for AI/ML applications
- Chapter 6: Explore graph operations and relationship modeling for complex data relationships
- Chapter 7: Dive deep into query optimization and performance tuning techniques
No Comments