使用首选语言的SQLite FTS4
我有一个SQLite表,它是使用FTS4模块生成的。每个条目至少用不同的语言列出两次,但仍然共享一个唯一的ID(int列,不索引)。 以下是我想做的: 我想用首选语言查找一个术语。我想使用另一种语言将结果与同一术语的查找合并。 但是,对于第二次查找,我希望忽略在第一次查找期间已经找到的所有条目(由它们的ID标识)。所以基本上我想这样做:使用首选语言的SQLite FTS4,sqlite,full-text-search,materialize,Sqlite,Full Text Search,Materialize,我有一个SQLite表,它是使用FTS4模块生成的。每个条目至少用不同的语言列出两次,但仍然共享一个唯一的ID(int列,不索引)。 以下是我想做的: 我想用首选语言查找一个术语。我想使用另一种语言将结果与同一术语的查找合并。 但是,对于第二次查找,我希望忽略在第一次查找期间已经找到的所有条目(由它们的ID标识)。所以基本上我想这样做: WITH term_search1 AS ( SELECT * FROM myFts WHERE myFts MATCH 'term'
WITH term_search1 AS (
SELECT *
FROM myFts
WHERE myFts MATCH 'term'
AND languageId = 1)
SELECT *
FROM term_search1
UNION
SELECT *
FROM myFts
WHERE myFts MATCH 'term'
AND languageId = 2
AND id NOT IN (SELECT id FROM term_search1)
这里的问题是,术语_seach1查询将执行两次。有没有办法实现我的结果?任何将其限制为2个查询(而不是3个)的解决方案都是非常好的
我还尝试使用递归查询,例如:
WITH RECURSIVE term_search1 AS (
SELECT *
FROM myFts
WHERE myFts MATCH 'term'
AND languageId = 1
UNION ALL
SELECT m.*
FROM myFts m LEFT OUTER JOIN term_search1 t ON (m.id = t.id)
WHERE myFts MATCH 'term'
AND m.languageId = 2
AND t.id IS NULL
)
SELECT * FROM term_search1
这也没用。显然,他只是对languageId=2执行了两次查找(这可能是一个bug吗?)
提前感谢:)您可以使用临时表将myFts的查询数量减少到2:
CREATE TEMP TABLE results (id INTEGER PRIMARY KEY);
INSERT INTO results
SELECT id FROM myFts
WHERE myFts MATCH 'term' AND languageId = 1;
INSERT INTO results
SELECT id FROM myFts
WHERE myFts MATCH 'term' AND languageId = 2
AND id NOT IN (SELECT id FROM results);
SELECT * FROM myFts
WHERE id IN (SELECT id FROM results);
DROP TABLE results;
如果可以更改模式,则只应在FTS表中保留文本数据。这样,在搜索不需要匹配
languageId
的数字和行时,可以避免错误的结果。创建另一个包含非文本数据(如id
和languageId
)的元表,并根据myFts
的rowid
合并来过滤行这样,您只需查询一次FTS表-使用临时表存储FTS表结果,然后使用元表对其进行排序。这是我能想到的最好方法:
SELECT *
FROM myFts t1
JOIN (SELECT COUNT(*) AS cnt, id
FROM myFts t2
WHERE t2.languageId in (1, 2)
AND t2.myFts MATCH 'term'
GROUP BY t2.id) t3
ON t1.id = t3.id
WHERE t1.myFts MATCH 'term'
AND t1.languageId in (1, 2)
AND (t1.languageId = 1 or t3.cnt = 1)
我不确定是否需要第二个MATCH
子句。
这样做的想法是首先计算可接受的行数,然后选择最好的一行
编辑:我不知道为什么它不适用于您的表格。这就是我测试它所做的(SQLite版本3.8.10.2):
执行查询将提供:
sqlite> SELECT *
...> FROM myFts t1
...> JOIN (SELECT COUNT(*) AS cnt, id
...> FROM myFts t2
...> WHERE t2.languageId in (1, 2)
...> AND t2.myFts MATCH 'term'
...> GROUP BY t2.id) t3
...> ON t1.id = t3.id
...> WHERE t1.myFts MATCH 'term'
...> AND t1.languageId in (1, 2)
...> AND (t1.languageId = 1 or t3.cnt = 1);
10|1|term 10 lang 1|2|10
11|1|term 11 lang 1|1|11
12|2|term 12 lang 2|1|12
13|2|term 13 lang 2|1|13
sqlite>
您好,很遗憾,您的查询不起作用。根据SQLite文档“单个FTS查询不可能返回具有不同languageid值的行”:/这就是为什么我必须首先将其拆分为两个查询的原因。奇怪的是,我对其进行了测试,它似乎可以工作。您的表是如何定义的?您没有正确使用languageId功能。尝试使用fts4(id,content,languageid=“languageid”)创建带有
CREATE VIRTUAL table myFts的表代码>然后,即使是一个简单的查询,如从myFts中选择*内容匹配“term*”
不返回结果,因为它假定languageId=0
。我不知道FTS4languageId=
选项。这在很大程度上限制了查询中可以做的事情,我必须研究一下这一点。到目前为止,创建临时表显然是解决这个问题的唯一解决方案(经常执行时听起来仍然像是性能杀手)。关于模式相关的东西。。languageId实际上不是我自己的专栏。它是FTS功能的一部分(隐藏列)。根据FTS文档“单个FTS查询不可能返回具有不同languageid值的行”:/这就是为什么我总是需要至少两个查询来完成此操作。创建临时表肯定是一项开销,但可能仍然会有性能。不幸的是,我的SQLite无法创建带有languageid扩展名的FTS4,因此我无法分析查询。您需要不同的令牌化器而不使用默认unicode61的具体原因是什么?
sqlite> SELECT *
...> FROM myFts t1
...> JOIN (SELECT COUNT(*) AS cnt, id
...> FROM myFts t2
...> WHERE t2.languageId in (1, 2)
...> AND t2.myFts MATCH 'term'
...> GROUP BY t2.id) t3
...> ON t1.id = t3.id
...> WHERE t1.myFts MATCH 'term'
...> AND t1.languageId in (1, 2)
...> AND (t1.languageId = 1 or t3.cnt = 1);
10|1|term 10 lang 1|2|10
11|1|term 11 lang 1|1|11
12|2|term 12 lang 2|1|12
13|2|term 13 lang 2|1|13
sqlite>