realm-dart
realm-dart copied to clipboard
[Bug]: Sort not working
What happened?
Maybe I missed something in the documentation, but I can not manage to sort within my Realm query.
According to the documentation, my syntax should be correct
final sortedCars = realm.query<Car>('TRUEPREDICATE SORT(model ASC)');
Repro steps
- Query for collection with sort clause
- Get error
- Try without the Sort -> It works
Version
Channel stable, 3.3.2, on Microsoft Windows [Version 10.0.19043.2006], locale de-AT
What Realm SDK flavor are you using?
MongoDB Atlas (i.e. Sync, auth, functions)
What type of application is this?
Flutter Application
Client OS and version
0.5.0+beta
Code snippets
RealmResults<Pet> petQuery = realm.query<Pet>("ownerId == oid($oid) SORT(createdAt DESC)");
Stacktrace of the exception/crash you're getting
No response
Relevant log output
[INFO] Realm: Connection[1]: Session[1]: Received QUERY_ERROR "Client provided query with bad syntax: invalid RQL for table "Pet": syntax error: unexpected Sort, expecting Or or RightParenthesis" (error_code=300, query_version=623)
The issue here is that you're subscribing to a sorted results which is not supported. You can still create a sorted query, just can't use it when adding subscriptions.
@nielsenko I can see that we're explicitly recreating the query with the sort descriptor in the C API: https://github.com/realm/realm-core/blob/f09d279ebd32ef49a82950283cb86c982829f659/src/realm/object-store/c_api/sync.cpp#L706 Do you remember why we're doing that as we can silently drop the sort descriptor and things would just work?
Thanks for the response. My user has a pets array tho. How am I supposed to access this in a sorted way then?
As I would access it over user.pets, and sorting that would require a realm write to sort them locally.
@nielsenko I can see that we're explicitly recreating the query with the sort descriptor in the C API: https://github.com/realm/realm-core/blob/f09d279ebd32ef49a82950283cb86c982829f659/src/realm/object-store/c_api/sync.cpp#L706 Do you remember why we're doing that as we can silently drop the sort descriptor and things would just work?
@nirinchev I wasn't actually aware of this restriction in core. I think it is allowed in some cases, I subscribe to TRUEPREDICATE SORT(time DESCENDING)
in the time_track sample, and that works fine. I do have an index on time, which may explain why.
Dropping the descriptor on subscribe, and say it would just work might be a stretch. Yes we could then subscribe for changes, but the results would still not be sorted, or am I mistaken? Isn't it actually better to fail?
@Navil could you describe your model in more details. Perhaps we can find a workaround.
@nielsenko
I simplified the model as much as possible:
class User {
final List<_Pet> pets = [];
}
class _Pet {
late final String name;
late final ObjectId ownerId;
}
I know want to show all pets in the UI sorted by their name.
- Create a subscription for all pets (ownerId == user.id)
- Access user.pets in the UI
- The list is not sorted
So in case I want to sort it via code, I will also change the order on the server side - which is not the biggest problem, but not very clean, as I just need them sorted on the frontend (and it feels like useless server updates). If I create a copy of the list and sort it afterwards, I lose the reference to the original objects.
Hope I could put my problem into words well enough.
@nielsenko the query being used on subscribe is a copy of the query that results are created with. So dropping the descriptor on line 706 would not alter the original results:
final results = realm.all<Pet>('TRUEPREDICATE SORT(name)');
realm.subscriptions.update((mutable) {
mutable.add(results); // <-- query is copied here
});
for (final pet in results) { // <-- results is still sorted
print(pet.name);
}
@Navil you can do something like this:
final ownedPets = realm.all<Pet>('ownerId = $0', [user.id]);
realm.subscriptions.update((mutable) {
mutable.add(ownedPets);
});
await realm.subscriptions.waitForSynchronization();
final sortedPets = user.pets.query('TRUEPREDICATE SORT(name)');
Sorry, I'm getting tired. I thought you where talking about subscribing to the query.changes
stream. Yes, for setting up flex sync subscriptions on the server it does not make sense to request a sort.
You should be able to do sortedPets.changes.listen((c) => c.results..) // <-- should be sorted
@nirinchev Sometimes the solution can be that easy :) Thank you very much, this is a very clean workaround.
@nielsenko Thank you. That solution worked. In my case it is sufficient to be synchronously fetched, no need for a listener in my case. I guess I got a bit confused by thinking that the subscription would fetch data in a sorted way (e.g. to load some entries with priority), but that explains a lot.
This solved my issue. Feel free to close this issue if that error is intended.
I guess we should discuss internally on the team, and with the kotlin team as well, if we want to strip the descriptor to avoid this error. Let us keep it open for now.
I have the same issue.
@RedFox64 Let me recap.
Don't use descriptors (ie. SORT
, LIMIT
, etc.) on the queries you use when setting up subscriptions.
Use a them instead on the queries you use to read from the local database.
Repeating @nirinchev's workaround:
final ownedPets = realm.all<Pet>('ownerId = $0', [user.id]);
realm.subscriptions.update((mutable) {
mutable.add(ownedPets); // <-- this query doesn't use sort
});
await realm.subscriptions.waitForSynchronization();
final sortedPets = user.pets.query('TRUEPREDICATE SORT(name)'); // <-- this does use sort
The reason we have kept this issue open, is that we may choose to strip the descriptor (here SORT
) automatically when subscribing.
We have chosen not to strip descriptors on subscription queries, informing the user of the error as today. Especially since limit
is not supported, which would significantly change the returned result, it would be surprising to drop the descriptor silently.