관계를 사용하여 Android Room 검색 및 필터링, 여러 테이블 쿼리
리포는 풀 리퀘스트를 발행하고 싶다면 코멘트를 포함 해주세요.
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
답변
@Relation 주석은 해당 사용 사례에 맞게 설계되지 않았다고 생각합니다. 필터링 된 하위 집합이 아닌 모든 관련 유형을 반환하도록 설계되었습니다. 세 가지 옵션이 있다고 생각합니다.
- Kotlin으로 필터링하면됩니다
pokemonWithTypes.filter { it.types.contains("GRASS") }
.. 나는 당신이 포켓몬의 기록이 10000 개를 넘지 않는다고 가정하기 때문에 성능은 문제가되지 않는다. - 조인 쿼리를 작성합니다. 미미한 성능 향상을위한 더 많은 노력이라고 생각합니다.
- 다음과 같이 데이터베이스보기를 사용하십시오. https://issuetracker.google.com/issues/65509934. 이것은 더 정적이고 모든 유형에 대한 뷰를 작성해야합니다.