Search code examples
androidkotlinrealm

Multiple posible conditions realm query


Say I have 3 choices of sorting.

Dog class have 3 kinds

  • Bulldog
  • Poodle
  • Pug

Base on 3 selection, I guess I have 9 combination?(not sure). Do you have to use a lot of if statements to sort and query the result? Say on the example I want to display Bulldog and poodle? or If anything is not selected I will have to show all of the list of dog registered. What if I have 10 selections each with different combination selection. Do you use if statements for all of those? That is like 100 possible combination. Is there any other way to query realm with other possible combinations? With so many kinds of dogs out there. It would be hard to do if statements and try to hard code all possible combinations

Scenario:

I have a dog class

class Dog : RealmObject(){
 var name:String
 var kind:String

}

now on my recycler view I have a list of dogs inputted by people. In that people can browse the dog. They can query what type. Say they want the recycler view to show a poodle and a pug or a husky and a poodle. They would just check the menu item checkbox and then voila the result of husky and poodle is shown

Edit :

I tried using or and and logical operators. But I can't figure out how to do the possible combinations. All I got is doing if statements which would be a lot since I have 5 selection choices and 25 possible combination

Edit : This is the exact problem on my part. The multiple possible combination query on realm.

My goal is to minimise the coding of if statements by relying on realm query if it's possible

If I hard coded the combination it would look something like this

if(pug && husky){
 val result = realm.where(Dog::class.java).equalto("kind","husky").equalto("kind",pug").findall()
}
.
.
.
.
. a lot of ifs

Solution

  • 1.) if you want to support multiple field query where query options are built from data in database, you can use distinct() and create a Set<String>.

    val kinds = realm.where<Dog>().distinct("kind").findAll().map { it.kind }.toSet()
    

    2.) Once you have sets, you can build the queries based on what's in the set.

    fun filter(realm: Realm, kinds: Set<String>): RealmResults<Dog> { // might wanna introduce a class for the sets
        val query = realm.where<Dog>()
        if(kinds.isNotEmpty()) {
            query.beginGroup()
            kinds.forEachIndexed { index, kind -> 
                if(index != 0) {
                    query.or()
                }
                query.equalTo("kind", kind)
            }
            query.endGroup()
        }
        return query.findAll()
    }
    

    If you want to send in a sort key, that'd just be a .sort(sortKey).

    Then as you can see, you can generalize this function:

    fun filter(realm: Realm, vararg filters: Pair<String, Set<*>>): RealmResults<Dog> { 
        val query = realm.where<Dog>()
    
        fun applyFilters(query: RealmQuery<Dog>, filterParams: Pair<String, Set<*>>) {
            val (fieldName, filter) = filterParams
            if(filter.isNotEmpty()) {
                query.beginGroup()
                filter.forEachIndexed { index, value -> 
                    if(index != 0) {
                        query.or()
                    }
                    query.equalTo(fieldName, value)
                }
                query.endGroup()
            }
        }
    
        filters.forEachIndexed { index, filter ->
            if(index != 0) {
                query.or()
            }
            applyFilters(query, filter)
        }
    
        return query.findAll()
    }
    

    Where it could be called as

    val results = filter(realm, 
                    "kind" to setOf("Bulldog", "Poodle"), 
                    "name" to setOf("George"))
    

    But please note that I wrote this just now on a whim so I might have messed up somewhere. I'm not sure if you can use Set<*> like that, but Set<String> should work just fine in your use-case anyways.