Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Realm querying with two conditions at the same time

Tags:

android

realm

How would I query a structure like this:

public class Animal extends RealmObject { 
    RealmList<Bird> birds;
}


public class Bird extends RealmObject { 
    int type;
    RealmList<RealmInteger> species;
}

RealmInteger is an object with an int value

I want to find all Animal objects that have Bird which has a species of value 3 AND that Bird is of type 2

I tried this but it keeps ignoring the type:

realm.where(Animal.class)
    .equalTo("birds.type", 2)
    .equalTo("birds.species.value", 3)
    .findAll();

My guess is it finds a match with value but doesn't check the type field at the same time. I need a way of doing .equalTo("birds.species.value", 3) to check only Birds of type 2?

Update: Tried the @EpicPandaForce answer below, it is also returning this Animal with the data:

"birds": [
     {
        "species": [3, 15, 26],
        "type": 1
     },
     {
        "species": [],
        "type": 2,
     }
]

Because this Animal does not have a species value of 3 (it's empty) of type 2, it should NOT return it. Yet it does.

like image 246
ono Avatar asked Aug 06 '17 07:08

ono


2 Answers

realm.where(Animal.class)
  .equalTo("birds.type", 2)
  .findAll()
  .where()
  .equalTo("birds.species.value", 3)
  .findAll();

This trick is only needed for multiple link queries.

like image 29
EpicPandaForce Avatar answered Oct 28 '22 10:10

EpicPandaForce


Unfortunately, you are running into a peculiarity of how link queries work, and currently, there is no easy way to do what you want.

The underlying reason is that you are querying from the point of view of Animal and that you have two levels of RealmList. What you are after is a sort of sub-query that isn't yet supported by Realm. The details of how link queries work are described here: https://realm.io/docs/java/latest/#link-queries. I would highly recommend working through the example in those docs.

That said, it is still possible to achieve what you want, but you need a combination of our newly added @LinkingObjects annotation + some manually work to do it. Here is how:

// Animal class must have a stable hashcode. I did it by adding a primary key
// here, but it can be done in multiple ways.
public class Animal extends RealmObject {
    @PrimaryKey
    public String id = UUID.randomUUID().toString();
    public RealmList<Bird> birds;

    @Override
    public boolean equals(Object o) {
        // Make sure you have a stable equals/hashcode
        // See https://realm.io/docs/java/latest/#realmobjects-hashcode
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Animal animal = (Animal) o;
        return id.equals(animal.id);
    }

    @Override
    public int hashCode() {
        return id.hashCode();
    }
}

// Add a @LinkingObjects field to Bird
// See https://realm.io/docs/java/latest/#inverse-relationships
public class Bird extends RealmObject {
    public int type;
    public RealmList<RealmInteger> species;
    @LinkingObjects("birds")
    public final RealmResults<Animal> animalGroup = null;

    @Override
    public String toString() {
        return "Bird{" +
                "type=" + type +
                '}';
    }
}

// Query the birds instead of Animal
RealmResults<Bird> birds = realm.where(Bird.class)
        .equalTo("type", 2)
        .equalTo("species.value", 3)
        .findAll();

// You must collect all Animals manually
// See https://github.com/realm/realm-java/issues/2232
Set<Animal> animals = new HashSet<>();
for (Bird bird : birds) {
    animals.addAll(bird.animalGroup);
}
like image 97
Christian Melchior Avatar answered Oct 28 '22 10:10

Christian Melchior