Search code examples
androidkotlinandroid-roomandroid-architecture-componentsandroid-livedata

LiveData + ViewModel + Room: Exposing a LiveData returned by query which changes over time (Through a fts search)


I have an FTS query in my DAO which I'd like to use to provide search in my App. The activity passes the query to view model each time the search text is changed.

The problem is that, Room returns a LiveData every single time the query is executed while I'd like to get same LiveData object updated when I run the query.

I was thinking about copying data from the LiveData which room returns into my dataSet (see the code below). Would it be a good approach? (And if yes, how would I actually do that?)

Here's my work so far:

In my Activity:

override fun onCreate(savedInstanceState: Bundle?) {
    //....

    wordViewModel = ViewModelProviders.of(this).get(WordMinimalViewModel::class.java)

    wordViewModel.dataSet.observe(this, Observer {
        it?.let {mRecyclerAdapter.setWords(it)}
    })
}

/* This is called everytime the text in search box is changed */
override fun onQueryTextChange(query: String?): Boolean {

    //Change query on the view model
    wordViewModel.searchWord(query)

    return true
}

ViewModel:


    private val repository :WordRepository =
        WordRepository(WordDatabase.getInstance(application).wordDao())

    //This is observed by MainActivity
    val dataSet :LiveData<List<WordMinimal>> = repository.allWordsMinimal


    //Called when search query is changed in activity
    //This should reflect changes to 'dataSet'
    fun searchWord(query :String?) {
        if (query == null || query.isEmpty()) {

            //Add all known words to dataSet, to make it like it was at the time of initiating this object
            //I'm willing to copy repository.allWordsMinimal into dataSet here

        } else {

            val results = repository.searchWord(query)
            //Copy 'results' into dataSet
        }
    }
}

Repository:


    //Queries all words from database
    val allWordsMinimal: LiveData<List<WordMinimal>> =
        wordDao.getAllWords()

    //Queries for word on Room using Fts
    fun searchWord(query: String) :LiveData<List<WordMinimal>> =
        wordDao.search("*$query*")

    //Returns the model for complete word (including the definition for word)
    fun getCompleteWordById(id: Int): LiveData<Word> =
        wordDao.getWordById(id)
}

DAO:

interface WordDao {

    /* Loads all words from the database */
    @Query("SELECT rowid, word FROM entriesFts")
    fun getAllWords() : LiveData<List<WordMinimal>>

    /* FTS search query */
    @Query("SELECT rowid, word FROM entriesFts WHERE word MATCH :query")
    fun search(query :String) :LiveData<List<WordMinimal>>

    /* For definition lookup */
    @Query("SELECT * FROM entries WHERE id=:id")
    fun getWordById(id :Int) :LiveData<Word>
}

Solution

  • val dataSet :LiveData<List<WordMinimal>>
    
    val searchQuery = MutableLiveData<String>()
    
    init {
        dataSet = Transformations.switchMap(searchQuery) { query ->
            if (query == null || query.length == 0) {
                //return WordRepository.getAllWords()
            } else {
                //return WordRepository.search(query)
            }
        }
    }
    
    
    fun setSearchQuery(searchedText: String) {
        searchQuery.value = searchedText
    }