본문 바로가기

Android/Function

[안드로이드] Room 데이터베이스의 검색기능 구현하기 ― Room Fts4

728x90
반응형

 

이 글은 Room Database기능을 사용하고 있다고 가정하고,

Fts4를 사용하여 DB의 검색기능을 사용하는 예제입니다.

Room과 LiveData를 MVVM패턴으로 구현한 예제는 다음글을 참고하세요

[Android/이론] - MVVM 시작하기(1) ― LiveData, Room을 MVVM패턴으로 사용해보자

[Android/이론] - MVVM 시작하기(2) ― Room Entity, Dao, Database 만들기

[Android/이론] - MVVM 시작하기(3) ― ViewModel, 데이터 바인딩(Data Binding)

[Android/이론] - MVVM 시작하기(4) ― Repository, Model-View 연동

 

코드 미리보기

먼저, 검색을 대상이 될 기존의 Entity는 다음과 같습니다.

@Entity(tableName = "tool_table")
data class ToolEntity(
    @PrimaryKey
    @ColumnInfo(name="name")
    val name: String
)

 

String 변수 name을 가지고 있고, 이 name을 검색할 것입니다.

그리고 FtsEntity를 만들어 줍니다.

@Entity(tableName = "toolsFts")
@Fts4(contentEntity = ToolEntity::class)
class ToolFtsEntity(
    val name: String
)

 

검색 대상이 될 Entity를 contentEntity로 하고, 검색하고 싶은 변수를 전부 추가해 주면 됩니다.

그리고 이 Entity를 데이터베이스에 추가해주어야 합니다.

@Database(entities = [ToolEntity::class, ToolFtsEntity::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {

    abstract fun toolDao(): ToolDao

    companion object {
        @Volatile
        private var INSTANCE: AppDatabase? = null
        fun getDatabase(
        ...

 

entities = [ToolEntity::class, ToolFtsEntity::class]

FtsEntity를 데이터베이스에 추가해주세요.

 

이제 Dao에 검색 메서드를 만들어줍니다.

@Query("SELECT tool_table.* FROM tool_table JOIN toolsFts ON (tool_table.name = toolsFts.name) WHERE toolsFts MATCH :query")
fun searchAllTools(query: String?): LiveData<List<ToolEntity>>

 

  • " SELECT tool_table.* FROM tool_table JOIN toolsFts ON (tool_table.name = toolsFts.name) WHERE toolsFts MATCH :query "

JOIN은 두개이상의 테이블을 가로방향으로 붙인다는 뜻입니다. ON (tool_table.name = toolsFts.name) 을 통해 tool_table의 name과 Fts table의 name을 맞춰 붙인다고 할 수 있습니다.

rowid를 사용하여 ON (tool_table.rowid = toolFts.rowid) 로도 사용 가능합니다.

또다른 자세한 사항은 SQLite의 문법을 알아야 합니다..

 

Dao에 위와같이 추가하고, Repository에 메서드를 추가하였습니다.

private val toolDao = mDatabase.toolDao()
...
fun searchTools(query: String?): LiveData<List<ToolEntity>> {
     return toolDao.searchAllTools(query)
 }

 

그다음 ViewModel에서 LiveData ToolEntity 변수 allTools를  Transformation을 이용하여 다시 정의하였습니다.

/** Use the savedStateHandle.getLiveData() as the input to switchMap,
 * allowing us to recalculate what LiveData to get from the DataRepository
 * based on what query the user has entered
 */
var allTools: LiveData<List<ToolEntity>> = Transformations.switchMap<CharSequence?, List<ToolEntity>>(
    savedStateHandle.getLiveData("QUERY", null),
    Function<CharSequence?, LiveData<List<ToolEntity>>> { query: CharSequence? ->
         if (TextUtils.isEmpty(query)) {
                    return@Function repository.allTools
          }
          else return@Function repository.searchTools("*$query*")
       }
)

 

savedStateHandle을 이용하여 검색 query를 저장시키고, 만약 저장된 query가 있다면 search를 결과를 반환하게 됩니다.

버튼을 누르면 EditText의 text를 가져와 저장시켰습니다.

fun onClickSearch(searchTxt: String){
      setQuery(searchTxt)
      Log.d("DiscoverViewModel", "Search $searchTxt")
}
fun setQuery(query: CharSequence?) {
      /** Save the user's query into the SavedStateHandle.
       * This ensures that we retain the value across process death
       * and is used as the input into the Transformations.switchMap above
       */
      savedStateHandle.set("QUERY",query)
}

 

ViewModel의 전체 소스와, 데이터 바인딩을 이용한 layout은 다음과 같습니다.

class MyViewModel(application: Application, val savedStateHandle: SavedStateHandle) 
: BaseViewModel(application) {

    private val repository: Repository =  Repository(AppDatabase.getDatabase(application,viewModelScope))
    var allTools: LiveData<List<ToolEntity>> = Transformations.switchMap<CharSequence?, List<ToolEntity>>(
            savedStateHandle.getLiveData("QUERY", null),
            Function<CharSequence?, LiveData<List<ToolEntity>>> { query: CharSequence? ->
                if (TextUtils.isEmpty(query)) {
                    return@Function repository.allTools
                }
                else return@Function repository.searchTools("*$query*")
            }
        )

    fun onClickSearch(searchTxt: String){
        setQuery(searchTxt)
        Log.d("DiscoverViewModel", "Search $searchTxt")
    }
    fun insert(toolEntity: ToolEntity) = viewModelScope.launch(Dispatchers.IO) {
        repository.insert(toolEntity)
    }
    fun setQuery(query: CharSequence?) {
        // Save the user's query into the SavedStateHandle.
        // This ensures that we retain the value across process death
        // and is used as the input into the Transformations.switchMap above
        savedStateHandle.set("QUERY",query)
    }
}

 

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <data>
        <variable
            name="viewModel"
            type="com.example.Myapp.viewmodel.MyViewModel" />
    </data>

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <EditText
            android:id="@+id/search_text"
            android:layout_marginLeft="5dp"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="10"
            android:hint="검색어를 입력 후 검색 버튼을 눌러주세요." />
        <ImageButton
            android:layout_marginRight="5dp"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:src="@drawable/ic_search"
            android:onClick="@{()->viewModel.onClickSearch(searchText.getText().toString())}"/>
    </LinearLayout>
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="10dp"
        tools:listitem="@layout/measurement_item" />


</LinearLayout>
</layout>

 

이는 앞서 링크했던 MVVM시작하기의 소스의 연장선입니다.

기본 기능 구현은 앞의 링크를 참조해주세요.

[Android/이론] - MVVM 시작하기(1) ― LiveData, Room을 MVVM패턴으로 사용해보자

 

검색 기능 구현 소스 결과는 다음과 같습니다.

728x90
반응형