Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stream not updating when data changes in sembast database? #394

Open
normidar opened this issue Oct 23, 2024 · 2 comments
Open

Stream not updating when data changes in sembast database? #394

normidar opened this issue Oct 23, 2024 · 2 comments

Comments

@normidar
Copy link

I am observing a Stream implementation like this:

  Stream<List<DbResultMap>> getStream(
      {required DbPath path, QueryFinder? finder, String? putIdOn}) {
    final store = _getStore(path: path);
    final query = store.query(
        finder: finder == null ? null : QueryTool.translate(finder));

    return query.onSnapshots(db).map((dbResult) {
      return dbResult
          .map((e) => DbResultMap(id: e.key.toString(), data: e.value))
          .toList();
    });
  }

However, when I update the data using this method:

  @override
  Future<void> update(
      {required DbPath path,
      required String id,
      required Map<String, Object?> data}) async {
    final store = _getStore(path: path);
    final d = Utils.prepareAddData(data);
    await store.record(id).put(db, d);
  }

The Stream is not notified of the changes. (Data was changed.)

@alextekartik
Copy link
Collaborator

That is indeed not normal and that's typically where sembast should be good at as it will update only if needed (i.e. if a record added matches the query or if an existing record in the query was modified or deleted), basically only if the query results need to be updated).

Can you confirm that indeed the record added (or modified) matches the query?

I know it is a pain but if you could manage to reproduce the issue in a unit test, that would help. Below is an example on how this could be tested:

import 'dart:async';

import 'package:sembast/sembast_memory.dart';
import 'package:test/test.dart';

void main() {
  late Database db;
  setUp(() async {
    /// Open the database (use memory)
    var factory = databaseFactoryMemory;
    db = await factory.openDatabase(sembastInMemoryDatabasePath);
  });
  tearDown(() async {
    await db.close();
  });
  test('onSnapshot', () async {
    // Current records
    late List<RecordSnapshot> currentQueryRecords;

    // Key is an int, value is a map
    var store = intMapStoreFactory.store();

    // Create a query to test records with 'key1' = 'value1'
    // Ordered by keys
    var query = store.query(
        finder: Finder(
            filter: Filter.equals('key1', 'value1'),
            sortOrders: [SortOrder(Field.key)]));

    // Add some data
    await db.transaction((txn) async {
      // matches
      await store.record(1).put(txn, {'key1': 'value1'});
      // does not match
      await store.record(2).put(txn, {'key1': 'value2'});
      // matches
      await store.record(3).put(txn, {'key1': 'value1'});
    });

    // create test completers when we have 2 records
    var count2Completer = Completer<void>();
    // create test completers when we have 3 records
    var count3Completer = Completer<void>();

    /// Listen to the query snapshots
    var subscription = query.onSnapshots(db).listen((records) {
      // For debugging purpose
      print('records $records');
      currentQueryRecords = records;
      if (records.length == 2 && !count2Completer.isCompleted) {
        count2Completer.complete();
      } else if (records.length == 3 && !count3Completer.isCompleted) {
        count3Completer.complete();
      }
    });

    // Wait for the first 2 records
    await count2Completer.future;
    // Record 1 and 3 matches
    expect(currentQueryRecords.map((snapshot) => snapshot.key), [1, 3]);

    // Modify 3 records, modify record 2 to match, delete record 3, add record 4
    // 3 rcords should match then
    await db.transaction((txn) async {
      // matches
      await store.record(2).put(txn, {'key1': 'value1'});
      // matches
      await store.record(3).delete(txn);
      // matches
      await store.record(4).put(txn, {'key1': 'value1'});
    });

    // Wait for the query to match 3 records
    await count3Completer.future;
    // Record 1, 2 and 4 matches
    expect(currentQueryRecords.map((snapshot) => snapshot.key), [1, 2, 4]);
    await subscription.cancel();
    await db.close();
  });
}

@normidar
Copy link
Author

Thanks, I will do that tonight.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants