Skip to main content

Query Operations

QuantaDB provides a powerful query engine that allows you to filter, sort, and paginate your data efficiently. This section covers the various query operations available in QuantaDB.

Basic Query Setup​

First, you'll need to create a QueryEngine instance from your database storage:

final queryEngine = QueryEngine(db.storage);

Query Types and Examples​

1. Basic Filtering​

Filter records based on a condition:

// Query active users
final activeUsers = await queryEngine.query<User>(
Query<User>().where((user) => user.isActive),
);
print('Active users: ${activeUsers.length}');

2. Sorting​

Sort records based on a field:

// Sort users by last login time
final sortedUsers = await queryEngine.query<User>(
Query<User>().sortBy((user) => user.lastLogin),
);
print('Users sorted by last login: ${sortedUsers.length}');

3. Pagination​

Retrieve a specific subset of records:

// Get 2 users, skipping the first one
final paginatedUsers = await queryEngine.query<User>(
Query<User>().take(2).skip(1),
);
print('Paginated users: ${paginatedUsers.length}');

4. Complex Queries​

Combine multiple conditions in a single query:

// Complex query with multiple conditions
final complexQuery = await queryEngine.query<User>(
Query<User>()
.where((user) => user.isActive)
.where((user) => user.email.contains('example.com'))
.sortBy((user) => user.name),
);
print('Complex query results: ${complexQuery.length}');

Reactive Queries​

QuantaDB supports reactive queries that automatically update when the underlying data changes. This is useful for real-time applications:

1. Watching Individual Records​

// Watch for changes in active users
final activeUsersStream = queryEngine.watch<User, User>(
Query<User>().where((user) => user.isActive),
);

// Listen to the stream
final subscription = activeUsersStream.listen(
(user) => print('Active user updated: ${user.name}'),
onError: (error) => print('Error in active users stream: $error'),
);

// Don't forget to cancel the subscription when done
subscription.cancel();

2. Watching Aggregated Data​

// Watch aggregated user statistics
final userStatsStream = queryEngine.watch<User, Map<String, dynamic>>(
Query<User>().aggregate((users) {
return {
'total': users.length,
'active': users.where((u) => u.isActive).length,
'domains': users.map((u) => u.email.split('@').last).toSet().length,
};
}),
);

// Listen to the stream
final subscription = userStatsStream.listen(
(stats) => print('User stats updated: $stats'),
onError: (error) => print('Error in stats stream: $error'),
);

// Don't forget to cancel the subscription when done
subscription.cancel();

Best Practices​

  1. Type Safety: Always specify the type parameter when querying to ensure type safety:

    queryEngine.query<User>(...)
  2. Error Handling: Wrap queries in try-catch blocks to handle potential errors:

    try {
    final results = await queryEngine.query<User>(...);
    } catch (e) {
    print('Query error: $e');
    }
  3. Resource Management: Always cancel reactive query subscriptions when they're no longer needed to prevent memory leaks.

  4. Query Optimization:

    • Use specific conditions in where clauses
    • Limit the number of records returned using take
    • Use skip for pagination instead of retrieving all records

Example Data Structure​

For the examples above, we used a User model that looks like this:

class User {
final String id;
String name;
String email;
bool isActive;
DateTime lastLogin;

User({
required this.id,
required this.name,
required this.email,
required this.isActive,
required this.lastLogin,
});
}