ElasticSearch入门 第九篇:实现正则表达式查询的思路

简介:

这是ElasticSearch 2.4 版本系列的第九篇:

 

ElasticSearch 2.4版本支持Java正则表达式查询,但是,在对大段的文本(Text Block)进行挖掘之前,必须了解正则表达式查询的特殊之处。由于分析器会对文本字段进行分词,移除停用词,小写转换等操作,最终存储在倒转索引中的是小写的标记流(Token Stream),默认情况下,每一个标记是一个分词(Term),这无法满足正则表达式查询的一般要求,这就是说,正则表达式查询的是原始文本,需要注意的是,ElasticSearch引擎都是从原始文本的第一个字符开始执行正则表达式匹配。

在ElasticSearch 2.4版本中启用正则表达式查询之前,需要考虑两个问题:分词吗?大小写敏感吗?

一,分词还是不分词?

通常情况下,ElasticSearch引擎对文本字段进行分词,移除停用词,转换成小写,这是全文搜索的标准配置,在这种设置下,正则表达式只能匹配文本字段的单个分词,而无法对原始文本执行正则表达式查询,为了实现正则表达式查询,必须设置文本字段不被分词,也就是是设置该字段的index属性为not_analyzed。在实际的产品环境中,对一个字段同时执行正则表达式查询和全文搜索的情况是经常存在的,ElasticSearch 2.4版本提供的映射参数 fields 能够满足该需求。

fields 参数:多元字段(multi-fields),用不同的处理方式,把一个相同的字段编入索引,以实现不同的目的。多元字段使用相同的数据派生新的字段,例如,一个字段field被编入索引作为分析字段(analyzed field)以执行全文搜索,把该字段设置为多元字段,那么ElasticSearch引擎派生一个新的字段field .raw,该字段文本作为一个词被编入索引,只对该字段执行排序或聚合操作。

注:多元字段(multi-fields)不同于多值字段(multi-values field),字段的多值是ElasticSearch内在支持的特性,“开箱即用”,不需要做任何配置。每个字段都能存储多个数据值,这就是意味着,每个字段都是数组类型,只不过在字段中存储的数据,其数据类型都是相同的。

1,多元字段使用示例

在示例索引映射中,eventdescription是一个多元字段,其index属性是analyzed,表示该字段是分析字段,ElasticSearch引擎把该字段的文本分析成分词流,编入索引,以执行全文搜索,这就意味着,倒排索引中存储的不是该字段的原始文本,而是分割的单个分词;而eventdescription.raw是多元字段的派生字段,其index属性是not_analyzed,表示该派生字段不会被分词,整个文本字段整体作为一个分词被编入索引,这就意味着,倒排索引中存储的是派生字段原始的文本值。

复制代码
 "eventdescription":{  
    "type":"string",
"index":"analyzed", "fields":{ "raw":{ "type":"string",
"index":"not_analyzed" } } }
复制代码

2,对原始文本执行正则表达式查询

ElasticSearch引擎在处理分析字段(analyzed field)时,使用指定的分析器执行分析操作,包括分词,移除停用词,转换大小写等,如果一个字段不是分析字段,那么ElasticSearch引擎不会对其执行任何分析工作。

映射参数index:决定ElasticSearch引擎是否对文本字段执行分析操作,也就是说分析操作将分割文本,把分词编入索引,并使分词能够被搜索到

  • 当参数值为analyzed时,该字段是分析字段,ElasticSearch引擎对该字段执行分析操作,把文本分割成分词流,存储在倒排索引中,使其支持全文搜索;
  • 当参数值为not_analyzed时,该字段不会被分析,ElasticSearch引擎把原始文本作为单个分词存储在倒排索引中,不支持全文搜索,但是支持词条级别的搜索;也就是说,字段的原始文本不经过分析而存储在倒排索引中,使用原始文本编入索引,在搜索的过程中,查询条件必须全部匹配整个原始文本;
  • 当参数值为no时,该字段不会被存储到倒排索引中,也不会被搜索到;

 这也就意味着,要对原始文本执行正则表达式查询,必须设置index属性为not_analyzed,这也就意味着,保留文本的原始形式,例如大小写,空格等。

3,单个分词的最大长度

如果设置字段的index属性为not_analyzed,原始文本将作为单个分词,其最大长度跟UTF8 编码有关,默认的最大长度是32766Bytes,如果字段的文本超过该限制,那么ElasticSearch将跳过(Skip)该文档,并在Response中抛出异常消息:

operation[607]: index returned 400 _index: ebrite _type: events _id: 76860 _version: 0 error: Type: illegal_argument_exception Reason: "Document contains at least one immense term in field="event_raw" (whose UTF8 encoding is longer than the max length 32766), all of which were skipped. Please correct the analyzer to not produce such terms. The prefix of the first immense term is: '[112, 114,... 115]...', original message: bytes can be at most 32766 in length; got 35100" CausedBy:Type: max_bytes_length_exceeded_exception Reason: "bytes can be at most 32766 in length; got 35100"

可以在字段中设置ignore_above属性,该属性值指的是字符数量,而不是字节数量;由于一个UTF8字符最多占用3个字节,因此,可以设置

“ignore_above”:10000

这样,超过30000字节之后的字符将会被分析器忽略,单个分词(Term)的最大长度是30000Bytes。

The value for ignore_above is the character count, but Lucene counts bytes. If you use UTF-8 text with many non-ASCII characters, you may want to set the limit to 32766 / 3 = 10922 since UTF-8 characters may occupy at most 3 bytes.

二,大小写敏感?

正则表达式查询一般是区分大小写的,有时,我们可能会希望,正则表达式查询忽略大小写,在这种情况下,多元字段(fields)就无法满足需求了,多元字段不能执行文本大小写转换。为了解决这个问题,我们可以新建一个字段,在更新索引时,把原始文本导入到分析字段,把相同的数据转换成小写形式导入到另一个字段中,这样做以后,分析字段及其派生字段,用于支持全文搜索和大小写敏感的正则表达式查询,另外一个字段用于忽略大小写的正则表达式搜索。

复制代码
 "eventdescription":{  
    "type":"string",
"index":"analyzed", "fields":{ "raw":{ "type":"string",
"index":"not_analyzed" } } }, "eventdescription_lowcase":{ "type":"string",
"index":"not_analyzed" } }
复制代码

三,存储控制

为了实现正则表达式查询,上例为一个数据创建三个字段,eventdescription、eventdescription.raw和 eventdescription_lowcase,这三个字段都需要存储在倒排索引中,ElasticSearch引擎是否使用3倍的容量来存储这个数据?

默认情况下,一旦字段值被编入索引,该字段能够被搜索,但是,字段的原始值没有存储到倒排索引中,这就意味着,该字段能够被搜索,却不能从倒排索引中获取该字段的原始值。通常情况下,这样设计能够节省硬盘存储空间,不会对应用程序有什么影响,实际上,ElasticSearch引擎把该字段的原始值存储在_source元字段中,默认情况下,_source元字段是存储的。

1,存储属性(store)

字段的原始值是否被存储到倒排索引,是由映射参数store决定的,默认值是false,也就是,原始值不存储到倒排索引中。在特定的情况下,存储字段的原始值是有意义的。例如,为了存储一篇博客,文档必须有title,date和一个非常大的正文字段(content),如果仅仅是获取title和date,而不用获取正文字段,那么你可以存储title和date,并把content字段的store属性设置为false。

"title":{  
    "type":"string",
    "store":true,
    "index":"analyzed"
},
 "date":{  
    "type":"date",
    "store":true,
    "index":"not_analyzed"
},
 "content":{  
    "type":"string",
    "store":false,
    "index":"analyzed"
}
View Code

映射参数index和store的区别在于:

  • store用于获取(Retrieve)字段的原始值,不支持查询,可以使用投影参数fields,对stroe属性为true的字段进行过滤,只获取(Retrieve)特定的字段,减少网络负载;
  • index用于查询(Search)字段,当index为analyzed时,对字段的分词执行全文查询;当index为not_analyzed时,字段的原始值作为一个分词,只能对字段的原始文本执行词条查询;

2,源字段(_source)

当把原始的JSON文档传递到ElasticSearch引擎时,ElasticSearch引擎使用_source字段存储最原始的JSON文档。_source字段本身不会被索引,也不会被搜索,但是,该字段会存储在倒排索引中,用于返回查询的结果。

_source字段会导致索引存储空间的增加,因此,可以禁用_source字段,但是,在禁用_source字段之前,请认真阅读官方文档《_source field》。

复制代码
"mappings": {
    "tweet": {
      "_source": {
        "enabled": false
      }
    }
  }
复制代码

一般情况下,不要禁用_source字段,当需要考虑占用的Disk空间时,请有限考虑压缩存储,提高压缩等级。在配置文档 中,压缩选项是 index.codec,默认值是LZ4压缩,设置best_compression 能够提供更高的压缩率,代价是降低数据存储的性能。

四,一个字段包含所有文本?

为了实现正则表达式查询,上例为一个数据创建三个字段,eventdescription、eventdescription.raw和 eventdescription_lowcase,这三个字段都需要存储在倒排索引中,通常做法是,同时对这三个字段执行正则表达式查询,但是在ElasticSearch中,在编码上,可以更简单。元字段 _all是一个特殊的“包罗万象”(catch-all)的字段,把其他字段的值拼接成一个大的字符串,字段值之间使用空格分隔。ElasticSearch引擎先分析_all字段,然后编入索引,但是,默认情况下,不会存储字段的原始值,这就意味着,_all字段能够被搜索,但是不会返回原始值。_all 字段把所有字段的原始值,都视为字符类型,并把字段的原始值通过分隔符空格拼接在一起。

注意,添加到_all字段的是原始值,而不是字段分析之后的词条(term)。字段的原始值是否包含到_all字段,是由该字段的属性 include_in_all控制的,默认值是true。启用_all字段是需要付出代价的,_all字段会消耗额外的CPU时钟周期和更多的硬盘空间,如果不是必需,推荐把_all字段禁用掉。

"content": { 
     "type": "string""include_in_all": false
},

当把_all字段禁用之后,用户可以创建自定义的"_all"字段:新建一个数据类型为string的字段,并在需要拼接的字段中设置属性“copy_to”。

在ElasticSearch中,每个索引只有一个_all字段,通过字段的属性copy_to能够创建自定义的"_all"字段。例如,字段 first_name和 last_name能够通过分隔符空格被拼接到一起,作为full_name字段的值。

{
  "mappings": {
    "mytype": {
      "properties": {
        "first_name": {
          "type":    "string",
          "copy_to": "full_name" 
        },
        "last_name": {
          "type":    "string",
          "copy_to": "full_name" 
        },
        "full_name": {
          "type":    "string"
        }
      }
    }
  }
}
PUT myindex/mytype/1
{
  "first_name": "John",
  "last_name": "Smith"
}

GET myindex/_search
{
  "query": {
    "match": {
      "full_name": "John Smith"
    }
  }
}
View Code

默认情况下,_all字段不会存储_source字段的值,也不会存储原始值,这是因为_all字段是其他字段结合在一起组成的,存储_all字段会占用大量的硬盘存储空间,如果设置_all字段的属性store为true,那么ElasticSearch引擎将会存储_all字段的原始值,其原始值也能够被获取到。 

五,示例

综上所述,为了实现正则表达式查询,为了实现正则表达式的查询,有两个设计思路

示例1,原始文本和小写文本各使用一个字段

通过bool查询的should子句,对多个字段执行正则表达式查询,当字段较多,或者字段的文本特别大时,使用该方式,节省硬盘空间,但是要编写更多的查询代码:

复制代码
"eventdescription":{  
    "type":"string",
    "index":"analyzed",
    "fields":{  
        "raw":{  
            "type":"string",
            "index":"not_analyzed",
            "ignore_above":10000
        }
    }
},
"eventdescription_lowcase":{  
    "type":"string",
    "index":"not_analyzed",
    "ignore_above":10000
    }
}
复制代码

示例2,添加冗余字段

在冗余字段上执行正则表达式查询,当在多个字段上执行相同的正则表达式时,使用该方式,便于编程,但是要注意,拼接字段的大小不能超过限制(32766Bytes):

"eventdescription":{  
    "type":"string",
    "index":"analyzed",
    "copy_to":"eventdescription_regexp"
},
"eventdescription_lowcase":{  
    "type":"string",
    "index":"not_analyzed",
    "copy_to":"eventdescription_regexp"
},
"eventdescription_regexp":{
    "type":"string",
    "index":"not_analyzed"
}
View Code

 

 

参考文档:

Elasticsearch Reference [2.4] » Mapping » Meta-Fields

Elasticsearch Reference [2.4] » Mapping » Mapping parameters

Elasticsearch Reference [2.4] » Query DSL » Term level queries » Regexp Query

Elasticsearch: The Definitive Guide [2.x] » Search in Depth » Partial Matching » wildcard and regexp Queries






本文转自悦光阴博客园博客,原文链接:http://www.cnblogs.com/ljhdo/p/4911878.html,如需转载请自行联系原作者

相关实践学习
以电商场景为例搭建AI语义搜索应用
本实验旨在通过阿里云Elasticsearch结合阿里云搜索开发工作台AI模型服务,构建一个高效、精准的语义搜索系统,模拟电商场景,深入理解AI搜索技术原理并掌握其实现过程。
ElasticSearch 最新快速入门教程
本课程由千锋教育提供。全文搜索的需求非常大。而开源的解决办法Elasricsearch(Elastic)就是一个非常好的工具。目前是全文搜索引擎的首选。本系列教程由浅入深讲解了在CentOS7系统下如何搭建ElasticSearch,如何使用Kibana实现各种方式的搜索并详细分析了搜索的原理,最后讲解了在Java应用中如何集成ElasticSearch并实现搜索。  
目录
相关文章
|
存储 Java API
Elasticsearch 7.8.0从入门到精通
这篇文章详细介绍了Elasticsearch 7.8.0的安装、核心概念(如正排索引和倒排索引)、RESTful风格、各种索引和文档操作、条件查询、聚合查询以及在Spring Boot中整合Elasticsearch的步骤和示例。
810 1
Elasticsearch 7.8.0从入门到精通
|
存储 关系型数据库 MySQL
浅谈Elasticsearch的入门与实践
本文主要围绕ES核心特性:分布式存储特性和分析检索能力,介绍了概念、原理与实践案例,希望让读者快速理解ES的核心特性与应用场景。
1222 15
|
数据可视化 Java Windows
Elasticsearch入门-环境安装ES和Kibana以及ES-Head可视化插件和浏览器插件es-client
本文介绍了如何在Windows环境下安装Elasticsearch(ES)、Elasticsearch Head可视化插件和Kibana,以及如何配置ES的跨域问题,确保Kibana能够连接到ES集群,并提供了安装过程中可能遇到的问题及其解决方案。
Elasticsearch入门-环境安装ES和Kibana以及ES-Head可视化插件和浏览器插件es-client
|
数据采集 JSON 数据挖掘
Elasticsearch 的DSL查询,聚合查询与多维度数据统计
Elasticsearch的DSL查询与聚合查询提供了强大的数据检索和统计分析能力。通过合理构建DSL查询,用户可以高效地搜索数据,并使用聚合查询对数据进行多维度统计分析。在实际应用中,灵活运用这些工具不仅能提高查询效率,还能为数据分析提供深入洞察。理解并掌握这些技术,将显著提升在大数据场景中的分析和处理能力。
830 20
ELK 圣经:Elasticsearch、Logstash、Kibana 从入门到精通
ELK是一套强大的日志管理和分析工具,广泛应用于日志监控、故障排查、业务分析等场景。本文档将详细介绍ELK的各个组件及其配置方法,帮助读者从零开始掌握ELK的使用。
|
JSON 搜索推荐 数据挖掘
ElasticSearch的简单介绍与使用【入门篇】
这篇文章是Elasticsearch的入门介绍,涵盖了Elasticsearch的基本概念、特点、安装方法以及如何进行基本的数据操作,包括索引文档、查询、更新、删除和使用bulk API进行批量操作。
ElasticSearch的简单介绍与使用【入门篇】
|
存储 JSON 监控
大数据-167 ELK Elasticsearch 详细介绍 特点 分片 查询
大数据-167 ELK Elasticsearch 详细介绍 特点 分片 查询
995 4
|
JSON 自然语言处理 算法
ElasticSearch基础2——DSL查询文档,黑马旅游项目查询功能
DSL查询文档、RestClient查询文档、全文检索查询、精准查询、复合查询、地理坐标查询、分页、排序、高亮、黑马旅游案例
ElasticSearch基础2——DSL查询文档,黑马旅游项目查询功能
|
JSON 监控 Java
Elasticsearch 入门:搭建高性能搜索集群
【9月更文第2天】Elasticsearch 是一个分布式的、RESTful 风格的搜索和分析引擎,基于 Apache Lucene 构建。它能够处理大量的数据,提供快速的搜索响应。本教程将指导你如何从零开始搭建一个基本的 Elasticsearch 集群,并演示如何进行简单的索引和查询操作。
816 3
|
自然语言处理 Java 关系型数据库
ElasticSearch 实现分词全文检索 - 聚合查询 cardinality
ElasticSearch 实现分词全文检索 - 聚合查询 cardinality
551 1