【Elasticsearch】4. Elasticsearch高级检索

396 阅读10分钟

检索原理

20210812011737.png

每一个Index都被分成两个区,一个是索引区,一个是元数据区(文档区)。

当一个Document存储进入Index时,存放的位置是Index的文档区,ES会给该Document分配一个唯一的编号(_id),然后ES对该Document进行解析,分析每一个字段的类型和数据,再将每一个字段的数据,属于哪一个Document,出现了几次等信息记录下来存入索引区。

当对该Index进行检索时,ES首先会在索引区匹配关键词,如果匹配不到,则直接返回空信息。如果匹配到了,通过附加信息去文档区检索对应的Document进行返回,这个过程叫做Document的击中

检索方式

ES官方提供了两中检索方式:

  • QueryString:通过URL拼接参数进行检索
  • QueryDSL:通过DSL携带参数进行检索

DSL,英文全称 Domain Specified Language,中文译为特定领域语言。是基于传递 JSON 格式的信息作为请求体 (Request Body) 与ES交互进行检索的一种方式。官方更推荐使用DSL进行检索,这种方式更直观,更简洁。

QueryString的方式在简单检索应用中确实很方便,但是在复杂检索中会使得URL过于冗长和复杂,并且浏览器地址栏对于URL的字符数规定并不是无限的,所以QueryString在复杂检索中是存在局限性的,官方不推荐使用。

例如,对 postilhub/user 下的所有Document进行分页查询,当前条号为0,每页容量为5,并对查询出来的每一个Document按照age降序排序。

GET postilhub/user/_search?q=*&from=0&size=5&sort=age:desc

QueryDSL入门

1. 查询返回信息分析

每一次ES查询返回的信息中不仅仅只有我们需要的信息,还有很多附带的信息

20210811182839.png

  • took:ES响应的毫秒值
  • time_out:Kibana在请求ES时是否超时
  • _shards:集群相关
    • total:分片总数
    • successful:分片正常数
  • hits:本次查询击中的结果对象
    • total:本次查询结果条目数
    • max_score:最大得分(系统根据搜索条件动态计算,相关度排序)
    • hits:符合条件的每一个Document对象

2. 查询所有Document

GET /postilhub/user/_search
{
  "query": {
    "match_all": {}
  }
}

上面指令的含义为:在名为postilhub的Index下,名为user的Type下,查询所有Document。

注意:match_all 虽然是查询全部,但是默认只返回10个Document的信息,如果想要指定返回的Document数量,需要设置size:

GET /postilhub/user/_search
{
  "query": {
    "match_all": {}
  },
  "size": 5
}

3. 查询所有Document并排序


GET /postilhub/user/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "age": {
        "order": "desc"
      }
    },
    {
      "username": {
        "order": "asc"
      }
    }
  ]
}

上面指令的含义为:在名为postilhub的Index下,名为user的Type下,查询所有Document,先按照age进行降序排序,再按照username进行升序排序。

注意:

  • 支持多字段排序,如果两个Document的第一个字段相同,则会按照第二个字段进行排序,以此类推。
  • 排序的字段类型必须能排序,比如:keyword,integer,date,不能是text。
  • 当检索的文档进行了排序,则ES不会计算任何一个文档的得分。

4. 分页查询Document

GET /postilhub/user/_search
{
  "query": {
    "match_all": {}
  },
  "from": 0, 
  "size": 2
}

上面指令的含义为:在名为postilhub的Index下,名为user的Type下,从第0号Document开始,每页存放两个Document,进行分页查询。

5. 指定Document返回字段查询

如果不使用 _source 指定返回的字段,那么ES默认所有的Document查询都是全字段返回。

GET /postilhub/user/_search
{
  "query": {
    "match_all": {}
  },
  "_source": "username"
}

上面指令的含义为:在名为postilhub的Index下,名为user的Type下,查询全部Document,只返回username字段信息。

如果需要返回Document中的多个字段信息:

GET /postilhub/user/_search
{
  "query": {
    "match_all": {}
  },
  "_source": ["username", "age", "birth"]
}

6. 分词器简述

在学习关键词检索之前,需要了解分词器。

在ES的常见字段类型中,例如keyword,text,integer,boolean,date,ip等,只有text类型是分词的。

ES中使用的默认分词器是Standard Analyzer标准分词器,该分词器对于英文是单词分词,对于中文是单字分词

如果检索的目标字段是text类型,那么关键词如果是 多个汉字 / 多个单词,即使被字段数据包含,也是检索不到的;只有关键词为 单个汉字 / 单个单词,并且被字段数据包含才会被检索到。

如果检索的目标字段不是text类型,则该字段不会被分词,例如keyword类型。这时无论关键词是 多个汉字 / 多个单词 还是 单个汉字 / 单个单词 必须完全匹配字段数据,才可以被检索出来。

例如:当前有一个Mapping里设置了一个keyword类型的字段username,一个text类型的字段introduction。其中一个Document中的username为"张三",introduction为"我是一名学生"。

  • 第一次检索,检索对象为username字段,提供的关键词为"张",未检索出结果。

  • 第二次检索,检索对象为username字段,提供的关键词为"三",未检索出结果。

  • 第三次检索,检索对象为username字段,提供的关键词为"张三",检索出该Document。

  • 第四次检索,检索对象为introduction字段,提供的关键词为"我是一名学生",未检索出结果。

  • 第五次检索,检索对象为introduction字段,提供的关键词为"学生",未检索出结果。

  • 第六次检索,检索对象为introduction字段,提供的关键词为"我",检索出该Document。

  • 第七次检索,检索对象为introduction字段,提供的关键词为"学",检索出该Document。

因为ES默认分词器对于中文是单字分词,所以给检索带来极大不变,之后需要对标准分词器进行替换。

7. 关键词查询Document

GET /postilhub/user/_search
{
  "query": {
    "term": {
      "username": {
        "value": "john"
      }
    }
  }
}

上面指令的含义为:在名为postilhub的Index下,名为user的Type下,进行关键词查询Document,查询目标字段为username,提供的关键词为"john"。

8. 范围查询Document

GET /postilhub/user/_search
{
  "query": {
    "range": {
      "age": {
        "gte": 10,
        "lte": 20
      }
    }
  }
}

上面指令的含义为:在名为postilhub的Index下,名为user的Type下,查询age字段大于等于10,小于等于20的所有Document。

注意:还可以使用 gt,lt等接口。

9. 前缀查询Document

GET /postilhub/user/_search
{
  "query": {
    "prefix": {
      "address": {
        "value": "北"
      }
    }
  }
}

上面指令的含义为:在名为postilhub的Index下,名为user的Type下,查询address字段数据中有以“北”作为前缀的所有Document。(例如:北京,北平,北方大区都会被检索到)

10. 通配符查询Document

  • ?:只能匹配1个任意字符。
  • *:匹配0到任意多个字符。
GET /postilhub/user/_search
{
  "query": {
    "wildcard": {
      "address": {
        "value": "北?"
      }
    }
  }
}

上面指令的含义为:在名为postilhub的Index下,名为user的Type下,查询address字段数据必须是以“北”开头,字符总长度为2的所有Document。(例如:北京,北平,会被检索到;北方大区不会被检索到)

11. 多id查询Document

该 id 指的是document在创建时指定的 _id 的值。

GET /postilhub/user/_search
{
  "query": {
    "ids": {
      "values": ["1", "2", "3"]
    }
  }
}

上面指令的含义为:在名为postilhub的Index下,名为user的Type下,查询 _id 为1,2,3的Document。

12. 模糊查询Document

模糊距离范围:0-2

比方说,原数据是"elasticsearch"

  • 关键字为 "elasticsearch",与原数据0处不同,模糊距离为0,可以模糊匹配。

  • 关键字为 "alasticsearch",与原数据1处不同,模糊距离为1,可以模糊匹配。

  • 关键字为 "alesticsearch",与原数据2处不同,模糊距离为2,可以模糊匹配。

  • 关键字为 "alestixsearch",与原数据3处不同,模糊距离为3,不可以模糊匹配。

如果关键词长度在2以内,最大模糊距离为0。(关键词和原数据必须完全匹配)

如果关键词长度在3-5之间,最大模糊距离为1。(关键词最多和原数据有1处不同)

如果关键词长度大于5,最大模糊距离为2。(如上示例)

GET /postilhub/user/_search
{
  "query": {
    "fuzzy": {
      "content": "alasticsearch"
    }
  }
}

上面指令的含义为:在名为postilhub的Index下,名为user的Type下,查询content字段数据能模糊匹配"alasticsearch"的Document。

注意:在以英文作为关键词检索的时候,关键词中不能包含大写字母,因为ES会将全部英文内容转小写。

content类型为text,以下同理。

13. 布尔查询Document

布尔查询可以组合上述所有查询,从而完成更复杂的查询。

  • must:相当于 && ,表示与,其中的查询条件必须同时满足。
  • should:相当于 ||,表示或,其中的查询条件满足一个就行。
  • must_not:相当于 !,表示非,其中的查询条件必须同时不满足。
GET /postilhub/user/_search
{
  "query": {
    "bool": {
      "must_not": [
        {
          "term": {
            "age": {
              "value": 21
            }
          }
        },
        {
          "range": {
            "age": {
              "gte": 10,
              "lte": 20
            }
          }
        }
      ]
    }
  }
}

上面指令的含义为:在名为postilhub的Index下,名为user的Type下,查询age字段不等于21,且大于等于10,且小于等于20的Document。

14. 多字段查询Document

如果搜索目标字段分词,那么ES会先将query中的关键词分词,然后再分别搜索。

如果搜索目标字段不分词,那么ES就会拿query中的关键词整体去搜索。

GET /postilhub/user/_search
{
  "query": {
    "multi_match": {
      "query": "北京",
      "fields": ["intro", "content"]
    }
  }
}

上面指令的含义为:在名为postilhub的Index下,名为user的Type下,查询intro字段数据中是"北京"的Document和content字段数据中包含"北"或"京"的Document。

官方建议在fields中设置的字段都为分词字段,这样搜索范围更广。

intro类型为keyword,以下同理。

15. 多字段分词查询Document

该种查询方式无论目标字段是分词还是不分词,都会强行将query中的关键词分词,然后在分别搜索。


GET /postilhub/user/_search
{
  "query": {
    "query_string": {
      "query": "北京",
      "fields": ["intro", "content"]
    }
  }
}

上面指令的含义为:在名为postilhub的Index下,名为user的Type下,查询intro字段数据中是"北"或"京"的Document和content字段数据中包含"北"或"京"的Document。

还可以指定分词器,指定分词规则(下文设置为IK分词器的ik_max_word分词模式):

GET /postilhub/user/_search
{
  "query": {
    "query_string": {
      "query": "北京",
      "fields": ["intro", "content"],
      "analyzer": "ik_max_word"
    }
  }
}

注意:设置的分词器或者分词模式必须和存储时设置的分词器或者分词模式相同,否则检索会出错。

16. 查询结果高亮

图片中红色的Java就是搜索关键词在结果中的高亮显示。它不属于查询范畴,属于对查询结果的二次渲染。

20210812021639.png

高亮的实现原理就是在关键字文本上加标签。但是ES并没有修改原数据,而是拷贝一份原数据单独做修改封装成highlight对象和原document数据一起返回。所以如果要使用带有关键字高亮的文本,我们需要从highlight对象中取出修改后的数据,替换原数据。

GET /postilhub/user/_search
{
  "query": {
    "term": {
      "content": {
        "value": "java"
      }
    }
  },
  "highlight": {
    "pre_tags": ["<span style='color:red'>"],
    "post_tags": ["</span>"],
    "fields": {
      "content": {}
    }
  }
}

上面指令的含义为:在名为postilhub的Index下,名为user的Type下,查询content字段数据中有"java"的Document,并将返回的原Document数据的content字段的数据中的所有的"java"标红,并封装在highlight对象中一起返回。

pre_tags和post_tags可以定制高亮的形态。

GET /postilhub/user/_search
{
  "query": {
    "term": {
      "content": {
        "value": "java"
      }
    }
  },
  "highlight": {
    "pre_tags": ["<span style='color:red'>"],
    "post_tags": ["</span>"],
    "fields": {
      "*": {}
    }
  }
}

上面指令的含义为:在名为postilhub的Index下,名为user的Type下,查询content字段数据中有"java"的Document,并将返回的原Document数据的所有字段的数据中的"java"标红,并封装在highlight对象中一起返回。