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

Add support for Keypath filtering in notifications #1547

Merged
merged 27 commits into from
Dec 1, 2023

Conversation

cmelchior
Copy link
Contributor

@cmelchior cmelchior commented Oct 13, 2023

Closes #661

This PR adds support for keypath filtering which makes it possible to select which fields should trigger the notifications, and enable notifications more than 4 levels deep.

TODO

  • Add tests for query flows
  • Add tests for single object query flows
  • Figure out how to do our internal interface, I.e. some do not support keypaths like Realm and aggregate flows. Either add two different interfaces or have 1 but just ignore the keypath for some notifications (internally)
  • Add support for passing the keypath to the C-API.
  • Should the public API use List<String>(), List<String>? or vararg String. Right now I'm leaning towards List<String>? but should be discussed.
  • Merge Rework KeyPathArray filters for notifications in the C-API. realm-core#7087 in Core

@cmelchior
Copy link
Contributor Author

This PR depends on realm/realm-core#7087 being merged first.

Christian Melchior added 11 commits November 5, 2023 20:47
# Conflicts:
#	packages/external/core
#	packages/jni-swig-stub/realm.i
#	packages/jni-swig-stub/src/main/jni/realm_api_helpers.cpp
#	packages/jni-swig-stub/src/main/jni/realm_api_helpers.h
# Conflicts:
#	packages/external/core
@@ -143,7 +143,9 @@ bool migration_callback(void *userdata, realm_t *old_realm, realm_t *new_realm,
// TODO OPTIMIZE Abstract pattern for all notification registrations for collections that receives
// changes as realm_collection_changes_t.
realm_notification_token_t *
register_results_notification_cb(realm_results_t *results, jobject callback) {
register_results_notification_cb(realm_results_t *results,
int64_t key_path_array_ptr,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we use void* on pointers?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We cannot in this case, as this is methods being called from the JVM which doesn't have that concept. We are using 0 in that case

reinterpret_cast<realm_object_t*>(collection_ptr),
user_data, // Use the callback as user data
user_data_free,
NULL, // See https://github.com/realm/realm-kotlin/issues/661
(key_path_array_ptr == 0) ? NULL : reinterpret_cast<realm_key_path_array_t*>(key_path_array_ptr),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this nullability check required?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strictly no, but it makes it easier to understand what is going on IMO

@cmelchior cmelchior marked this pull request as ready for review November 16, 2023 07:58
Copy link
Contributor

@rorbech rorbech left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really nice. I have a number of comments around internals but most notably around tests.

*/

internal interface KeyPathFlowable<T> {
fun asFlow(keyPaths: List<String>? = null): Flow<T>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason for using List and not vararg?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the internal interface that just mirrors the public API which uses List. I was considering whether to use vararg or List in the public API but ended up with List as it makes it easier to extend the API later. I.e. there is the potential that projections will end up adding arguments to the asFlow method as well.

Copy link
Collaborator

@nhachicha nhachicha left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 🎉 minor comments 👍

findLatest(results.first())!!.booleanField = true
}
realm.write {
// Update field that should trigger a notification
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not following here, you're navigating to the top parent and updating it, whilst the query is on the leaf element? this should not trigger the notification while the previous update (booleanField = true) should ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, because I used the keypath listOf("objectBacklinks.objectBacklinks.objectBacklinks.objectBacklinks.objectBacklinks.stringField") which only consider changes through the backlinks as things that should trigger a notification

* objects inside the set will result in a change being emitted. Nested properties can be
* defined using a dotted syntax, e.g. `parent.child.name`. If no keypaths are provided, changes
* to all top-level properties and nested properties up to 4 levels down will trigger a change.
* Keypaths are only supported for sets of objects.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why only supported for sets of objects? it's also supported for List, RealmResults etc...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is, but this doc is on the RealmSet. They key thing I wanted to spell out was that keyPaths only works on RealmSet<RealmObject> but maybe it could be clarified better?

@@ -61,7 +63,7 @@ internal abstract class ManagedRealmMap<K, V> constructor(
internal val parent: RealmObjectReference<*>,
internal val nativePointer: RealmMapPointer,
val operator: MapOperator<K, V>
) : AbstractMutableMap<K, V>(), RealmMap<K, V>, CoreNotifiable<ManagedRealmMap<K, V>, MapChange<K, V>>, Flowable<MapChange<K, V>> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why removing Flowable interface?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was an internal interface that matched our public API. But with the KeyPath changes that is no longer the case and is actually harmful instead now as Notifier cannot use it to handle all flows the same.

const jclass clazz = jenv->FindClass("java/lang/String");

while ($1[len]) len++;
jresult = jenv->NewObjectArray(len, clazz, NULL);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check for OOM

Copy link
Contributor

@rorbech rorbech left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some minor comments about nested runBlocking, etc.. Just wanted to highlight it, but don't think we need to bother fixing it and rerunning CI ... unless there is other reasons for doing so.

:shipit:

* to that type, otherwise an IllegalArgumentException is thrown with the provided error message.
*/
@OptIn(ExperimentalContracts::class)
public inline fun <reified T : Any?> isType(arg: Any?, errorMessage: String) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🥇

Christian Melchior added 2 commits December 1, 2023 14:21
@cmelchior cmelchior merged commit 3ec94a4 into main Dec 1, 2023
1 of 2 checks passed
@cmelchior cmelchior deleted the cm/keypath-notifications branch December 1, 2023 13:54
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 14, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add support for keypath filtering
4 participants