# 全文搜索
注意
本指南仅适用于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: 使用 ICU 分词器在单个文本列上创建索引。
ALTER TABLE [table_name] ADD INDEX [index_name] [column_name]
TYPE fts('{"<column_name>":{"tokenizer":{"type":"icu"}}}');
-- 示例 4: 使用词干分词器并应用停用词进行过滤,创建索引。
ALTER TABLE [table_name] ADD INDEX [index_name] [column_name]
TYPE fts('{"<column_name>":{"tokenizer":{"type":"stem", "stop_word_filters":["english"]}}}');
-- 示例 5: 在多个文本列上创建索引;每个列默认使用默认分词器。
ALTER TABLE [table_name] ADD INDEX [index_name] ([column_name_0], [column_name_1], ...)
TYPE fts;
-- 示例 6: 为索引中的每个文本列指定不同的分词器类型。
ALTER TABLE [table_name] ADD INDEX [index_name] ([column_name_0], [column_name_1], ...)
TYPE fts('{"<column_name_0>":{"tokenizer":{"type":"icu"}}, "<column_name_1>":{"tokenizer":{"type":"whitespace"}}}');
下表列出了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 分词器,可以通过关注单词的词干形式来提高搜索准确性。
单列索引和多列索引在文本搜索功能中发挥着至关重要的作用。单列索引有助于在特定字段中进行高效搜索,而多列索引则允许在多个字段中进行全面搜索,从而提高整体搜索性能。要在单列索引上执行文本搜索,可以使用 TextSearch 函数。对于多列索引,full_text_search 函数可以在所有索引列上实现有效搜索。
# 创建单列文本索引
要创建单列文本索引,请使用以下 SQL 语句:
ALTER TABLE default.en_wiki_abstract
ADD INDEX body_idx body
TYPE fts('{"body":{"tokenizer":{"type":"stem", "stop_word_filters":["english"]}}}');
在此示例中,我们在 body 列上创建了一个 FTS 索引。请注意,FTS 索引(类似于 ClickHouse 中的跳过索引)仅应用于新插入的数据。因此,仅添加索引不会影响现有数据。要为已有记录建立索引,请执行以下语句:
ALTER TABLE default.en_wiki_abstract MATERIALIZE INDEX body_idx;
# 创建多列文本索引
您还可以为多个文本列创建索引,以提高跨多个字段的搜索能力。例如:
ALTER TABLE default.en_wiki_abstract
ADD INDEX multi_col_idx (body, title)
TYPE fts('{"body":{"tokenizer":{"type":"stem", "stop_word_filters":["english"]}}, "title":{"tokenizer":{"type":"default"}}}');
在这种情况下,我们为 body 和 title 列定义了 FTS 索引,并为每一列使用不同的分词器。当然,您还需要像创建单列索引时一样执行 MATERIALIZE 语句:
ALTER TABLE default.en_wiki_abstract MATERIALIZE INDEX multi_col_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 |
从DB版本v1.8或更高版本开始,TextSearch()
函数可以使用多列FTS索引在某个文本列上进行搜索。使用方式与单列FTS索引相同。
# 利用自然语言查询
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 将返回与所有条件匹配的结果。 |
NOTE
分布式表的TextSearch仅在数据库版本v1.7.0或更高版本中可用。对于分布式表的TextSearch,只需要在本地表上创建相应的FTS索引即可。
以下是 TextSearch()
查询设置的详细说明:
Settings | Default Value | Candidate Values | Description |
---|---|---|---|
dfs_query_then_fetch | false | true , false | 对于分布式表,此设置决定是否首先执行分布式查询以从所有分片收集统计信息,然后根据收集的统计信息进行精确的全文搜索(FTS)。 |
# 示例用法
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 |
# 表函数 full_text_search
Note
表函数 full_text_search() 仅在数据库版本 v1.7.0 或更高版本中可用。
表函数 full_text_search() 用于在 MyScale 中对包含多个文本列的 FTS 索引执行文本搜索。full_text_search() 表函数的基本语法如下:
full_text_search(table_name, index_name, query [,with_score] [,enable_nlq] [,operator])
table_name
指的是包含 FTS 索引的表。index_name
指的是包含多个要搜索的文本列的 FTS 索引。query
指的是符合 Tantivy 查询语言的搜索字符串。with_score
指的是是否返回 bm25 分数列(默认为 0)。enable_nlq
指的是是否启用自然语言查询解析(默认为 1)。operator
指的是用于组合每个查询词(由分词器分词)的逻辑运算符(默认为 'OR')。
以下示例展示了如何使用 full_text_search()
函数。该示例返回 body 列包含 "non-profit institute in Washington" 或 title 列包含 "Institute" 的最相关的前 5 篇文档。相关性的衡量指标是 full_text_search()
函数返回的 BM25 分数,分数越高,相关性越强。
SELECT
id,
title,
body,
bm25_score
FROM full_text_search('default.en_wiki_abstract', 'multi_col_idx', 'body:non-profit institute in Washington or title:Institute', 1)
ORDER BY bm25_score DESC
LIMIT 5;
从结果中可以看到,body 列中的每一行文本要么与搜索词 "non-profit institute in Washington" 相关,要么 title 列中的文本与搜索词 "Institute" 相关。
id | title | body | bm25_score |
---|---|---|---|
849009 | Washington Institute | Washington Institute may mean | 38.110603 |
872513 | Earth Policy Institute | Earth Policy Institute was an independent non-profit environmental organization based in Washington, D.C. | 35.545998 |
895248 | Arab American Institute | Founded in 1985, the Arab American Institute is a non-profit membership organization based in Washington D.C. | 34.77088 |
1730029 | Washington Cancer Institute | The Washington Cancer Institute (WCI) is Washington, D.C. | 34.445385 |
1950599 | Environmental Law Institute | The Environmental Law Institute (ELI) is a non-profit, non-partisan organization, headquartered in Washington, D.C. | 34.046894 |
# 加速 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强大的文本处理能力。