问题描述
我有两个表:
-
persons
-
persons_fts
。
这是表格的定义:
CREATE TABLE persons(name TEXT PRIMARY KEY NOT NULL,details TEXT);
CREATE VIRTUAL TABLE persons_fts USING FTS4(name TEXT NOT NULL,details TEXT,context=persons);
我想对persons_fts
表上的查询进行全文搜索,然后根据相关性对结果进行排名。在查看了official docs的操作方法后,我以以下查询结束:
SELECT *
FROM persons
JOIN persons_fts ON persons.name = persons_fts.name
WHERE persons_fts MATCH :query
ORDER BY rank(matchinfo(persons_fts)) DESC;
除了额外的联接外,该查询与官方文档中列出的查询完全相同。但是,当我尝试执行它时出现错误:
我在做什么错了?
请注意,对于我来说,不能使用FTS5。
解决方法
问题中链接的SQLite文档阐明了response
函数在其所用查询上方的注释中的作用:
如果应用程序提供了一个称为“ rank”的SQLite用户函数,该函数可解释matchinfo返回的数据块并基于其返回数字相关性,则可以使用以下SQL来返回10个最相关文档的标题在数据集中进行用户查询。
void wordCount(std::string wordFile)
{
std::map<std::string,int> M;
std::string word = "";
for (int i = 0; i < str.size(); i++)
{
if (str[i] == ' ')
{
if (M.find(word) == M.end())
{
M.insert(make_pair(word,1));
word = "";
}
else
{
M[word]++;
word = "";
}
}
else
word += str[i];
}
if (M.find(word) == M.end())
M.insert(make_pair(word,1));
else
M[word]++;
for (auto &it : M)
{
std::cout << it.first << ": Occurs "
<< it.second
<< std::endl;
}
}
应该是用户提供的功能。它不随SQLite一起提供。
这是Kotlin中rank
函数的实现,该函数根据rank
提供的数据,使用默认的“ pcx”参数计算相关性得分:
rank
要了解此代码的工作原理,请仔细阅读官方文档中提供的rankfunc
example。
由于我们的等级函数是Kotlin函数,因此SQLite不能直接使用它。相反,我们需要首先从数据库中检索matchinfo
blob,然后将其传递给我们的rank函数。
以下是如何使用Room进行操作的示例:
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
}
检索到的matchinfo
包含代表匹配信息的数字序列,其中每个数字均由4个字节表示。第一个字节是实际值,接下来的三个字节为零。因此,我们需要在将此ByteArray传递到@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
此设置可以像这样使用:
rank