在系列的第一篇文章中我们介绍了ElasticSearch的基本概念和操作,本文将继续介绍ElasticSearch查询和索引功能。

目录:

查询

结构化搜索将查询条件以json的形式包含在http请求的body中,通常情况下搜索请求应该使用GET方法但不是所有的客户端和服务端都支持GET请求包含body。 因此,ElasticSearch支持使用GET或POST进行搜索。

本节示例均使用默认analysismapping配置, 此状态下ElasticSearch与普通的文档数据库几乎无异。我们将在下一节中介绍如何进行模糊查询,使它成为真正的搜索引擎。

精确查询

精确查询通常起到过滤的作用,因为不使用分析器解析通常非常迅速。

查询可以使用from和size参数对结果进行分页:

{
"from": 0,
"size": 10,
"query": {
"term": {
"nickname": "easy"
}
}
}

term查询

term查询用于进行精确匹配:

POST /blog/user/_search
{
"query": {
"term": {
"nickname": "easy"
}
}
} response:
{
"took": 23,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 0.45532417,
"hits": [
{
"_index": "blog",
"_type": "user",
"_id": "1",
"_score": 0.45532417,
"_source": {
"nickname": "easy",
"username": "user1",
"status": "normal"
}
},
{
"_index": "blog",
"_type": "user",
"_id": "2",
"_score": 0.43648314,
"_source": {
"nickname": "easy",
"username": "user2"
"status": "normal"
}
}
]
}
}

在响应的hits.hits字段中我们可以看到匹配的文档,文档_score字段是采用TF-IDF算法得出匹配程度得分,结果集中的文档按照得分降序排列。

上述查询可以用sql表示为:

SELECT * FROM user
WHERE nickname = "easy";

terms查询

terms查询可以视为多个term查询的组合:

POST /blog/user/_search
{
"query": {
"terms": {
"nickname": ["easy", "ease"]
}
}
} response:
{
"took": 18,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 1.0970675,
"hits": [
{
"_index": "blog",
"_type": "user",
"_id": "2",
"_score": 1.5779335,
"_source": {
"nickname": "ease",
"status": "normal"
}
},
{
"_index": "blog",
"_type": "user",
"_id": "4",
"_score": 1.0970675,
"_source": {
"nickname": "easy",
"content": "simple",
"status": "normal"
}
}
]
}
}

range查询

range查询用于数值等类型进行过滤,比如示例中过滤粉丝数大于等于2的用户:

GET /user/naive/_search
{
"query": {
"range": {
"followerCount": {
"gte": 2
},
"registerTime": {
"gt": "2017-01-01 00:00:00",
"lt": "2018-01-01 00:00:00"
}
}
}
}

示例中的range查询可以用SQL描述为:

SELECT * FROM user_naive
WHERE followerCount >= 2
AND unix_timestamp(registerTime) > unix_timestamp('2017-01-01 00:00:00')
AND unix_timestamp(registerTime) < unix_timestamp('2018-01-01 00:00:00');

可用的范围表达式有:

  • lt: 小于(less than)
  • lte: 小于等于(less than / equals)
  • gt: 大于(greater than)
  • gte: 大于等于(greater than / equals)

可以进行范围查询的字段:

  • 数值
  • 日期
  • 字符串: 按照词典顺序排列

全文查询

用户输入字符串通常无法进行精确查询,全文查询可以使用分析器解析查询字符串然后根据相关度筛选查询结果。

match查询

match查询全文字段(类型为analyzed)时,会使用分析器将查询字符串解析成词条列表,然后进行匹配。

查询字符串"easy simple"会被standard分析器解析为[easy, simple], 这个查询等价于:

{
"terms": {
"nickname": ["easy", "simple"]
}
}
POST /blog/user/_search
{
"query": {
"match": {
"nickname": "easy simple"
}
}
} response:
{
"took": 19,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 1.1382749,
"hits": [
{
"_index": "blog",
"_type": "user",
"_id": "3",
"_score": 1.1382749,
"_source": {
"nickname": "simple",
"status": "normal"
}
},
{
"_index": "blog",
"_type": "user",
"_id": "1",
"_score": 1.049597,
"_source": {
"nickname": "easy",
"status": "normal"
}
}
]
}
}

若以eas sim为关键词进行term查询不会匹配到任何文档。

multi_match查询

multi_match用于对多个字段进行同一个查询:

{
"query": {
"multi_match": {
"analyzer": "keyword",
"query": "easy",
"fields": [
"nickname.ngram",
"username.ngram"
]
}
}
}

上述查询等同于对nickname.ngramusername.ngram两个字段进行match查询,对结果进行综合排序。

排序方式有:

  • best_fields
  • most_fields
  • cross_fields

script查询

script查询可以使用脚本描述查询条件,进行复杂的查询。

{
"query": {
"script": {
"script": {
"source": "doc['count'].value > params.limit",
"lang": "painless",
"params": {
"limit": 1
}
}
}
}

我们通常使用ElasticSearch提供的painless脚本语言编写脚本,也可以使用Groovy等脚本语言。

组合查询

组合查询用于将多个查询组合起来,以表达更复杂的查询条件或排序规则。

组合查询都是可以自由嵌套的,如我们可以bool查询中使用另一个bool查询或dis_max作为must子查询。

bool查询

Bool查询用于组合多个条件查询相关度最高的文档, 下面展示了一个Bool查询请求:

POST /user/naive/_search
{
"query": {
"bool": {
"must": {
"match": {
"nickname": "easy"
}
},
"must_not": {
"match": {
"nickname": "hard"
}
},
"should": {
"match": {
"nickname": "simple"
}
},
"filter": [
{
"term": {
"status": "normal"
}
}
]
}
}
} response:
{
"took": 20,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 1.3847495,
"hits": [
{
"_index": "blog",
"_type": "user",
"_id": "4",
"_score": 1.3847495,
"_source": {
"nickname": "easy",
"content": "simple",
"status": "normal"
}
},
{
"_index": "blog",
"_type": "user",
"_id": "5",
"_score": 0.45532417,
"_source": {
"nickname": "easy",
"content": "bitter",
"status": "normal"
}
}
]
}
}

上述bool查询的目标为:

  • must条件必须满足, 即nickname字段必须与词条easy匹配,字段的匹配程度会影响得分
  • must_not条件必须不满足, 即nickname字段不能与词条hard匹配
  • should条件不做要求, 但满足should条件的文档会获得更高的相关度评分_score。 当should查询中包含多个字段时, 会将各字段得分的和作为总分。所以查询到两个nickname与easy匹配的文档,但是contentsimple的字段获得了更高的评分。
  • filter条件必须满足,但是匹配程度不会影响得分。

filter子查询一般使用精确查询,因为过滤器不进行相关度计算且使用过滤器缓存, 其执行速度非常快。

dis_max查询

上文已经提到bool查询的should查询会将各字段得分之和作为总分,然而在实际应用中通常一个字段高度匹配的文档可能比拥有多个字段低匹配更符合用户的期望。

dismax查询同样用于组合多个查询,但是按照匹配程度最高的字段确定总分:

{
"query": {
"dis_max": {
"queries": [
{
"match": {
"nickname": "easy"
}
},
{
"match": {
"content": "easy"
}
}
]
}
}
}

function_score 查询

function_score可以更加自由地控制评分过程:

{
"query": {
"function_score": {
"filter": {
"term": { "uid": 1 }
},
"functions": [
{
"filter": { "term": { "features": "a" }},
"weight": 1
},
{
"filter": { "term": { "features": "b" }},
"weight": 2
}
],
"score_mode": "sum",
}
}
}

一致性随机

function_score可以对结果进行随机排序:

{
"query": {
"random_score": {
"seed": 2
},
"bost_mode": "replace",
"query": {
"dis_max": {
"queries": [
{
"multi_match": {
"analyzer": "keyword",
"query": "finley",
"boost": 1.2,
"fields": [
"username.ngram"
]
}
},
{
"multi_match": {
"analyzer": "keyword",
"query": "finley",
"boost": 1,
"fields": [
"nickname.ngram"
]
}
}
]
}
}
}
}

只要随机种子random_score.seed不变结果的排序就不会变化,这种方式被称为一致性随机评分。

一致性随机评分机制可以为每个用户生成一个独有的排序,并支持翻页浏览。

排序

ElasticSearch的搜索结果默认按照_score进行降序排列,_score是根据TF-IDF算法得出文档与查询调价匹配度。

在一些情况下我们希望自定义排序方式, 比如按创建时间排列。

POST /blog/user/_search
{
"query" : {
"term" : {
"uid" : 1
}
},
"sort": {
"date": {
"order": "desc"
}
}
}

script 排序

{
"query" : {
"term" : {
"uid" : 1
}
},
"sort": {
"script_score": {
"source": "_score / (params.current - doc['create_at'].value)"
"params": {
"current": 1534606168
}
}
}
}

索引管理

ElasticSearch中的Index是最高级的逻辑的结构, 类似于MySQL中的数据库(schema),可以配置独立的搜索策略和存储策略。

ElasticSearch通过倒排索引进行搜索,所谓倒排索引是指对文档进行分析提取关键词,然后建立关键词到文档的索引,当我们搜索关键词时就可以找到它所关联的文档。

我们可以通过在Index中配置分析器和映射来设置倒排索引的策略。

分析器是通用的从文档提取关键词的方法,即将文档中某个字段映射为关键字的方法。例如:过滤停用词,分词,添加同义词等。

映射则是具体指定文档中的某个字段应该使用什么分析器来提取关键词。

分析器

这个拆分的过程即是分析的过程, 分析执行的操作包括不限于: 字符过滤, 分词, 停用词过滤, 添加同义词.

ES提供了很多内置分析器:

  • standard: 默认分析器, 根据Unicode定义删除标点并将词条小写
  • simple: 根据空格分词并将词条小写
  • whitespace: 根据空格分词但不将词条小写
  • english: 过滤英文停用词并将词条还原为词根, 详情参考官方文档.
  • ngram: 滑动窗口分析器,取文本中所有子串作为关键词。 比如对easy进行处理可以得到关键词:e, a, s, y, ea, as, sy, eas, asy, easy
  • edge-ngram: 边缘固定滑动窗口分析器,取文本所有从头开始的子串作为关键词。 比如对easy进行处理可以得到关键词:e, ea, eas, easy。常用于联想搜索,根据用户输入前几个字符进行搜索。

此外, 也可以通过配置字符过滤器(char_filter), 词过滤器(filter), 分词器(tokenizer)的方式来自定义分析器。

这里展示基于ngram的分析器定义:

PUT /blog
{
"settings": {
"analysis": {
"filter": {
"grams_filter": {
"type": "ngram",
"min_gram": 1,
"max_gram": 5
}
},
"analyzer": {
"gram_analyzer": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"lowercase",
"grams_filter"
]
}
}
}
}
},
mappings: {...}
}

自定义分析器的更多信息可以查阅官方文档:

类型和映射

映射则是具体指定文档中的某个字段应该使用什么分析器来提取关键词:

PUT /blog
{
"settings": { ... },
"mappings": {
"user": {
"properties": {
"nickname": {
"type": "string",
"analyzer": "gram_analyzer",
"fields": {
"keyword:": {
"type": "keyword"
}
}
},
"status": {
"type": "text",
"fields": {
"keyword:": {
"type": "keyword"
}
}
}
}
}
}
}

上述JSON中user项定义了一个根对象, 它通常与文档对应。根对象下可以包含下列几个字段:

文档中每一个字段都有3个配置项:

  • type: 指定字段类型, 如:text, long, doubledate.
  • index: 指定字段索引的类型:
    • no: 不可被搜索
    • not_analyzed: 必须精确匹配
    • analyzed: 使用分析器建立倒排索引
  • analyzer: 该字段使用的默认分析器
  • fields: 字段的属性, 可以配置独立的type, index和analyzer。用于将一个字段映射为不同类型适应不同用途。

管理索引

在分析器及映射两节中展示了创建索引所需的PUT请求片段,将类型和映射一节中PUT请求的settings字段, 用分析器一节中的settings字段替换即可得到完整创建索引请求。

发送DELETE请求可以删除索引:

  • DELETE /user: 删除user索引
  • DELET /user1,user2: 删除user1和user2两个suoyin
  • DELETE /user*: 根据通配符删除索引
  • DELET /_all, DELETE /*: 删除所有索引

GET /_cat/indices可以列出ElasticSearch上的所有索引。

GET /blog?pretty列出索引blog的所有信息。

中文搜索

若需要进行中文搜索我们需要使用中文分析器,它的核心是中文分词器(tokenizer)。

这里我们使用插件elastic-analysis-ik进行中文搜索。

PUT /post/Post
{
"settings": {
"analysis": {
"analyzer": {
"cn_smart": {
"type": "custom",
"tokenizer": "ik_smart",
"filter": [
{
"type": "length",
"min": "3"
}
]
},
"max_word": {
"type": "custom",
"tokenizer": "ik_max_word",
"filter": [
{
"type": "length",
"min": "3"
}
]
}
}
}
},
"mappings": {
"user": {
"properties": {
"content": {
"type": "text",
"analyzer": "cn_smart",
"fields": {
"max_word": {
"type": "text",
"analyzer": "max_word"
}
}
}
}
}
}
}

使用match进行查询:

{
"query" : {
"match" : {
"content" : "搜索"
}
}
}
{
"query" : {
"match" : {
"content.max_word" : "搜索"
}
}
}

ik_smart会进行标准的中文分词, ik_max_word则会试图穷尽所有分词

ElasticSearch快速指南的更多相关文章

  1. Elasticsearch 快速开始

    Elasticsearch 是一个分布式的 RESTful 风格的搜索和数据分析引擎. 查询 : Elasticsearch 允许执行和合并多种类型的搜索 - 结构化.非结构化.地理位置.度量指标 - ...

  2. 史上最全面的Elasticsearch使用指南

    Elasticsearch使用指南 Elasticsearch使用指南 前言 ES是什么 什么是全文检索 ES的应用场景 ES的存储结构 第一章:安装 1.下载 2.解压 3.配置 4.启动 5.查看 ...

  3. 初识Elastic search—附《Elasticsearch权威指南—官方guide的译文》

    本文作为Elastic search系列的开篇之作,简要介绍其简要历史.安装及基本概念和核心模块. 简史 Elastic search基于Lucene(信息检索引擎,ES里一个index—索引,一个索 ...

  4. [译] MongoDB Java异步驱动快速指南

    导读 mongodb-java-driver是mongodb的Java驱动项目. 本文是对MongoDB-java-driver官方文档 MongoDB Async Driver Quick Tour ...

  5. Elasticsearch 权威指南

    Elasticsearch 权威指南 http://fuxiaopang.gitbooks.io/learnelasticsearch/content/index.html

  6. 转:C++ Boost/tr1 Regex(正则表达式)快速指南

    C++ Boost/tr1 Regex(正则表达式)快速指南 正则表达式自Boost 1.18推出,目前已经成为C++11(tr1)的标准部分. 本文以Boost 1.39正则表达式为基础,应该广泛适 ...

  7. Elasticsearch 权威指南 NESTAPI地址

    Elasticsearch 权威指南:http://fuxiaopang.gitbooks.io/learnelasticsearch/content/index.html NEST:http://n ...

  8. (译)快速指南:用UIViewPropertyAnimator做动画

    翻译自:QUICK GUIDE: ANIMATIONS WITH UIVIEWPROPERTYANIMATOR 译者:Haley_Wong iOS 10 带来了一大票有意思的新特性,像 UIViewP ...

  9. JUnit5 快速指南

    JUnit5 快速指南 version: junit5 1. 安装 2. JUnit 注解 3. 编写单元测试 3.1. 基本的单元测试类和方法 3.2. 定制测试类和方法的显示名称 3.3. 断言( ...

随机推荐

  1. 理解JavaScript原型

    Javascript原型总会给人产生一些困惑,无论是经验丰富的专家,还是作者自己也时常表现出对这个概念某些有限的理解,我认为这样的困惑在我们一开始接触原型时就已经产生了,它们常常和new.constr ...

  2. Centos 7 防火墙firewalld命令

    今天自己在Hyper-v下搭建三台Linux服务器集群,用于学习ELKstack(即大数据日志解决技术栈Elasticsearch,Logstash,Kibana的简称),下载的Linux版本为cen ...

  3. node 使用koa2 异步读文件

    目的:在一个文件夹(image)中有很多文件夹和文件,排除掉文件,将所有文件夹找出来 知识点: async 函数与 await  .只有在async函数内部,才能使用await,await等的必须是p ...

  4. git和github新手安装使用教程(三步入门)

    git和github新手安装使用教程(三步入门) 对于新手来说,每次更换设备时,github的安装和配置都会耗费大量时间.主要原因是每次安装时都只关心了[怎么做],而忘记了记住[为什么].本文从操作的 ...

  5. JVM垃圾收集器整理

    概述 垃圾收集器是jvm实现内存回收的具体实现.本次分享要介绍的7种垃圾收集器的作用区域及其之间的关系如下图: 注: 如果2个垃圾收集器之间有连线,表示可以搭配使用 垃圾收集器并没有最好的,只有针对不 ...

  6. vscode 开发工具

    做开发两年了,而我记忆力不太好,所以写代码得靠强大的编辑器提示. 陆陆续续使用了如 notepad++.dreamweaver.sublime text.webstorm.phpstorm.Atom等 ...

  7. MariaDB扩展特性--虚拟列

    存在于表中的列,它们的值是根据确定的表达式或者是根据表中其他列的值自动计算的. 虚拟列有两种,分别对应了定义虚拟列的修饰关键词: 'VIRTUAL'修饰词含义为该虚拟列的值会在查询的时候计算生成. ' ...

  8. 【读书笔记】【深入理解ES6】#11-Promise与异步编程

    异步编程的背景知识 JavaScript 引擎是基于单线程(Single-threaded)实际循环的概念构建的,同一时刻只允许一个代码块在执行. 所以需要跟踪即将运行的代码,那些代码被放在一个任务队 ...

  9. git 本地代码到github

    一·什么是gitHub? 官网解释:gitHub是一个让无论处于何地的代码工作者能工作于同一个项目,同一个版本的平台.(GitHub is a code hosting platform for ve ...

  10. Redis随笔(二)redis desktop manager 安装并且连接redis服务器

    1.首先在win10下安装redis desktop manager 2.查看虚拟机防火墙状态,启动状态,则关闭掉 查看防火墙状态: systemctl status firewalld.servic ...