Búsqueda y filtro de habitaciones de Android usando relaciones, consulta varias tablas
El repositorio se encuentra a continuación. Si desea emitir una solicitud de extracción, incluya comentarios.
Creé una base de datos de habitaciones para mi PokemonApp. Quiero poder filtrar y buscar en la base de datos según el nombre de Pokémon y los tipos de Pokémon.
Tengo una tabla para mi entidad Pokemon, una tabla para la entidad PokemonType y una tabla de unión para la entidad PokemonTypeJoin, también tengo una clase de datos PokemonWithTypes que incorpora una entidad Pokemon y define una relación entre esta y una lista de entidades PokemonType.
Entidad 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"
Entidad 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"
PokemonTypesÚnete a la entidad:
@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"
Clase 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>
)
dada esta estructura, puedo obtener y buscar por pokemon_name todos los PokemonWithTypes usando la siguiente consulta:
@Transaction
@Query("SELECT * FROM pokemon WHERE pokemon_name LIKE :search ORDER BY pokemon_id ASC")
fun getPokemonWithTypes(search: String?): LiveData<List<PokemonWithTypes>>
pero, ¿cómo puedo ahora agregar un filtro (lista de cadenas) que solo devuelva PokemonWithTypes donde cualquiera de PokemonWithTypes.types coincida con un tipo dado en la lista de filtros?
Entonces, dados 3 Pokémon (algunos datos eliminados por brevedad)
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)])
Actualmente tengo todos los Pokémon y puedo buscar por pokemon_name, pero me gustaría poder mostrar solo tipos de agua o solo tipos de hierba. Cualquier idea es bienvenida.
Intenté simplemente filtrar en una cadena en lugar de una lista de cadenas con una consulta como esta
@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>>
pero no funcionó
puedes ver todo completo aquihttps://github.com/martipello/PokeApp/tree/add_filters
Respuestas
Creo que la anotación @Relation no está diseñada para ese caso de uso. Solo está diseñado para devolver TODOS los tipos relacionados, no un subconjunto filtrado. Creo que tienes 3 opciones:
- Solo fíltralo con Kotlin:
pokemonWithTypes.filter { it.types.contains("GRASS") }
. Supongo que no tienes más de 10000 registros de pokemons, por lo que el rendimiento no es un problema. - Escriba una consulta de combinación. Creo que eso es más esfuerzo para una ganancia de rendimiento insignificante.
- Usar vistas de base de datos según:https://issuetracker.google.com/issues/65509934. Esto es más estático y tendrás que escribir una vista para cada tipo.