Cerca e filtra Android Room utilizzando le relazioni, interroga più tabelle
Repo è sotto se si desidera emettere una richiesta pull, si prega di includere commenti.
Ho creato un database Room per la mia PokemonApp, voglio essere in grado di filtrare e cercare nel database in base al nome e al tipo di Pokemon.
Ho una tabella per la mia entità Pokemon, una tabella per l'entità PokemonType e una tabella di giunzione per l'entità PokemonTypeJoin, ho anche una classe di dati PokemonWithTypes che incorpora un'entità Pokemon e definisce una relazione tra questa e un elenco di entità 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>
)
data questa struttura posso ottenere e cercare per pokemon_name tutti i PokemonWithTypes usando la seguente query:
@Transaction
@Query("SELECT * FROM pokemon WHERE pokemon_name LIKE :search ORDER BY pokemon_id ASC")
fun getPokemonWithTypes(search: String?): LiveData<List<PokemonWithTypes>>
ma come posso ora aggiungere un filtro (elenco di stringhe) che restituisce solo PokemonWithTypes dove uno qualsiasi dei PokemonWithTypes.types corrisponde a un determinato tipo nell'elenco dei filtri?
Quindi dato 3 Pokemon (alcuni dati rimossi per brevità)
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)])
Al momento ottengo tutti i Pokemon e posso cercare per pokemon_name, ma mi piacerebbe essere in grado di mostrare solo i tipi di acqua o solo i tipi di erba, qualsiasi idea è benvenuta,
Ho provato a filtrare solo su una stringa anziché su un elenco di stringhe con una query come questa
@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>>
ma non ha funzionato
puoi controllare l'intera cosa quihttps://github.com/martipello/PokeApp/tree/add_filters
Risposte
Penso che l'annotazione @Relation non sia progettata per quel caso d'uso. È progettato solo per restituire TUTTI i tipi correlati, non un sottoinsieme filtrato. Penso che tu abbia 3 opzioni:
- Basta filtrarlo con Kotlin:
pokemonWithTypes.filter { it.types.contains("GRASS") }
. Presumo che tu non abbia più di 10000 record di pokemon, quindi le prestazioni non sono un problema. - Scrivi una query di unione. Penso che sia uno sforzo maggiore per un guadagno di prestazioni trascurabile.
- Usa le visualizzazioni del database come da:https://issuetracker.google.com/issues/65509934. Questo è più statico e dovrai scrivere una vista per ogni tipo.