Recherche et filtrage de salle Android à l'aide de relations, interrogation de plusieurs tables

Aug 17 2020

Le dépôt est ci-dessous si vous souhaitez émettre une demande d'extraction, veuillez inclure des commentaires.

J'ai créé une base de données de pièces pour mon PokemonApp. Je souhaite pouvoir filtrer et rechercher dans la base de données en fonction du nom et des types de Pokémon.

J'ai une table pour mon entité Pokemon, une table pour l'entité PokemonType et une table de jonction pour l'entité PokemonTypeJoin, j'ai aussi une classe de données PokemonWithTypes qui embarque une entité Pokemon et définit une relation entre celle-ci et une liste d'entités PokemonType.

Entité Pokémon :

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

Entité 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"

Entité 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"

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

étant donné cette structure, je peux obtenir et rechercher par pokemon_name tous les PokemonWithTypes en utilisant la requête suivante :

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

mais comment puis-je maintenant ajouter un filtre (liste de chaînes) qui ne renvoie que PokemonWithTypes où l'un des PokemonWithTypes.types correspond à un type donné dans la liste des filtres ?

Donc, étant donné 3 Pokémon (certaines données ont été supprimées par souci de brièveté)

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

Je reçois actuellement tous les Pokémon et je peux rechercher par pokemon_name mais j'aimerais pouvoir montrer uniquement les types d'eau ou uniquement les types d'herbe, toutes les idées sont les bienvenues,

J'ai essayé de filtrer simplement sur une chaîne au lieu d'une liste de chaînes avec une requête comme celle-ci

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

mais ça n'a pas marché

vous pouvez consulter l'intégralité icihttps://github.com/martipello/PokeApp/tree/add_filters

Réponses

1 CarsonHolzheimer Aug 17 2020 at 08:05

Je pense que l'annotation @Relation n'est pas conçue pour ce cas d'utilisation. Il est uniquement conçu pour renvoyer TOUS les types associés, pas un sous-ensemble filtré. Je pense que tu as 3 possibilités :

  1. Il suffit de le filtrer avec Kotlin : pokemonWithTypes.filter { it.types.contains("GRASS") }. Je suppose que vous n'avez pas plus de 10 000 enregistrements de pokémons, donc les performances ne sont pas un problème.
  2. Rédigez une requête de jointure. Je pense que c'est plus d'effort pour un gain de performance négligeable.
  3. Utilisez les vues de base de données selon :https://issuetracker.google.com/issues/65509934. Ceci est plus statique et vous devrez écrire une vue pour chaque type.