Rajendra
Rajendra

Reputation: 504

Is there a way to conditional sort in two fields in Realm?

I have a Rating Realm objects list as Follows:

public class RatingRealmObject extends RealmObject {

    @SerializedName("average")
    private float average;
    @SerializedName("raters")
    private int raters;

    public float getAverage() {
        return average;
    }

    public void setAverage(float average) {
        this.average = average;
    }

    public int getRaters() {
        return raters;
    }

    public void setRaters(int raters) {
        this.raters = raters;
    }
}

I want to sort the RealmResultList by descending order of average but the number of raters should be greater than or equal to 5.

e.g. If we have two recipes as A and B.

then after sort, A should come first in the list.

Any help how to make the condition in realm query.

Upvotes: 0

Views: 1764

Answers (3)

Rajendra
Rajendra

Reputation: 504

@EpicPandaForce and @beeender Thank you guys, You both helped me alot to solve this problem.
As I am using GSON library for the deserialization so as per GSON guidelines setter method does not call. So additional field computation in Rating object didn't work for me.

I have solved my problem by the first solution provided by @EpicPandaForce.

List<RecipeRealmObject> ratingMoreThan5List = recipeRealmResult.where().greaterThanOrEqualTo("rating.raters", 5).findAllSorted("rating.average", Sort.DESCENDING);
                    List<RecipeRealmObject> ratingLessThan5List = recipeRealmResult.where().lessThan("rating.raters", 5).findAllSorted("rating.average", Sort.DESCENDING);
                    List<RecipeRealmObject> ratingNullList = recipeRealmResult.where().isNull("rating").findAll();

                    List<RecipeRealmObject> recipeList = new ArrayList<>();
                    recipeList.addAll(ratingMoreThan5List);
                    recipeList.addAll(ratingLessThan5List);
                    recipeList.addAll(ratingNullList);
                    onCompleteListener.onSearchComplete(recipeList);

Upvotes: 0

beeender
beeender

Reputation: 3565

You could define a weight field in your RatingRealmObject, and compute the weight whenever you set average or raters. Then the solution will be just as simple as sorting by the weight.

Some example:

public class RatingRealmObject extends RealmObject {
    private float average;
    private int raters;
    @Index
    private float weight;

    private void computeWeight() {
        // This is for showing the idea, you need to change how to calculate the weight depends on your requirement.
        weight = raters / 5 + average;
    }

    public float getAverage() {
        return average;
    }

    public void setAverage(float average) {
        this.average = average;
        computeWeight();
    }

    public int getRaters() {
        return raters;
    }

    public void setRaters(int raters) {
        this.raters = raters;
        computeWeight();
    }
}

Then you could just have realm.where(RatingRealmObject.class).findAllSortedAsync("weight");

Upvotes: 1

EpicPandaForce
EpicPandaForce

Reputation: 81549

One possible solution would be to concatenate two RealmResults together to get the sort you want, see here.


Another solution would be that if you extend your schema with an additional field

public class RatingRealmObject extends RealmObject {
    @Inedx
    private float average;

    private int raters;

    @Index
    private int hasOverFiveRaters; // 0 or 1 like a TINYINT in SQLite

    public float getAverage() {
        return average;
    }

    public void setAverage(float average) {
        this.average = average;
    }

    public int getRaters() {
        return raters;
    }

    public void setRaters(int raters) {
        this.raters = raters;
        this.hasOverFiveRaters = raters >= 5 ? 1 : 0;
    }
}

Because now you can do

realm.where(RatingRealmObject.class)
     .findAllSortedAsync(
         new String[] {"hasOverFiveRaters", "average"}, 
         new Sort[] { Sort.DESCENDING, Sort.DESCENDING });

P.S.: realm.copyFromRealm() copies every single element from the Realm (in your case, on the UI thread) and therefore removes lazy evaluation, it is generally not needed nor recommended unless you are trying to create a detached modifiable object or want to send the RealmObject through GSON.

Upvotes: 3

Related Questions