Pencarian dan filter Ruang Android menggunakan relasi, membuat kueri beberapa tabel

Aug 17 2020

Repo di bawah jika Anda ingin mengeluarkan permintaan tarik, harap sertakan komentar.

Saya telah membuat database Room untuk PokemonApp saya. Saya ingin dapat memfilter dan mencari database berdasarkan nama Pokemon dan jenis Pokemon.

Saya memiliki tabel untuk entitas Pokemon saya, tabel untuk entitas PokemonType dan tabel persimpangan untuk entitas PokemonTypeJoin, saya juga memiliki kelas data PokemonWithTypes yang menyematkan entitas Pokemon dan mendefinisikan hubungan antara ini dan daftar entitas PokemonType.

Entitas Pokemon:

    @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"

Entitas 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 entitas:

    @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>
    )

mengingat struktur ini saya bisa mendapatkan dan mencari dengan pokemon_name semua PokemonWithTypes menggunakan query berikut:

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

tetapi bagaimana sekarang saya dapat menambahkan filter (daftar string) yang hanya mengembalikan PokemonWithTypes di mana salah satu PokemonWithTypes.types cocok dengan jenis yang diberikan dalam daftar filter?

Jadi diberikan 3 Pokemon (beberapa data dihapus agar singkat)

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)])

Saat ini saya mendapatkan semua Pokemon dan dapat mencari berdasarkan pokemon_name tetapi saya ingin dapat menunjukkan hanya jenis air atau jenis rumput saja, semua ide selamat datang,

Saya mencoba hanya memfilter pada string alih-alih daftar string dengan kueri seperti ini

@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>>

tapi tidak berhasil

Anda dapat melihat semuanya di sini https://github.com/martipello/PokeApp/tree/add_filters

Jawaban

1 CarsonHolzheimer Aug 17 2020 at 08:05

Menurut saya, anotasi @Relation tidak dirancang untuk kasus penggunaan tersebut. Ini hanya dirancang untuk mengembalikan SEMUA tipe terkait, bukan subset yang difilter. Saya pikir Anda memiliki 3 opsi:

  1. Hanya menyaring dengan Kotlin: pokemonWithTypes.filter { it.types.contains("GRASS") }. Saya berasumsi Anda tidak memiliki lebih dari 10.000 catatan pokemon sehingga kinerja tidak menjadi masalah.
  2. Tulis kueri gabungan. Saya pikir itu lebih banyak upaya untuk mendapatkan kinerja yang dapat diabaikan.
  3. Gunakan Tampilan database sesuai: https://issuetracker.google.com/issues/65509934. Ini lebih statis dan Anda harus menulis tampilan untuk setiap jenis.