query method

Future<List<T>> query(
  1. DatumQuery query, {
  2. DataSource source = DataSource.local,
  3. String? userId,
  4. bool includeDeleted = false,
})

Implementation

Future<List<T>> query(
  DatumQuery query, {
  DataSource source = DataSource.local,
  String? userId,
  bool includeDeleted = false,
}) async {
  _ensureInitialized();

  // Tombstones are filtered via query pushdown so limit/offset count only
  // live rows. Remote queries are passed through untouched — translation
  // capabilities vary by remote adapter.
  if (!includeDeleted && source == DataSource.local) {
    query = _excludeTombstones(query);
  }

  // Create a cache key for this query
  final cacheKey = _cacheCoordinator.createQueryCacheKey(query, source, userId);

  // Local query caching is opt-in (config.enableQueryCache, default false):
  // the local DB is already fast, and caching returned shared, mutable
  // instances that could go stale (external sync/realtime writes) and break
  // reactive updates. Only for local queries without related entities.
  final useQueryCache = config.enableQueryCache && source == DataSource.local && query.withRelated.isEmpty;
  if (useQueryCache) {
    final cached = _cacheCoordinator.getCachedQuery(cacheKey);
    if (cached != null) {
      _logger.debug('Using cached query results for key: $cacheKey');
      return Future.wait(cached.map(_applyPostFetchTransforms));
    }
  }

  final adapter = (source == DataSource.local ? localAdapter : remoteAdapter) as dynamic;
  final entities = await adapter.query(query, userId: userId) as List<T>;

  if (query.withRelated.isNotEmpty && entities.isNotEmpty) {
    await _fetchAndStitchRelations(entities, query.withRelated, source, userId);
  }

  // Cache the results (only when query caching is enabled).
  if (useQueryCache) {
    _cacheCoordinator.cacheQuery(cacheKey, entities);
  }

  // Apply post-fetch transforms with error handling
  final transformedEntities = <T>[];
  for (final entity in entities) {
    try {
      final transformed = await _applyPostFetchTransforms(entity);
      transformedEntities.add(transformed);
    } catch (e, stack) {
      _logger.error('Failed to apply post-fetch transforms to entity ${entity.id}: $e', stack);
      // Continue with other entities instead of failing the entire operation
      transformedEntities.add(entity); // Use original entity if transform fails
    }
  }
  return transformedEntities;
}