带有多个非强制性过滤器的 Room DAO

bal*_*ekg 7 android kotlin android-sqlite android-room

在我的应用程序中,用户可以在每个组合中使用多个过滤器来过滤他们的数据(仅应用一个、多个或不应用)。

在此之前,我只有一个过滤器,所以每次应用它时,我都在切换 DAO 方法。现在我有 6 个过滤器,所以有几十种组合,因此为每个组合创建一个方法是不可能的。我也不能大量修改我的数据库,因为它已经可供用户使用。

我当前的代码如下所示:

@Query("SELECT id, name, date FROM UserData")
fun getAll(): DataSource.Factory<Int, UserItem> //no filters

@Query("SELECT id, name, date FROM UserData WHERE name LIKE '%' || :search  || '%'")
fun getAllFiltered(query: String): DataSource.Factory<Int, UserItem> //one filter applied
Run Code Online (Sandbox Code Playgroud)

有没有办法修改查询,以便所有过滤器组合都有一种方法?

更新:

这是我的数据类,我想过滤哪些实例:

@Entity(tableName = "UserItem")
data class UserItem(

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    val id: Long? = null,

    @ColumnInfo(name = "created_at")
    val createdAt: Date,

    @ColumnInfo(name = "is_uploaded")
    val isUploaded: Boolean,

    @ColumnInfo(name = "name")
    val name: String,

    @ColumnInfo(name = "item_sum")
    val sum: Int = 0,

    @ColumnInfo(name = "tags")
    val tags: List<String> = listOf(),
)
Run Code Online (Sandbox Code Playgroud)

我想过滤/检查数字和布尔属性的相等性,检查列表属性是否包含指定的字符串。基本上,我希望能够过滤所有我能过滤的东西。如果不可能,我至少会对一些过滤器感到满意。

bal*_*ekg 2

根据@CommonsWare 的评论,我尝试使用RawQuery来实现我想要的。

首先要做的是创建 Filters 数据类,该数据类将来将保存所有过滤器。删除一个或添加多个确实很容易。

data class Filters(
    val query: String? = null,
    val isUploaded: Boolean? = null,
    // all the other filters
)
Run Code Online (Sandbox Code Playgroud)

将构建查询并从数据库返回结果的函数:

fun getAllFiltered(filters: Filters): DataSource.Factory<Int, UserItem> {
    val conditions = mutableListOf<Pair<String, Any?>>()
    with(filters) {
        query?.let { conditions.add("name LIKE '%' || ? || '%'" to it) }
        isUploaded?.let { conditions.add("is_uploaded = ${it.toInt()}" to null) }
        // "subqueries" to filter  specific field
    }

    if (conditions.isEmpty())
        return getAll()

    val conditionsMerged = conditions.joinToString(separator = " AND ") { it.first }
    val bindArgs = conditions.mapNotNull { it.second }

    val query = SimpleSQLiteQuery(
        "SELECT id, name, date FROM UserData WHERE $conditionsMerged",
        bindArgs.toTypedArray()
    )
    return getAllFiltered(query)
}

@RawQuery(observedEntities = [UserItem::class])
fun getAllFiltered(query: SupportSQLiteQuery): DataSource.Factory<Int, UserItem>

private fun Boolean.toInt() = if (this) 1 else 0
Run Code Online (Sandbox Code Playgroud)

我不知道它将如何执行,因为查询必须在运行时构建(但从我所做的一些测试来看,我没有注意到太多的性能损失),但优点是很容易添加其他过滤器或删除现有过滤器,如果仅应用多个过滤器中的一个,则创建的查询非常简单。