# 全文搜索
注意
本指南仅适用于DB版本1.5.0或更高版本。
多年来,具有LIKE
和ILIKE
等运算符的传统数据库文本搜索功能一直是基础。然而,由于几个限制,它们无法满足现代信息检索系统的需求:
- 缺乏语言支持: 传统方法在处理语言细微差别时存在困难,无法识别单词派生(例如,“satisfies”与“satisfy”),这可能导致搜索结果不完整或不准确。虽然可以使用
OR
手动搜索变体,但这种方法繁琐且容易出错。 - 缺乏结果排序: 没有结果排序的能力,浏览成千上万个匹配项变得低效。
- 性能问题: 缺乏索引支持意味着每次搜索都必须处理每个文档,导致性能缓慢。
为了克服这些挑战,MyScale引入了一种名为FTS索引(全文搜索索引)的新索引类型,由Tantivy (opens new window)库提供支持,这是一个高性能的开源全文搜索引擎库。FTS索引支持BM25索引算法,实现了高效和相关的搜索结果。这种集成增强了MyScale的全文搜索功能,并提高了整体性能。
# 教程概述
本教程将指导您进行三种类型的FTS索引搜索实验:
- 创建FTS索引:了解如何为不同的用例配置FTS索引。
- 使用
TextSearch()
函数进行搜索:了解如何使用BM25分数搜索和排序文本。 - 自然语言查询:通过使用
AND
和OR
等自然语言连接词来增强搜索逻辑。 - MyScale中的字符串搜索函数:探索MyScale如何利用FST索引来改进字符串搜索功能。
在开始之前,请确保您已经设置了一个MyScale集群。有关设置说明,请参阅我们的快速入门指南 (opens new window)。
# 数据集概述
我们将使用Wikipedia摘要数据集 (opens new window),其中包含超过560万条记录,以Parquet格式提供。此数据集将直接从S3导入到MyScale中,无需本地下载。
下表简要描述了此数据集的内容。
id | body | title | url |
---|---|---|---|
... | ... | ... | ... |
77 | Jake Rodkin is an American .... and Puzzle Agent. | Jake Rodkin | https://en.wikipedia.org/wiki/Jake_Rodkin (opens new window) |
78 | Friedlandpreis der Heimkehrer is ... of Germany. | Friedlandpreis der Heimkehrer | https://en.wikipedia.org/wiki/Friedlandpreis_der_Heimkehrer (opens new window) |
... | ... | ... | ... |
# 创建和填充表
使用以下SQL命令在MyScale中创建en_wiki_abstract
表:
CREATE TABLE default.en_wiki_abstract(
`id` UInt64,
`body` String,
`title` String,
`url` String,
)
ENGINE = MergeTree
ORDER BY id;
然后,从S3导入数据集。请耐心等待数据导入完成。
INSERT INTO default.en_wiki_abstract SELECT * FROM s3('https://myscale-datasets.s3.ap-southeast-1.amazonaws.com/wiki_abstract_5m.parquet','Parquet');
验证表中包含5648453行数据。
SELECT count(*) FROM default.en_wiki_abstract;
输出:
count() |
---|
5648453 |
为了提高搜索性能,我们可以通过将其合并为单个数据部分来优化表。这一步是可选的。
OPTIMIZE TABLE default.en_wiki_abstract FINAL;
运行以下SQL语句检查此表中的数据是否已压缩为一个部分。
SELECT COUNT(*) FROM system.parts
WHERE table = 'en_wiki_abstract' AND active = 1;
如果数据压缩为1,则此SQL语句将返回以下结果集:
count() |
---|
1 |
# 理解FTS索引参数
MyScale支持各种分词器,每个分词器适用于不同的场景。在创建FTS索引时,您可以使用JSON配置为分词器自定义索引。
提示
请在FTS索引的参数中提供有效的JSON字符串。
-- 示例1:创建一个没有指定参数的索引,使用默认配置
ALTER TABLE [table_name] ADD INDEX [index_name] [column_name]
TYPE fts;
-- 示例2:使用默认分词器创建索引
ALTER TABLE [table_name] ADD INDEX [index_name] [column_name]
TYPE fts('{"<column_name>":{"tokenizer":{"type":"default"}}}');
-- 示例3:使用stem分词器并应用停用词
ALTER TABLE [table_name] ADD INDEX [index_name] [column_name]
TYPE fts('{"<column_name>":{"tokenizer":{"type":"stem", "stop_word_filters":["english"]}}}');
下表列出了FTS索引支持的分词器类型。
分词器类型 | 描述 |
---|---|
default | 默认分词器,按非字母字符拆分文本,不区分大小写 |
raw | 原始分词器,在文本上不进行分词处理,将整个文本视为单个标记 |
simple | 简单分词器,按非字母字符拆分文本 |
stem | 词干分词器,支持多种语言,将单词转换为其词干形式,可以忽略单词时态 |
whitespace | 空格分词器,按空格字符(空格、制表符、换行等)拆分文本 |
ngram | N-gram分词器,根据指定的n-gram 长度拆分文本 |
chinese | 中文分词器,对中文文本进行分词处理,内部使用jieba分词库进行分词 |
icu | ICU 分词器擅长处理多语言文本。如果你在多语言文本分词方面遇到困难,ICU 分词器将是你的终极解决方案。 |
# 常见分词器参数
default
和raw
分词器仅支持store_doc
参数,其他分词器除了支持上述常见参数外,还支持以下常见参数。
参数名称 | 类型 | 默认值 | 描述 |
---|---|---|---|
store_doc | 布尔值 | false | 是否存储原始文档,目前不建议启用 |
length_limit | 数字 | 40 | 分词标记的最大长度 |
case_sensitive | 布尔值 | false | 分词是否区分大小写 |
除了上述常见参数外,大多数分词器还支持其他参数。
# simple
、stem
、whitespace
、icu
分词器
参数名称 | 类型 | 默认值 | 描述 |
---|---|---|---|
stop_word_filters | 字符串数组 | [] | 停用词过滤器,指定在分词过程中要丢弃的停用词的语言,所有有效的语言包括["danish", "dutch", "english", "finnish", "french", "german", "hungarian", "italian", "norwegian", "portuguese", "russian", "spanish", "swedish"] |
# stem
、icu
分词器
参数名称 | 类型 | 默认值 | 描述 |
---|---|---|---|
stem_languages | 字符串数组 | [] | 用于词干提取的语言,对于英语,它可以在分词过程中忽略单词时态,stem分词器支持的语言有["arabic", "danish", "dutch", "english", "finnish", "french", "german", "greek", "hungarian", "italian", "norwegian", "portuguese", "romanian", "russian", "spanish", "swedish", "tamil", "turkish"] |
# ngram
分词器
参数名称 | 类型 | 默认值 | 描述 |
---|---|---|---|
min_gram | 数字 | 2 | 最小n-gram数量 |
max_gram | 数字 | 3 | 最大n-gram数量 |
prefix_only | 布尔值 | false | 是否仅从单词前缀提取n-gram |
# chinese
分词器
对于中文分词器,使用cang-jie (opens new window)作为底层实现。
参数名称 | 类型 | 默认值 | 描述 |
---|---|---|---|
jieba | 字符串 | "default" | "default" 表示使用jieba字典,"empty" 表示不使用内置的jieba字典进行分词。有效值为"default" 或"empty" |
mode | 字符串 | "search" | 中文分词模式,有效值为"all"、"default"、"search"或"unicode",每种模式之间的区别可以参考cang-jie/options.rs (opens new window) |
hmm | 布尔值 | false | 是否启用HMM |
# icu
分词器
参数名称 | 类型 | 默认值 | 描述 |
---|---|---|---|
mode | 字符串 | "word" | 有效值为 "word" 、"sentence" 、"line" 、"grapheme" 、"word" 。 "word" 模式在多语言环境中表现最佳。 |
更多关于 ICU 分词器的特性信息,请参见 icu_segmenter (opens new window)
上述提供了MyScale FTS索引分词器参数的详细描述。在创建FTS索引时,您可以选择最适合您需求的分词器类型,并配置其参数以提高搜索性能和结果。
# 创建FTS索引
使用适当的分词器自定义您的FTS索引以优化搜索性能。例如,使用带有英语停用词的stem
分词器可以通过关注单词的根形式来提高搜索准确性。
ALTER TABLE default.en_wiki_abstract
ADD INDEX body_idx (body)
TYPE fts('{"body":{"tokenizer":{"type":"stem", "stop_word_filters":["english"]}}}');
通常情况下,FTS索引(类似于ClickHouse中的跳过索引)仅适用于新插入的数据,因此仅添加索引不会影响现有数据。要对已存在的数据进行索引,请使用以下语句:
ALTER TABLE default.en_wiki_abstract MATERIALIZE INDEX body_idx;
# 使用文档BM25排序进行搜索
注意
首次执行TextSearch()
可能会较慢,因为它需要加载FTS索引。
以下示例演示了如何使用TextSearch()
函数。该示例返回与“non-profit institute in Washington”相关的前10个最相关文档。衡量相关性的指标是TextSearch()
函数返回的BM25分数,分数越高,相关性越高。
SELECT
id,
title,
body,
TextSearch(body, 'non-profit institute in Washington') AS score
FROM default.en_wiki_abstract
ORDER BY score DESC
LIMIT 5;
从结果中,我们可以看到每行body列中的文本与搜索短语“non-profit institute in Washington”相关。
id | title | body | score |
---|---|---|---|
3400768 | Drug Strategies | Drug Strategies is a non-profit research institute located in Washington D.C. | 24.457561 |
872513 | Earth Policy Institute | Earth Policy Institute was an independent non-profit environmental organization based in Washington, D.C. | 22.730673 |
895248 | Arab American Institute | Founded in 1985, the Arab American Institute is a non-profit membership organization based in Washington D.C. | 21.955559 |
1950599 | Environmental Law Institute | The Environmental Law Institute (ELI) is a non-profit, non-partisan organization, headquartered in Washington, D.C. | 21.231567 |
2351478 | Public Knowledge | Public Knowledge is a non-profit Washington, D.C. | 20.742344 |
# 利用自然语言查询
MyScale使用Tantivy库进行全文搜索(FTS)索引,支持复杂的自然语言查询。有关详细信息,请参阅Tantivy的文档 (opens new window)。
下面是一个使用AND
和OR
进行多条件组合查询的示例。我们想要搜索与纽约市、非洲或巴黎相关的人类学主题。SQL查询如下:
SELECT
id,
title,
body,
TextSearch(body, 'Anthropological AND ("New York City" OR African OR Paris)') AS score
FROM default.en_wiki_abstract
ORDER BY score DESC
LIMIT 5;
搜索结果显示,每行文本都包含左侧AND语句中的条件“Anthropological”,同时每个结果至少包含右侧AND语句中的一个条件“African”、“New York City”或“Paris”。
id | title | body | score |
---|---|---|---|
2826913 | African Anthropologist | African Anthropologist is the journal of the Pan African Anthropological Association (PAAA). | 20.131313 |
3905943 | Tunnel People | Tunnel People is an anthropological-journalistic account describing an underground homeless community in New York City. It is written by war photographer and anthropologist Teun Voeten and was published by PM Press in 2010. | 13.759308 |
3790627 | Les Accords de Bella | Les Accords de Bella is a 2007 anthropological documentary film directed by David Constantin. It was selected by the African Film Festival of Cordoba - FCAT. | 12.769518 |
4488199 | Naparay | Naparay, in African anthropological study, is a non-linear conception of human life held by some West African peoples such as the Yoruba. Similar to reincarnation, naparay holds that lives are cyclic and attributes of previous lives may carry over to a new life. | 11.682068 |
1725559 | Gradhiva | Gradhiva is an anthropological and museological journal, founded in 1986 by the poet and social scientist Michel Leiris and by the anthropologist Jean Jamin. It is since 2005 published by the Musée du Quai Branly in Paris. | 11.135916 |
# TextSearch参数解释
注意
参数仅适用于DB版本v1.6.3或更高版本。
以下是TextSearch()
参数的详细说明:
参数 | 默认值 | 候选值 | 描述 |
---|---|---|---|
enable_nlq | true | true , false | 此参数决定是否启用自然语言查询解析。当设置为true 时,FTS将将用户输入解释为自然语言查询。当设置为false 时,FTS将使用标准术语查询解析用户输入。 |
operator | OR | OR , AND | 此参数指定用于组合每个查询条件(由分词器标记化)的逻辑运算符。选择OR 将返回与任何条件匹配的结果,而选择AND 将返回与所有条件匹配的结果。 |
# 示例用法
SELECT
id,
title,
body,
TextSearch('enable_nlq=true', 'operator=OR')(body, 'mammoth AND Europe') AS score
FROM default.wiki_abstract_text
ORDER BY score DESC
LIMIT 5
基于结果,每行都包含“mammoth”和“Europe”,符合启用自然语言查询的逻辑。
请注意,只有2个搜索结果。尽管我们的SQL查询将限制设置为5,但只有2个表中的条目符合条件。
id | title | body | score |
---|---|---|---|
3171491 | Leymus racemosus | Leymus racemosus is a species of perennial wild rye known by the common name mammoth wild rye. It is native to southeastern and eastern Europe, Middle Asia, Caucasus, Siberia, China, Mongolia, New Zealand, and parts of North America. | 10.067189 |
2719784 | Venus of Hohle Fels | The Venus of Hohle Fels (also known as the Venus of Schelklingen; in German variously ) is an Upper Paleolithic Venus figurine made of mammoth ivory that was unearthed in 2008 in Hohle Fels, a cave near Schelklingen, Germany. It is dated to between 35,000 and 40,000 years ago, belonging to the early Aurignacian, at the very beginning of the Upper Paleolithic, which is associated with the earliest presence of Cro-Magnon in Europe. | 6.9371195 |
SELECT
id,
title,
body,
TextSearch('enable_nlq=false', 'operator=OR')(body, 'Atlantic AND Europe') AS score
FROM default.wiki_abstract_text
ORDER BY score DESC
LIMIT 5
在禁用自然语言查询后,结果不再保证同时出现“Atlantic”和“Europe”。由于运算符的默认值是OR,任何包含“Atlantic”、“AND”或“Europe”的行都将包含在搜索结果中。
id | title | body | score |
---|---|---|---|
3046233 | And | And or AND may refer to: | 13.748591 |
5050203 | A N D (Tricot album) | And}} | 13.047318 |
357499 | Andromeda I | And 1}} | 12.335348 |
678064 | Omicron Andromedae | And 1}} | 12.335348 |
3716928 | Platycheirus ramsaerensis | Platycheirus ramsaerensis is a species of hoverfly. It is found along the parts of northern Europe that face the Atlantic. | 11.937536 |
# 加速 ClickHouse 内置字符串函数
FTS 索引也可以用来加速 ClickHouse 的内置字符串函数,包括 equals
、notEquals
、like
、notLike
、hasToken
、hasTokenOrNull
、in
、notIn
、startsWith
、endsWith
、has
、mapContains
和 multiSearchAny
。
为了控制在执行这些字符串函数时是否启用 FTS 索引,我们提供了一个名为 enable_fts_index_for_string_functions
的查询设置。默认值是 0,这意味着在执行这些内置 ClickHouse 函数时不使用 FTS 索引。用户可以手动将其设置为 1 以启用 FTS。有关如何使用查询设置的更多信息,请参阅 ClickHouse Query-level Settings (opens new window)。
# 为什么 enable_fts_index_for_string_functions
的默认值是 0?
效率考虑:
当搜索的字符串在整个表中相对不常出现时,使用 FTS 可以实现显著加速。然而,如果搜索的字符串在表中非常频繁地出现,如常见的单词 What
、his
等,在这种情况下使用 FTS 索引实际上会减慢常规函数的查询速度,因为大部分粒度单元(granules)都需要读取。
搜索结果准确性:
FTS 索引将表中的文本进行分词,并且分词的行为因分词器的类型而异。例如,raw
分词器将原始文本视为一个单一的分词,而 whitespace
分词器根据空格将原始文本拆分为多个分词。以下是这两个分词器对 "WhiteHouse is very beautiful."
的分词结果:
tokenizer(raw)
:token<"WhiteHouse is very beautiful.">
tokenizer(whitespace)
:token<"WhiteHouse">
,token<"is">
,token<"very">
,token<"beautiful">
分词器的行为会影响字符串匹配函数的准确性。当使用 raw
分词器时,执行 like 查询 %WhiteHouse%beautiful%
,FTS 会将 like 查询转换为 Tantivy 中的 RegexQuery
并搜索所有分词,可以正确匹配 token<"WhiteHouse is very beautiful.">
。
但是,当使用 whitespace
分词器时,FTS 会将 like 查询 %WhiteHouse%beautiful%
转换为正则表达式匹配,发现它无法匹配任何现有分词,因此无法将文本片段标记为命中,导致搜索结果不准确。
因此,对于转换为 RegexQuery
的字符串函数,包括 like
、notLike
、startsWith
和 endsWith
,当 enable_fts_index_for_string_functions
设置为 1 时,只有 raw
分词器才能提供正确的结果。
# 使用示例
示例 1:执行简单的关键词搜索
搜索单词 'Tsinghua'。
SELECT count(*)
FROM default.en_wiki_abstract
WHERE hasToken(body, 'Tsinghua')
SETTINGS enable_fts_index_for_string_functions=1;
输出:
count() |
---|
81 |
示例 2:执行多关键词搜索
查找包含 "Eiffel Tower" 但不包含 "Paris" 的文章。
SELECT count(*)
FROM default.en_wiki_abstract
WHERE (NOT hasToken(body, 'Paris')) AND multiSearchAny(body, ['Eiffel', 'Tower'])
SETTINGS enable_fts_index_for_string_functions=1;
输出:
count() |
---|
2828 |
示例 3:执行复杂条件组合查询
组合不同的名称、地点、学科和其他术语。
SELECT count(*)
FROM default.en_wiki_abstract
WHERE (NOT multiSearchAny(body, ['Montessori', 'postulated', 'Rooney'])) AND (hasToken(body, 'Patsy') OR hasToken(body, 'Anthropological'))
SETTINGS enable_fts_index_for_string_functions=1;
输出:
count() |
---|
204 |
# 结论
本指南介绍了如何利用MyScale进行高级文本搜索功能,从设置FTS索引到执行自然语言查询。通过应用这些技术,您可以高效地管理和搜索非结构化文本数据,展示了MyScale强大的文本处理能力。