realm-dart icon indicating copy to clipboard operation
realm-dart copied to clipboard

[Bug]: Sort not working

Open Navil opened this issue 2 years ago • 8 comments

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

  1. Query for collection with sort clause
  2. Get error
  3. 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)

Navil avatar Oct 13 '22 14:10 Navil

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?

nirinchev avatar Oct 13 '22 15:10 nirinchev

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.

Navil avatar Oct 13 '22 15:10 Navil

@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 avatar Oct 13 '22 15:10 nielsenko

@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.

Navil avatar Oct 13 '22 16:10 Navil

@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)');

nirinchev avatar Oct 13 '22 16:10 nirinchev

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

nielsenko avatar Oct 13 '22 16:10 nielsenko

@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.

Navil avatar Oct 13 '22 16:10 Navil

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.

nielsenko avatar Oct 13 '22 17:10 nielsenko

I have the same issue.

RedFox64 avatar Dec 01 '22 11:12 RedFox64

@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.

nielsenko avatar Dec 01 '22 11:12 nielsenko

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.

nielsenko avatar Dec 13 '22 13:12 nielsenko