• 【Elasticsearch教程18】Mapping字段类型之text 以及term、match和analyzer


    一、text场景

    text类型是适合全文搜索的场景,ES会将文本分析成多个词并索引。

    • text类型适合存放易阅读的(human-readable)非结构化文本,比如邮件内容、评论、商品介绍等
    • 对于阅读性差的文本,如系统日志、Http请求体这些machine-generated数据,可以用wildcard类型
    • text类型不适合排序聚合(虽然可以实现,但是不建议)
    • 如果要进行排序聚合,建议使用keyword类型
    • 所以可以给text类型添加keyword子类型token_count子类型,各司其职
    PUT pigg_test_text
    {
      "mappings": {
        "properties": {
          "name": { 			# 姓名 name
            "type": "text",
            "fields": {
              "keyword": {		# 子字段 name.keyword
                "type": "keyword",
                "ignore_above" : 256
              },
              "length": { 		# 子字段 name.length
                "type": "token_count",
                "analyzer": "standard"
              }
            }
          },
          "tag": {				# 标签 tag
            "type": "keyword"
          },
          "word": {				# 台词 word
            "type": "text"
          }
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    二、term查询

    • term判断某个字段是否包含某个确定的值。一般用在keywordintegerdatetoken_countip等类型上
    • 避免在text类型上用term,在text上应该用matchmatch_phrase来全文搜索

    先插入两条《王者荣耀》里英雄的数据:

    PUT pigg_test_text/_doc/1
    {
      "name": "亚瑟王",
      "tag": ["对抗路", "打野"],
      "word": [
          "王者背负,王者审判,王者不可阻挡"
        ]
    }
    
    PUT pigg_test_text/_doc/2
    {
      "name": "关羽",
      "tag": ["对抗路", "辅助"],
      "word": [
          "把眼光从二爷的绿帽子上移开",
          "聪明的人就应该与我的大刀保持安全距离"
        ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    (1)查询name亚瑟王的人

    • name.keyword上用term查询,返回id=1的文档
    • 注意:term不是等于的意思,是包含的意思
    GET pigg_test_text/_search
    {
      "query": {
        "term": {
          "name.keyword": "亚瑟王"
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    (2)查询可走对抗路的人

    • tag上用term查询,返回id=1和2的文档,因为他们的tag包含对抗路
    GET pigg_test_text/_search
    {
      "query": {
        "term": {
          "tag": "对抗路" 
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    (3)查询是打野辅助的人

    • tag上用terms查询(注意多了个s),返回id=1和2的文档
    • 因为terms查询只要包含数组中任意一个就算匹配
    
    GET pigg_test_text/_search
    {
      "query": {
        "terms": {
          "tag": ["打野", "辅助"]
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    (4)查询name是3个字的人

    • token_count类型的name.length上做精确匹配,返回亚瑟王的文档
    GET pigg_test_text/_search
    {
      "query": {
        "term": {
          "name.length": 3
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    三、match查询

    虽然上面亚瑟王关羽这2个文档内容是中文,而且我也没有配置ik中文分词器,但是这不影响我们的学习,我们只要知道中文被默认的standard analyzer切分成独立的汉字就行了。

    match全文搜索鼓励王,返回亚瑟王文档,因为匹配中了这个汉字。

    GET pigg_test_text/_search
    {
      "query": {
        "match": {
          "name": "鼓励王"
        }
      }  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    对上面这条语句不是很理解的话,我从亚瑟王如何存储?和鼓励王如何搜索?这2个角度的问题来解释。

    1. 亚瑟王如何存储?

    • _termvectors可以帮助我们查看文本是如何分割成词条的
    • 国内博客对这里的term的翻译有多种:词条、词根、词项等等,我们不必纠结,知道意思就行
    查询id=1的文档的name字段的词条向量
    
    GET pigg_test_text/_doc/1/_termvectors?fields=name
    
    • 1
    • 2
    • 3

    返回这3个词,说明在倒排索引里有类似如下的关系:

    文档ID
    1
    1
    1

    2. 鼓励王如何搜索?

    第1种方法:使用_analyze分析standard这个分析器是如何分割搜索关键字的。这里得指定name字段上search-timeanalyzer,即search_analyzer

    GET /_analyze
    {
      "analyzer" : "standard",
      "text" : "鼓励王"
    }
    
    返回"鼓""励""王"3个token
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    第2种方法:使用_validate验证语句是否合法,其参数explain(默认为true)会解释语句的执行计划

    GET pigg_test_text/_validate/query?explain
    {
      "query": {
        "match": {
          "name": "鼓励王"
        }
      }  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    返回结果如下,name:鼓 name:励 name:王说明是把鼓励王拆成3个汉字分别到name字段上做匹配的。

    "valid" : true,
    "explanations" : [
      {
        "index" : "pigg_test_text",
        "valid" : true,
        "explanation" : "name:鼓 name:励 name:王"
      }
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    第3种方法:使用_explain查询鼓励王是如何匹配到id=1的文档的?这种方式的前提是我们已经知道关键词匹配到哪个文档了,想知道匹配的原因。

    解释`鼓励王`为何在name字段上匹配到id=1的文档
    GET /pigg_test_text/_explain/1
    {
      "query" : {
        "match" : { "name" : "鼓励王" }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    返回内容比较长,也比较复杂,因为涉及到打分机制,这里就贴一个重点:

    "description" : "weight(name:王 in 0) [PerFieldSimilarity], result of:",
    
    • 1

    说明是这个字让鼓励王name字段上匹配到id=1的文档的。

    3. match的参数

    match还有2个比较重要的参:operatorminimum_should_match,他们可以控制match查询的行为。

    3.1 operator

    上面match查询鼓励王的语句,其实可以写成如下:

    GET pigg_test_text/_search
    {
      "query": {
        "match": {
            "name": {
              "query": "鼓励王",
              "operator": "or"
            }
        }
      }  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 这个operater的默认值就是or,就是只要匹配到任意一个词,就算匹配成功。
    • 如果要"鼓励王"这三个词全部匹配,可以设置 "operator": "and"
    GET pigg_test_text/_validate/query?explain=true
    {
      "query": {
        "match": {
            "name": {
              "query": "鼓励王",
              "operator": "and"
            }
        }
      }  
    }
    
    返回如下:说明这3个字都得匹配
    "explanations" : [
      {
        "index" : "pigg_test_text",
        "valid" : true,
        "explanation" : "+name:鼓 +name:励 +name:王"
      }
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3.1 minimum_should_match

    • minimum_should_match可以设置匹配的最小词数,不要与operator一起使用,意思会冲突。
    • 它可以赋值正数、负数、百分比等,但是我们常用的是设置一个正数,即指定最小匹配的词数。
    指定要至少匹配成功2个字,才算文档匹配成功
    GET pigg_test_text/_search
    {
      "query": {
        "match": {
            "name": {
              "query": "鼓励王",
              "minimum_should_match": "2"
            }
        }
      }  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    4. 匹配短语 match_phrase

    match_phrase短语查询,这个会将“绿帽子”作为一个短语整体去匹配,而不会拆成3个字

    该语句返回关羽这个文档,因为他的台词包含"绿帽子"
    GET pigg_test_text/_search
    {
      "query": {
        "match_phrase": {
          "word": "绿帽子"
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    查询语句的执行计划:

    GET pigg_test_text/_validate/query?explain
    {
      "query": {
        "match_phrase": {
          "word": "绿帽子"
        }
      }
    }
    
    返回如下:
    "explanations" : [
      {
        "index" : "pigg_test_text",
        "valid" : true,
        "explanation" : "word:\"绿 帽 子\""
      }
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    四、分析器 analyzer

    text类型最重要的参数就是analyzer(分析器),它决定在index-time(创建或更新文档)和search-time(搜索文档)如何对文本进行分词。

    • analyzer:只配置了analyzer时,在index-timesearch-time时,都使用analyzer配置的分析器
    • search_analyzer:配置了search_analyzer时,在search-time时,使用search_analyzer配置的分析器

    standard 分析器是text类型的默认分析器,它按照词边界对文本进行分割(如英语按照空格,中文切成独立汉字)。它删除了大多数标点符号和停止词并进行小写。standard 分析器对英语这样的西方语音是大多适用的。

    分析器(analyzer)的配置包含3个重要部分,依次是character filterstokenizertoken filters
    每当一个文档被 ingest 节点纳入,它需要经历如下的步骤,才能最终把文档写入到ES数据库中
    在这里插入图片描述

    英文中文analyzer配置项个数说明
    character filters字符过滤器char_filter0~n个剥离html标签,转换特殊字符如&and
    tokenizer分词器tokenizer1个把文本按一定规则分割成一个个词token,比如常见的standardwhitespace
    token filters词过滤器filter0~n个对上一步产生的token进行规范化。
    比如转小写、删除或新增术语、同义词转换等。

    举例:tokenizersimple_pattern_split,配置按照下划线_来分割文本。

    PUT my-index-000001
    {
      "settings": {
        "analysis": {
          "analyzer": {
            "my_analyzer": {
              "tokenizer": "my_tokenizer"
            }
          },
          "tokenizer": {
            "my_tokenizer": {
              "type": "simple_pattern_split",
              "pattern": "_"
            }
          }
        }
      }
    }
    
    POST my-index-000001/_analyze
    {
      "analyzer": "my_analyzer",
      "text": "亚瑟王__鼓励王_可丽王"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    按照下划线_切分后的词就是["亚瑟王", "鼓励王", "可丽王"]

    • 其实我们工作中也不用配置很多,这里了解下就行,不用深究每一个选项
    • 因为ES提供了很多开箱即用的内置analyzer,我们可根据场景来选择
    • 对于中文的分词,出名的有IK分词器拼音分词器,可以参考我之前写的

    IK分词器 ik_max_word、ik_smart
    Pinyin拼音分词器
    ik中文分词器+pinyin拼音分词器+同义词

  • 相关阅读:
    前端教程-TypeScript
    04 # 手写 apply
    Git相关操作
    白话剖析CSDN勤学会
    JavaScript作用域
    使用RocketMQ 实现基于标签过滤的消息队列生产和消费
    SpringCloud-Eureka-介绍+非集群式搭建
    G1回收器的适用场景
    【课程作业】西瓜书 机器学习课后习题 : 第七章
    @SpringBootApplication注解SpringBoot深度理解(课时八)
  • 原文地址:https://blog.csdn.net/winterking3/article/details/126663540