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

Change Listener proposal #16

Open
richard457 opened this issue May 5, 2021 · 1 comment
Open

Change Listener proposal #16

richard457 opened this issue May 5, 2021 · 1 comment

Comments

@richard457
Copy link
Contributor

richard457 commented May 5, 2021

Working with data change on Query is challenging and we have an existing API for listening changes on Query, but the problem is that it is extremely hard to use it in some conditions for example this
`
@OverRide
Stream get({required String variantId}) async* {
Q2.parameters = {'T': AppTables.stock, 'ID': variantId};

Q2.addChangeListener((results) {
  for (Map map in results.allResults) {
    controller.add(sstockFromJson(jsonEncode(map)));
  }
});

}`
The above code can not work since the stream did not yield any data and we can not add the yield inside the addChangeListner
What if the change listener returns a Stream of changes instead? this would simplify a lot of scenarios

Proposed new API
`

Q2.addChangeListener((results)async * {
for (Map map in results.allResults) {
yield sstockFromJson(jsonEncode(map));
}
});`

@Rudiksz
Copy link
Owner

Rudiksz commented May 5, 2021

First, I recommend you stop using the allResults method of the resultset. I kept it for backwards compatibility, but it's more efficient to parse the records using the next() and rowDict() or rowArray() methods.

You are trying to use generator functions but even the documentation says that they are useful for only simple use cases.

If the events of your stream comes from different parts of your program, and not just from a stream or futures that can traversed by an async function, then use a StreamController to create and populate the stream.

You should consider that the Result set is only a pointer to an underlying C memory and rows are only read into dart memory when you access them with rowDict or rowArray, and if you use the FLDict and FLArray's returned by them, you could manipulate the C memory directly without duplicating the object in Dart. Although it is a lot easier to transform the Fleece object sto Dart classes and work with those - which is what the allResults method does.

With that being said, the callbacks in the changelistener capture the surrounding context so it's fairly easy to turn the result set into streams, ChangeNotifiers, Blocs, Mobx observables, or whatever.
Take this example for instance. The changes are published on a stream and you can set up listeners however you like. Te streamcontroller also give you full control over how you handle the changes.

void main() async {
  initializeCblC();
  if (!Directory(TESTDIR).existsSync()) {
    Directory(TESTDIR).createSync();
  }

  var db = Database('query5', directory: TESTDIR);

  db.saveDocument(Document('testdoc1', data: {'foo': 'bar', 'version': '1'}));

  final query = Query(db, 'SELECT * WHERE foo LIKE "%ba%"');

  final changesStreamController = StreamController<String>();
  final changesStream = changesStreamController.stream.asBroadcastStream();
  
  changesStream.listen((change) {
    print('------inline listener----');
    print(change);
  });

  // OR

  asyncListener(changesStream);

  var token = query.addChangeListener((results) {
    while (results.next()) {
      changesStreamController.sink.add(results.rowDict.json);
    }
  });

  await Future.delayed(Duration(seconds: 2));
  db.saveDocument(Document('testdoc2', data: {'foo': 'baz', 'version': '1'}));

  await Future.delayed(Duration(seconds: 3));
  db.saveDocument(Document('testdoc2', data: {'foo': 'baz', 'version': '2'}));

  await Future.delayed(Duration(seconds: 1));
  db.saveDocument(Document('testdoc1', data: {'foo': 'baz', 'version': '2'}));
}

asyncListener(Stream<String> stream) async {
  await for (String queryChange in stream) {
    print('------listener function----');
    print(queryChange);
  }
}

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