scanKeys method

  1. @override
Future<Stream<String>> scanKeys(
  1. KeyPattern pattern, {
  2. bool includeExpired = false,
  3. OrderByKey? orderBy,
  4. int? limit,
  5. int? skip,
})
override

Stream the keystore keys that match pattern. Backend- portable successor to KeyValueStore.getKeys for callers that want structured filtering rather than building regular expressions.

The return type is Stream<String> (rather than Stream<K>): KeyPattern's fields — sharedBy, sharedWith, namespace, idPrefix — are atKey-shaped, so the result is inherently a stream of atKey strings.

By default, expired or not-yet-born keys are excluded; pass includeExpired: true to surface them.

orderBy controls the result order. null (default) means "the backend's natural order". limit caps the number of keys yielded; skip discards the first N.

The returned Future completes once the backend has accepted the request; the Stream then yields the matching keys.

Implementation

@override
Future<Stream<String>> scanKeys(
  KeyPattern pattern, {
  bool includeExpired = false,
  OrderByKey? orderBy,
  int? limit,
  int? skip,
}) async {
  var where = '1=1';
  final params = <Object?>[];
  if (!includeExpired) {
    final now = _nowMillis();
    where +=
        ' AND (available_at IS NULL OR available_at <= ?) AND (expires_at IS NULL OR expires_at > ?)';
    params
      ..add(now)
      ..add(now);
  }
  final order = switch (orderBy) {
    OrderByKey.byKey => ' ORDER BY atkey',
    OrderByKey.byExpiresAt =>
      ' ORDER BY (expires_at IS NULL), expires_at', // nulls last
    OrderByKey.byCreatedAt =>
      " ORDER BY json_extract(metadata, '\$.createdAt')",
    null => '',
  };
  final rows =
      _db.raw.select('SELECT atkey FROM at_data WHERE $where$order;', params);
  // KeyPattern filter, then skip/limit (limit is post-filter, matching
  // the Hive semantics).
  var matched = rows
      .map((r) => r['atkey'] as String)
      .where((k) => SqliteQueryTranslator.matchesKeyPattern(k, pattern));
  if (skip != null) matched = matched.skip(skip);
  if (limit != null) matched = matched.take(limit);
  return Stream.fromIterable(matched.toList());
}