Filtering query with BEGINSWITH of Realm database in Kotlin

36 Views Asked by At

I am having issues with a query which includes BEGINSWITH (or CONTAINS for that matter). I am trying to follow the example as found in the Mongo Db documentation:

This query to find instances of the <Pantry> model is not filtering the way it should.

(all of this is in the viewModel)

This is the query:

val searchResults = realm
        .query<Pantry>("itemName BEGINSWITH $0", searchText.value)
        .find()
        .asFlow()
        .map { results ->
            results.list.toList()
        }
        .stateIn(
            viewModelScope,
            SharingStarted.WhileSubscribed(),
            emptyList()
        )

searchText is a String with a search pattern input by the user to find instances of the Pantry model. This done by use of a TextField.

'realm' is defined like this: private val realm = MyApp.realm.

MyApp is :

class MyApp: Application() {

    companion object {
        lateinit var realm: Realm
    }

    override fun onCreate() {
        super.onCreate()
        realm = Realm.open(
            configuration = RealmConfiguration.create(
                schema = setOf(
                    Pantry::class,
                    Department::class,
                    Market::class,
                )
            )
        )
    }
}

I implemented this function to see what was happening; if the query was doing anything or not:

suspend fun checkSearchResults(){
        searchResults.collect { results ->
            if (results.isEmpty()) {
                Log.d("Search", "No search results found")
            } else {
                // Loop through or access items in results
                for (item in results) {
                    Log.d("Search", "Found item: ${item.itemName}")
                }
            }
        }

    }

This is the log as the user inputs characters:

The value of searchText: h
D  Found item: bananas
D  Found item: clorox
D  Found item: garbanzos
D  Found item: ham
D  Found item: laundry detergent, pods
D  Found item: milk
D  Found item: olives
D  Found item: pastries
D  Found item: salmon
D  Found item: water, sparkling
D  The value of searchText: ha
D  Found item: bananas
D  Found item: clorox
D  Found item: garbanzos
D  Found item: ham
D  Found item: laundry detergent, pods
D  Found item: milk
D  Found item: olives
D  Found item: pastries
D  Found item: salmon
D  Found item: water, sparkling
D  The value of searchText: ham
D  Found item: bananas
D  Found item: clorox
D  Found item: garbanzos
D  Found item: ham
D  Found item: laundry detergent, pods
D  Found item: milk
D  Found item: olives
D  Found item: pastries
D  Found item: salmon
D  Found item: water, sparkling

which is the entire database.

So, from the log its evident that the query is ignoring the filter and all instances are included.

Can anyone point out where I am failing? I think I'm following the documentation pretty closely.

UPDATE: if I use a query like this: .query<Pantry>("itemName = $0", searchText.value) and using a known itemName, the result is an empty list.

UPDATE 2: searchResults does not update. My understanding was that because it's a StateFlow it would update each time the conditions change (searchtext.value), but its not happening.

1

There are 1 best solutions below

0
Ray On

This was the issue: the value of searchResults was not updating, therefore even though the textField was issuing characters to the searchText variable, nothing was happening.

The solution: force an update on the variable searchResults. This was accomplished by enclosing the code for searchResults in a function and calling the function from the onValueChange in the textField.

Complications: My understanding is that just because a function is called, doesn't necessarily generate an update on a StateFlow entity, so by enclosing it in a .collect can make that happen. Complications between viewModel and the Composable and the use of var and val in the declaration of variables.

So to solve for said complications, I declared the following:

private val _searchResults = MutableStateFlow<List<Pantry>>(emptyList())
var searchResults: StateFlow<List<Pantry>> = _searchResults.asStateFlow()

1 for use in the viewModel and the other for the Composable.

and this is the function:

suspend fun fetchSearchResults(searchTerm: String) {
    realm.query<Pantry>("itemName CONTAINS $0", searchTerm)
        .find()
        .asFlow()
        .map { results -> results.list.toList() }
        .collect { filteredList ->
            _searchResults.value = filteredList 
        }
}

it needs to be in a suspend function, due to the .collect.

So the function is called from the textField:

TextField(
        value = searchText,
        onValueChange = { viewModel.changeText(it)
                  scope.launch {
                      viewModel.fetchSearchResults(it)
                  }
                               },

It works pretty well. Tested it CONTAINS and with BEGINSWITH and both work.