Android 房间:针对字符串列查询列表项

Android room: query list items against string column

我有一个字符串列表:

val mylist = listOf("cat","flower")

和一个 table,它有一个名为 question 的字符串类型的列 我可以编写查询来查找与列表项之一完全匹配的问题:

@Query("SELECT * FROM objects WHERE question IN (:mylist)")
List<Object> queryObjects(List<String> mylist);

但实际上问题列数据不是单词类型,而是字符串。我需要找到每个列表项都在该字符串中的结果。例如记录:is this a cat

IN 的使用基本上是对 IN 子句左侧的表达式与右侧值列表的 = 测试。那只是考虑完全匹配。

然而,你想要的是多个带有通配符的 LIKE,并且每个 LIKE 之间有一个 OR 例如 question LIKE '%cat%' OR question LIKE '%flower%'CASE WHEN THEN ELSE END 或者递归通用 table 表达式 (CTE)。

前两个(LIKE 或 CASE)可能必须通过 @RawQuery 完成,其中 LIKE/CASE 子句是在 运行 时构建的。

递归 CTE 选项基本上会构建一个单词列表(但如果包含标点符号等除空格以外的任何内容,可能会变得更加复杂。)

另一种选择是考虑全文搜索 (FTS)。不妨参考https://www.raywenderlich.com/14292824-full-text-search-in-room-tutorial-getting-started

工作示例

下面是一个最简单的例子,多个 LIKE 子句用 OR 分隔:-

对象(实体):-

@Entity
data class Objects(
    @PrimaryKey
    val id: Long? = null,
    val question: String
) 

AllDAO(道):-

@Dao
interface AllDAO {

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(objects: Objects)

    @RawQuery
    fun getObjectsRawQuery(query: SupportSQLiteQuery): List<Objects>

    fun getObjects(values: List<String>): List<Objects> {
        var i = 0
        val sb = StringBuilder().append("SELECT * FROM objects WHERE ")
        for(v in values) {
            if (i++ > 0) {
                sb.append(" OR ")
            }
            sb.append(" question LIKE '%${v}%'")
        }
        sb.append(";")
        return getObjectsRawQuery(SimpleSQLiteQuery(sb.toString()))
    }
}

TheDatabase(为方便起见,未使用 .allowMainThreadQueries):-

@Database(entities = [Objects::class], version = 1, exportSchema = false)
abstract class TheDatabase: RoomDatabase() {
    abstract fun getAllDAO(): AllDAO

    companion object {
        var instance: TheDatabase? = null
        fun getInstance(context: Context): TheDatabase {
            if (instance == null) {
                instance = Room.databaseBuilder(context,TheDatabase::class.java,"the_database.db")
                    .allowMainThreadQueries()
                    .build()
            }
            return instance as TheDatabase
        }
    }
}

将它们放在一起,加载一些测试数据并运行提取一些数据:-

class MainActivity : AppCompatActivity() {
    lateinit var db: TheDatabase
    lateinit var dao: AllDAO
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        db = TheDatabase.getInstance(this)
        dao = db.getAllDAO()

        dao.insert(Objects(question = "This is a cat."))
        dao.insert(Objects(question = "This is a flower."))
        dao.insert(Objects(question = "this is nothing."))
        dao.insert(Objects(question = "The quick brown fox jumped over the lazy dog"))

        logObjects(dao.getObjects(listOf("cat","dog")),"Extract1\t")
        logObjects(dao.getObjects(listOf("flower","cat")),"Extract2\t")
        logObjects(dao.getObjects(listOf("brown","nothing")),"Extract3\t")
    }

    fun logObjects(objects: List<Objects>,prefix: String) {
        for (o in objects) {
            Log.d("OBJECTINFO","$prefix Question is ${o.question} ID is ${o.id}")
        }
    }
}

结果

2022-04-18 04:58:05.471 D/OBJECTINFO: Extract1   Question is This is a cat. ID is 1
2022-04-18 04:58:05.471 D/OBJECTINFO: Extract1   Question is The quick brown fox jumped over the lazy dog ID is 4

2022-04-18 04:58:05.473 D/OBJECTINFO: Extract2   Question is This is a cat. ID is 1
2022-04-18 04:58:05.473 D/OBJECTINFO: Extract2   Question is This is a flower. ID is 2

2022-04-18 04:58:05.474 D/OBJECTINFO: Extract3   Question is this is nothing. ID is 3
2022-04-18 04:58:05.474 D/OBJECTINFO: Extract3   Question is The quick brown fox jumped over the lazy dog ID is 4
  • 注意上面没有考虑处理空列表(由于SELECT * FROM objects WHERE ;的语法错误会导致失败)。也就是说这个例子只是为了演示基本原理。