관계를 사용하여 Android Room 검색 및 필터링, 여러 테이블 쿼리

Aug 17 2020

리포는 풀 리퀘스트를 발행하고 싶다면 코멘트를 포함 해주세요.

PokemonApp에 대한 Room 데이터베이스를 만들었습니다. Pokemon 이름과 Pokemon 유형에 따라 데이터베이스를 필터링하고 검색 할 수 있기를 원합니다.

내 Pokemon 엔터티에 대한 테이블, PokemonType 엔터티에 대한 테이블 및 PokemonTypeJoin 엔터티에 대한 접합 테이블이 있습니다. 또한 Pokemon 엔터티를 포함하고 이것과 PokemonType 엔터티 목록 간의 관계를 정의하는 데이터 클래스 PokemonWithTypes도 있습니다.

포켓몬 엔티티 :

    @TypeConverters(RoomStringListConverter::class)
    @Entity
    data class Pokemon(
        @NotNull
        @PrimaryKey
        @ColumnInfo(name = POKEMON_ID)
        var id: Int,
    
        @ColumnInfo(name = POKEMON_NAME)
        var name: String,
    
        @ColumnInfo(name = POKEMON_URL)
        var url: String,
    
        @ColumnInfo(name = POKEMON_WEIGHT)
        val weight: Int,
    
        @ColumnInfo(name = POKEMON_HEIGHT)
        val height: Int,
    
        @ColumnInfo(name = POKEMON_SPECIES)
        var species: String,
    
        @ColumnInfo(name = POKEMON_MOVES)
        val moves: List<String>
    
    ) 

    const val POKEMON_ID: String = "pokemon_id"
    const val POKEMON_NAME: String = "pokemon_name"
    const val POKEMON_URL: String = "pokemon_url"
    const val POKEMON_HEIGHT: String = "pokemon_height"
    const val POKEMON_WEIGHT: String = "pokemon_weight"
    const val POKEMON_MOVES: String = "pokemon_moves"
    const val POKEMON_SPECIES: String = "pokemon_species"

PokemonType 엔티티 :

    @Entity
    data class PokemonType (
    
        @NotNull
        @PrimaryKey
        @ColumnInfo(name = POKEMON_TYPE_ID)
        var id: Int,
    
        @ColumnInfo(name = POKEMON_TYPE_NAME)
        var name: String,
    
        @ColumnInfo(name = POKEMON_TYPE_SLOT)
        var slot: Int
    
    )

    const val POKEMON_TYPE_ID: String = "type_id"
    const val POKEMON_TYPE_NAME: String = "type_name"
    const val POKEMON_TYPE_SLOT: String = "type_slot"

PokemonTypesJoin 엔티티 :

    @Entity(primaryKeys = [POKEMON_ID, POKEMON_TYPE_ID])
    class PokemonTypesJoin(
        @NotNull
        @ColumnInfo(name = POKEMON_ID, index = true)
        val pokemon_id: Int,
    
        @NotNull
        @ColumnInfo(name = POKEMON_TYPE_ID, index = true)
        val pokemon_type_id: Int
    
    )
    
    const val POKEMON_ID: String = "id"
    const val POKEMON_TYPE_ID: String = "type_id"

PokemonWithTypes 클래스

    data class PokemonWithTypes(
        @Embedded
        val pokemon: Pokemon,
        @Relation(
            parentColumn = Pokemon.POKEMON_ID,
            entity = PokemonType::class,
            entityColumn = PokemonType.POKEMON_TYPE_ID,
            associateBy = Junction(
                value = PokemonTypesJoin::class,
                parentColumn = PokemonTypesJoin.POKEMON_ID,
                entityColumn = PokemonTypesJoin.POKEMON_TYPE_ID
            )
        )
        val types: List<PokemonType>
    )

이 구조가 주어지면 다음 쿼리를 사용하여 pokemon_name 모든 PokemonWithTypes를 가져오고 검색 할 수 있습니다.

@Transaction
@Query("SELECT * FROM pokemon WHERE pokemon_name LIKE :search ORDER BY pokemon_id ASC")
fun getPokemonWithTypes(search: String?): LiveData<List<PokemonWithTypes>>

하지만 이제 PokemonWithTypes.types가 필터 목록의 지정된 유형과 일치하는 PokemonWithTypes 만 반환하는 필터 (문자열 목록)를 어떻게 추가 할 수 있습니까?

그래서 주어진 3 개의 포켓몬 (간결성을 위해 일부 데이터가 제거됨)

PokemonWithTypes(pokemon=Pokemon(id=1, name=bulbasaur, types=[PokemonType(id=4, name=poison, slot=2), PokemonType(id=12, name=grass, slot=1)])
PokemonWithTypes(pokemon=Pokemon(id=4, name=charmander, types=[PokemonType(id=10, name=fire, slot=2), PokemonType(id=12, name=grass, slot=1)])
PokemonWithTypes(pokemon=Pokemon(id=7, name=squirtle, types=[PokemonType(id=11, name=water, slot=2), PokemonType(id=12, name=grass, slot=1)])

나는 현재 모든 포켓몬을 얻고 pokemon_name으로 검색 할 수 있지만 물 유형 또는 잔디 유형 만 표시 할 수 있기를 바랍니다.

나는 다음과 같은 쿼리로 문자열 목록 대신 문자열에서 필터링을 시도했습니다.

@Transaction
@Query("SELECT * FROM pokemon, pokemonType WHERE type_name LIKE :filter AND pokemon_name LIKE :search ORDER BY pokemon_id ASC")
fun getPokemonWithTypes(search: String?, filter: String): LiveData<List<PokemonWithTypes>>

하지만 작동하지 않았다

여기에서 전체 내용을 확인할 수 있습니다. https://github.com/martipello/PokeApp/tree/add_filters

답변

1 CarsonHolzheimer Aug 17 2020 at 08:05

@Relation 주석은 해당 사용 사례에 맞게 설계되지 않았다고 생각합니다. 필터링 된 하위 집합이 아닌 모든 관련 유형을 반환하도록 설계되었습니다. 세 가지 옵션이 있다고 생각합니다.

  1. Kotlin으로 필터링하면됩니다 pokemonWithTypes.filter { it.types.contains("GRASS") }.. 나는 당신이 포켓몬의 기록이 10000 개를 넘지 않는다고 가정하기 때문에 성능은 문제가되지 않는다.
  2. 조인 쿼리를 작성합니다. 미미한 성능 향상을위한 더 많은 노력이라고 생각합니다.
  3. 다음과 같이 데이터베이스보기를 사용하십시오. https://issuetracker.google.com/issues/65509934. 이것은 더 정적이고 모든 유형에 대한 뷰를 작성해야합니다.