使用首选语言的SQLite FTS4

使用首选语言的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'

我有一个SQLite表,它是使用FTS4模块生成的。每个条目至少用不同的语言列出两次,但仍然共享一个唯一的ID(int列,不索引)。 以下是我想做的: 我想用首选语言查找一个术语。我想使用另一种语言将结果与同一术语的查找合并。 但是,对于第二次查找,我希望忽略在第一次查找期间已经找到的所有条目(由它们的ID标识)。所以基本上我想这样做:

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
。我不知道FTS4
languageId=
选项。这在很大程度上限制了查询中可以做的事情,我必须研究一下这一点。到目前为止,创建临时表显然是解决这个问题的唯一解决方案(经常执行时听起来仍然像是性能杀手)。关于模式相关的东西。。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>