问题中链接的 SQLite 文档阐明了该rank
函数在使用它的查询上方的注释中的作用:
如果应用程序提供了一个名为“rank”的 SQLite 用户函数,它解释 matchinfo 返回的数据块并基于它返回一个数字相关性,那么可以使用以下 SQL 来返回数据集中 10 个最相关文档的标题用于用户查询。
rank
预计是用户提供的功能。它不随 SQLite 一起提供。
这是 Kotlin 中函数的实现,它根据使用默认“pcx”参数rank
提供的数据计算相关性分数:matchinfo
fun rank(matchInfo: IntArray): Double {
val numPhrases = matchInfo[0]
val numColumns = matchInfo[1]
var score = 0.0
for (phrase in 0 until numPhrases) {
val offset = 2 + phrase * numColumns * 3
for (column in 0 until numColumns) {
val numHitsInRow = matchInfo[offset + 3 * column]
val numHitsInAllRows = matchInfo[offset + 3 * column + 1]
if (numHitsInAllRows > 0) {
score += numHitsInRow.toDouble() / numHitsInAllRows.toDouble()
}
}
}
return score
}
要了解此代码的工作原理,您应该阅读官方文档中给出的rankfunc
示例。
由于我们的 rank 函数是 Kotlin 函数,SQLite 不能直接使用它。相反,我们需要首先matchinfo
从数据库中检索 blob,然后将其传递给我们的 rank 函数。
这是一个关于如何使用 Room 执行此操作的示例:
@Dao
interface PersonsDao {
@Query("""
SELECT *, matchinfo(persons_fts, 'pcx') as mi
FROM persons
JOIN persons_fts ON persons.name = persons_fts.name
WHERE persons_fts MATCH :query
""")
suspend fun search(query: String): List<PersonWithMatchInfo>
}
data class PersonWithMatchInfo(
@Embedded
val person: Person
@ColumnInfo(name = "mi")
val matchInfo: ByteArray
)
检索到的ByteArray
包含表示匹配信息的数字序列,其中每个数字由 4 个字节表示。第一个字节是实际值,接下来的三个字节为零。因此,我们需要在将此 ByteArray 传递给rank
. 这可以通过一个简单的方法来完成:
fun ByteArray.skip(skipSize: Int): IntArray {
val cleanedArr = IntArray(this.size / skipSize)
var pointer = 0
for (i in this.indices step skipSize) {
cleanedArr[pointer] = this[i].toInt()
pointer++
}
return cleanedArr
}
可以像这样使用此设置:
suspend fun searchWithRanks(query: String): List<Person> {
return personDao.search(query)
.sortedByDescending { result -> rank(result.matchInfo.skip(4)) }
.map { result -> result.person }
}