作者:来自 Elastic Kevin Corcoran 及 Ioana Tagirta

MATCH 和 TO_TEXT 将全文搜索能力带到你从未建立索引的数据中。在 ES|QL 中,你可以对计算列、未映射字段以及联邦数据源执行全文搜索。
ES|QL MATCH 现在可以对你从未建立索引的数据执行全文搜索。无论是计算列、未映射字段、动态拼接生成的字符串,还是存储在 S3 中的联邦数据,都可以进行全文搜索。新的 TO_TEXT 函数会告诉 ES|QL 将任意字符串视为可分析(analyzable)的文本,因此 MATCH 可以对仅在查询生命周期内存在的值执行分词、大小写归一化以及词项匹配。这超越了大多数查询引擎针对未建立索引字符串所提供的 LIKE 和 RLIKE 模式匹配,而是真正的全文分析功能。该功能现已在 Elastic Cloud Serverless 中提供,并作为 Elasticsearch 9.5 的技术预览功能发布。
MATCH 和 TO_TEXT 如何让任意 ES|QL 表达式支持全文搜索
让我们从一个在 Elasticsearch 9.4 中无法实现的查询开始,该查询使用了 EVAL 命令:
scss
`
1. FROM cooking_blog
2. | EVAL summary = TO_TEXT(CONCAT(title, description))
3. | WHERE MATCH(summary, "pancakes")
4. | KEEP title, author
`AI写代码
在这个示例中,summary 没有映射(mapping),也没有分析器(analyzer)配置。它也没有关联任何倒排索引(inverted index)。它仅在当前查询执行期间存在,但现在你依然可以对它执行全文搜索。这一能力由两项新增功能共同实现。
首先,[MATCH](https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/search-functions/match "MATCH") 的第一个参数现在可以接受任意表达式,而不仅仅是已映射字段。这包括:
-
由
EVAL生成的列; -
内联使用的函数返回值;
-
直接从原始文档读取的未映射字段。
此外,所有 MATCH 通常支持的数据类型,在这种新用法中同样受支持。
第二项新增功能是 [TO_TEXT](https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/type-conversion-functions/to_text "TO_TEXT") 函数,这是 ES|QL 第一个输出 text 类型的转换函数。
此前,text 类型的列只能来自已建立索引的映射字段,而所有由 ES|QL 表达式生成的字符串都会被视为 keyword 类型,而不是 text 类型。
这种区别非常重要,因为 MATCH 对两者的处理方式不同:
-
text类型会进行文本分析(analysis); -
keyword类型则进行精确匹配,这与针对已建立索引的keyword字段执行MATCH查询时最终重写为term查询的行为一致。
TO_TEXT(x) 就是在告诉 ES|QL:"请把这个字符串当作全文文本来处理。"
该功能作为 Elasticsearch 9.5 的技术预览发布,因此目前仍存在一些限制:
-
目前仅支持过滤(filtering)。针对表达式执行的
MATCH查询暂时不会参与相关性评分(relevance score),只有针对已建立索引字段的匹配才会影响评分。 -
在表达式上执行
MATCH时,目前还不支持fuzziness等查询选项。 -
运行时文本(runtime text)统一使用标准分析器(standard analyzer)进行分析,目前尚不能自定义配置。
Elastic 正在持续完善这些限制,并将在后续版本中逐步支持相关能力。
为什么在 ES|QL 中使用全文搜索,而不是 LIKE 或 RLIKE?
ES|QL 已经提供了两种无需索引即可搜索字符串的方式:LIKE(通配符模式匹配)和 RLIKE(正则表达式)。它们都可以作用于任意字符串表达式,因此很自然会问:MATCH 又带来了什么?
答案是文本分析(analysis)------ 一种更高级的搜索方式,它利用词干提取(stemming)、同义词(synonyms)以及停用词(stopwords)处理等技术来执行全文搜索。
LIKE 本质上只是简单的子字符串匹配,并不了解字符串中的单词。
例如,你想查找与狐狸(fox)相关的日志:
sql
`
1. FROM app_logs
2. | WHERE message LIKE "*fox*"
`AI写代码
这个查询会遗漏 "Fox spotted near the henhouse",因为大小写不同;却会匹配 "Outfoxed by the competition",尽管它并不是在讨论狐狸。
也就是说,它在两个方向上都会出现问题:
-
因大小写导致漏匹配(false negatives);
-
因子字符串出现在其他单词中导致误匹配(false positives)。
正则表达式可以解决大小写问题,但处理单词边界会迅速变得复杂。例如:
css
`
1. FROM app_logs
2. | WHERE message RLIKE "(.* )?[Ff][Oo][Xx]([ ,.:;].*)?"
`AI写代码
即便如此,这个表达式仍然不够完善。
它无法匹配句末跟着 ! 或 ? 的 "fox",也没有考虑制表符、引号或括号等情况。每修复一种情况,正则表达式都会变得更长,而下一位阅读该查询的人还得花时间弄清楚它到底在做什么。
MATCH 则彻底解决了这个问题,因为它会先对查询字符串和值都执行分析器(www.elastic.co/docs/refere...)处理,将文本分词并转换为小写词项,然后再按词项进行匹配:
sql
`
1. FROM app_logs
2. | WHERE MATCH(TO_TEXT(message), "fox")
`AI写代码
这个查询会匹配如下内容:
-
"The quick brown fox" -
"FOX spotted near the henhouse"
但不会匹配:
-
"Outfoxed by the competition" -
"FOXTROT protocol enabled"
无论单词周围有什么标点符号,都不会影响匹配结果。
当然,这同样适用于多词查询,例如:
less
`MATCH(TO_TEXT(message), "brown fox")` AI写代码
它的行为也完全符合你的直觉。
目前,Elastic 正在开发对 36 种专用语言分析器(language analyzers)的支持,使从未建立索引或映射的数据也能够针对自然语言执行全文搜索。
未建立索引和未映射数据的全文搜索应用场景
前面的示例搜索的是由已映射字段计算得到的值。而 ES|QL MATCH 应用于表达式的真正价值,在于它能够搜索那些过去根本无法搜索的数据。下面来看几个典型场景。
如何在不添加映射的情况下搜索 ES|QL 中的未映射字段
有时,你会有意不为某些字段创建映射,例如冗长的堆栈跟踪(stack trace)、原始请求负载(raw request payload),甚至调试信息(debug blob)。
为这些字段建立索引意味着每个文档都会额外占用磁盘和堆内存,而对于可能一个季度才查询一次的字段来说,这种成本并不值得。
过去,这个决定几乎是不可逆的,因为未映射字段完全无法参与查询。
在 Elasticsearch 9.5 中,你可以使用 SET unmapped_fields="load",让 ES|QL 直接从源文档(_source)加载未映射字段,并将其视为 keyword 类型。随后,再使用 TO_TEXT 包装该字段,就可以对它执行全文搜索:
sql
`
1. SET unmapped_fields="load";
2. FROM app_logs
3. | WHERE MATCH(TO_TEXT(stack_trace), "java.lang.NullPointerException")
4. | KEEP @timestamp, service.name, message
`AI写代码
这里,stack_trace 从未建立映射。每个值都会直接从原始文档中读取,并在查询时动态进行文本分析,然后逐行匹配。
这确实需要更多计算,因此速度永远无法与基于倒排索引(inverted index)的查询相比。
但现在,那个你没有建立索引的字段,不再意味着"无法搜索"。
你既可以在日常情况下保持映射精简,又能在真正需要时回答那些一个季度才会提出一次的问题。
无需重新建立索引,对 keyword 字段执行全文搜索
keyword 字段用途广泛。
它支持精确匹配、快速聚合以及排序,因此许多字段都会映射为 keyword 类型。
但映射是在数据写入时决定的,而需求却可能发生变化。
例如,product_name 最初被映射为 keyword,因为仪表板需要按产品名称进行聚合。但在积累了一年的产品数据之后,有人希望能够按产品名称进行全文搜索。
过去的解决方案是:
-
将字段映射改为
text(或增加一个 multi-field); -
对所有数据重新建立索引(reindex)。
这通常既耗时又昂贵,因此很多用户根本不愿意这么做。
现在,只需要调用一个函数:
sql
`
1. FROM products
2. | WHERE MATCH(TO_TEXT(product_name), "wireless noise cancelling headphones")
3. | KEEP product_name, brand, price
`AI写代码
TO_TEXT 会在查询时将 keyword 值动态转换为 text,因此 MATCH 会对它执行文本分析,而不是进行精确匹配。
这样,你无需修改映射,也无需重新建立索引,就可以对 keyword 字段执行全文搜索。
当然,如果全文搜索最终成为日常查询,把字段真正映射为 text 仍然是更好的长期方案。但 TO_TEXT 可以让你今天就得到答案,而无需任何额外的数据处理。
跨不同映射的索引搜索同一个字段
ES|QL 可以同时查询多个索引,而同一个字段在不同索引中并不要求具有相同的数据类型。
当某个字段在不同索引中具有不同类型时,ES|QL 会将其视为联合类型(union type),并通过类型转换函数来解决差异。
例如,message 字段在今年的索引模板中是 text 类型,而去年则是 keyword 类型:
scss
`
1. FROM logs-2025, logs-2026
2. | EVAL msg = TO_TEXT(message)
3. | WHERE MATCH(msg, "connection reset")
`AI写代码
无论数据来自 text 索引还是 keyword 索引,所有值都会在查询时进行文本分析。
旧索引中的 keyword 值也会像其他文本一样完成分词和小写化处理,因此 "connection reset" 同样能够匹配 "Connection RESET by peer",无论它位于哪个索引。
另一种有趣的情况是:某个字段只在一个索引中建立了映射,而在另一个索引中虽然存在,但没有建立映射。
例如:
sql
`
1. SET unmapped_fields="load";
2. FROM logs-2025, logs-2026
3. | WHERE MATCH(TO_TEXT(error_details), "timeout")
`AI写代码
这里有一个值得注意的细节。
如果 error_details 在 logs-2026 中已建立映射,而在 logs-2025 中没有映射,Elasticsearch 无法将该查询下推(push down)到 Lucene,因为对于未映射该字段的索引,Lucene 会直接返回零匹配结果,从而导致错误的查询结果。
因此,查询规划器(planner)会识别出该字段可能未建立映射,并改为对所有来源的文档逐行执行整个 MATCH 查询。
你无需知道哪些索引为该字段建立了映射,哪些没有。
查询会自动处理这些差异,并直接返回正确的结果。
ES|QL 如何在没有倒排索引的情况下于查询时分析文本
当 ES|QL 规划针对表达式执行 MATCH 查询时,它会首先将查询字符串分析(analyze)一次,将其转换为一组词项(terms)。
随后,每一行数据如何进行匹配,取决于表达式的数据类型:
| 表达式类型 | 处理方式 | 匹配行为 |
|---|---|---|
text(通过 TO_TEXT 转换) |
使用分析器将值分词并转换为小写词项 | 按词项进行匹配;只要任意词项与任意查询词项相等,该行即匹配(OR 语义) |
keyword、ip、date、数值类型 |
不进行文本分析;查询常量仅转换一次为对应的原生类型 | 对每一行执行精确匹配 |
这两种处理方式都会绕过 Lucene,而是直接逐行计算数据。
对于非 text 类型,这种处理方式与针对这些字段类型下推到 Lucene 执行 match 查询时的行为完全一致,因此无论查询是否命中索引,其语义都保持一致。
倒排索引(inverted index)会在数据写入(ingest)阶段完成分析工作,因此查询时无需访问不匹配的文档。
而运行时(runtime)的 MATCH 则是在查询阶段对每一行数据执行分析。
前者之所以速度快,是因为大部分工作已经在数据写入时完成;后者之所以更加灵活,是因为数据根本无需建立索引即可执行全文搜索。
ES|QL 全文搜索的下一步计划
本文介绍的功能只是一个更大计划的第一步,其目标是让 ES|QL 能够对任何数据执行搜索,而不仅仅是那些提前建立了索引的数据。
前文提到的限制正在积极改进中,未来的路线图还包括:
-
相关性评分(Scoring)
运行时匹配(runtime matches)将参与
_score的计算,因此即使数据从未建立索引,你也可以按相关性排序搜索结果。 -
表达式上的
MATCH_PHRASE该功能现已在 Elastic Cloud Serverless 中提供,并将于 Elastic Stack 9.6 中推出。
-
可配置分析器(Configurable analyzers)
为表达式上的
MATCH和MATCH_PHRASE提供分析器支持,从而在查询时使用语言分析器、词干提取(stemming)和同义词(synonyms)。 -
匹配选项(Match options)
为运行时匹配提供
fuzziness、operator等查询选项。 -
向量搜索(Vector search)
在查询过程中为每一行数据生成向量嵌入(embeddings),并对运行时
dense_vector表达式执行 k 最近邻(kNN)搜索,将语义搜索能力扩展到未建立索引的数据。
立即体验 ES|QL 表达式全文搜索
你现在就可以体验运行时搜索。
该功能现已在 Elastic Cloud Serverless 中提供------新的 ES|QL 功能通常都会首先在这里发布;同时,它也作为 Elasticsearch 9.5 的技术预览(technical preview)功能提供。
建议先阅读搜索函数(search functions)参考文档,并查看 ES|QL 限制(limitations)页面,了解当前功能边界。
之所以以技术预览形式发布,是因为我们希望获得你的反馈。如果你对从未建立索引的数据执行搜索,并且结果让你感到惊喜 ------ 无论是正面的还是负面的 ------ 我们都非常希望听到你的反馈。