ค้นหาห้อง Android และกรองโดยใช้ความสัมพันธ์ค้นหาหลายตาราง

Aug 17 2020

Repo อยู่ด้านล่างหากคุณต้องการส่งคำขอดึงโปรดใส่ความคิดเห็น

ฉันได้สร้างฐานข้อมูลห้องสำหรับ PokemonApp ของฉันฉันต้องการให้สามารถกรองและค้นหาฐานข้อมูลตามชื่อโปเกมอนและประเภทโปเกมอน

ฉันมีตารางสำหรับเอนทิตีโปเกมอนของฉันตารางสำหรับเอนทิตี PokemonType และตารางทางแยกสำหรับเอนทิตี PokemonTypeJoin ฉันยังมีชั้นข้อมูล PokemonWithTypes ซึ่งฝังเอนทิตีโปเกมอนและกำหนดความสัมพันธ์ระหว่างสิ่งนี้กับรายการของเอนทิตี PokemonType

เอนทิตีโปเกมอน:

    @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 ได้อย่างไรโดยที่ PokemonWithTypes.types ใด ๆ ตรงกับประเภทที่กำหนดในรายการตัวกรอง

ให้โปเกมอน 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 ไม่ได้ออกแบบมาสำหรับกรณีการใช้งานนั้น ได้รับการออกแบบมาเพื่อส่งคืนทุกประเภทที่เกี่ยวข้องเท่านั้นไม่ใช่ชุดย่อยที่กรอง ฉันคิดว่าคุณมี 3 ตัวเลือก:

  1. เพียงแค่กรองด้วย pokemonWithTypes.filter { it.types.contains("GRASS") }Kotlin: ฉันถือว่าคุณไม่มีบันทึกโปเกมอนมากกว่า 10,000 รายการดังนั้นประสิทธิภาพจึงไม่ใช่ปัญหา
  2. เขียนแบบสอบถามเข้าร่วม ฉันคิดว่านั่นเป็นความพยายามมากขึ้นสำหรับการเพิ่มประสิทธิภาพเล็กน้อย
  3. ใช้มุมมองฐานข้อมูลตาม: https://issuetracker.google.com/issues/65509934. สิ่งนี้คงที่มากขึ้นและคุณจะต้องเขียนมุมมองสำหรับทุกประเภท