02 elasticsearch基本概念和接口介绍

Elastic Search 和 Kibana

转载来源: 张人大 https://zhuanlan.zhihu.com/p/316356535

Elastic Search 介绍和安装

Elasticsearch 是一个需要安装配置的软件。

ELK 技术栈说明:

Elastic 有一条完整的产品线 ELK – Elasticsearch、Logstash、Kibana,前面说的三个就是常说的 ELK 技术栈(开源实时日志分析平台)。

MySQL ---Logstash数据同步---> ElasticSearch索引库

MySQL ---- MySQL可视化软件

ElasticSearch索引库 ---- Kibana可视化软件

Logstash 的作用就是一个数据收集器,将各种格式各种渠道的数据通过它收集解析之后格式化输出到 Elastic Search ,最后再由 Kibana 提供的比较友好的 Web 界面进行汇总、分析、搜索。

ELK 内部实际就是个管道结构,数据从 Logstash 到 Elastic Search 再到 Kibana 做可视化展示。这三个组件各自也可以单独使用,比如 Logstash 不仅可以将数据输出到 Elastic Search ,也可以到数据库、缓存等。

简介

Elastic

Elastic 官网:https://www.elastic.co/cn/

Elastic 有一条完整的产品线:Elasticsearch、Logstash、Kibana 等,前面说的三个就是常说的 ELK 技术栈。

Elasticsearch

Elasticsearch 官网:https://www.elastic.co/cn/products/elasticsearch

功能:

  • 分布式的搜索引擎 – 百度、Google、站内搜索。
  • 全文检索 = 提供模糊搜索等自动度很高的查询方式,并进行相关性排名,高亮等功能。
  • 数据分析引擎(分组聚合)- 电商网站一周内手机销量 Top 10。
  • 对海量数据进行近乎实时处理 – 水平扩展,每秒钟可处理海量事件,同时能够自动管理索引和查询在集群中的分布方式,以实现极其流畅的操作。

Elastic Search 具备以下特点:

  • 高速、扩展性、最相关的搜索结果。
  • 分布式 – 节点对外表现对等,每个节点都可以作为入门,加入节点自动负载均衡。
  • JSON – 输入输出格式是 JSON。
  • Restful 风格,一切 API 都遵循 Rest 原则,容易上手。
  • 近实时搜索,数据更新在 Elasticsearch 中几乎是完全同步的,数据检索近乎实时。
  • 安装方便 – 没有其它依赖,下载后安装很方便,简单修改几个参数就可以搭建集群。
  • 支持超大数据:可以扩展到 PB 级别的结构化和非结构化数据。

版本

目前 Elasticsearch 最新的版本是 7.x,企业内目前用的比较多是 6.x,以 6.2.4 为例子,需要 JDK 1.8 及以上。

安装和配置

为了快速看到效果可以直接在本地 window 下安装 Elasticsearch,实际开发是在 Linux 中使用,但使用方式是一样的;环境要求:JDK 8 及以上版本。

第一步:解压安装包

把压缩包 elasticsearch-6.2.4.zip 放到一个没有中文没有空格的位置,解压即可。

\bin            命令,启动,关闭
\config         配置文件
\lib            ES 的 jar 包依赖
\logs           日志
\modules        ES 工作所依赖的一些组件,启动时加载
\plugins        放置一些第三方插件,如 IK 分词器
LICENSE.txt
NOTICE.txt
README.textile

第二步:修改配置文件

1、修改索引数据和日志数据存储的路径 \config\elasticsearch.yml

第 33 行和 37 行,修改完记得把注释打开:

# ----------------------------------- Paths ------------------------------------
#
# Path to directory where to store the data (separate multiple locations by comma):
#
path.data: e:\class\es\data
#
# Path to log files:
#
path.logs: e:\class\es\logs
#

第三步:启动

进入 bin 目录中直接双击 elasticsearch.bat

如果启动失败,需要修改虚拟机内存的大小,默认为 1 G,可以调小。

在 config 目录下找到 jvm.options 文件 ,修改后如下:

# Xms represents the initial size of total heap space
# Xmx represents the maximum size of total heap space

-Xms256m
-Xmx256m
  • Xms 是指设定程序启动时占用内存大小。一般来讲,大点,程序会启动的快一点,但是也可能会导致机器暂时间变慢。
  • Xmx 是指设定程序运行期间最大可占用的内存大小。如果程序运行需要占用更多的内存,超出了这个设置值,就会抛出 OutOfMemory 异常。

访问

启动后台部分输出如下:

[2020-11-08T21:52:33,482][INFO ][o.e.n.Node               ] [G9aQRl_] starting ...
[2020-11-08T21:52:34,307][INFO ][o.e.t.TransportService   ] [G9aQRl_] publish_address {127.0.0.1:9300}, bound_addresses {127.0.0.1:9300}, {[::1]:9300}
[2020-11-08T21:52:37,363][INFO ][o.e.c.s.MasterService    ] [G9aQRl_] zen-disco-elected-as-master ([0] nodes joined), reason: new_master {G9aQRl_}{G9aQRl_qR2KeysskhZY0xQ}{87fblLs1Rci_HVjZVzVlOQ}{127.0.0.1}{127.0.0.1:9300}
[2020-11-08T21:52:37,363][INFO ][o.e.c.s.ClusterApplierService] [G9aQRl_] new_master {G9aQRl_}{G9aQRl_qR2KeysskhZY0xQ}{87fblLs1Rci_HVjZVzVlOQ}{127.0.0.1}{127.0.0.1:9300}, reason: apply cluster state (from master [master {G9aQRl_}{G9aQRl_qR2KeysskhZY0xQ}{87fblLs1Rci_HVjZVzVlOQ}{127.0.0.1}{127.0.0.1:9300} committed version [1] source [zen-disco-elected-as-master ([0] nodes joined)]])
[2020-11-08T21:52:37,426][INFO ][o.e.g.GatewayService     ] [G9aQRl_] recovered [0] indices into cluster_state
[2020-11-08T21:52:37,762][INFO ][o.e.h.n.Netty4HttpServerTransport] [G9aQRl_] publish_address {127.0.0.1:9200}, bound_addresses {127.0.0.1:9200}, {[::1]:9200}
[2020-11-08T21:52:37,762][INFO ][o.e.n.Node               ] [G9aQRl_] started

可以看到绑定了两个端口:

9300 – 集群节点间通讯接口,接收 tcp 协议。

9200 – 客户端访问接口,接收 Http 协议。

在浏览器中访问:http://127.0.0.1:9200

{
  "name" : "G9aQRl_",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "ezpbwfUtTqubgqUGPvJWAg",
  "version" : {
    "number" : "6.2.4",
    "build_hash" : "ccec39f",
    "build_date" : "2018-04-12T20:37:28.497551Z",
    "build_snapshot" : false,
    "lucene_version" : "7.2.1",
    "minimum_wire_compatibility_version" : "5.6.0",
    "minimum_index_compatibility_version" : "5.0.0"
  },
  "tagline" : "You Know, for Search"
}

安装 kibana

什么是 Kibana

Kibana 是一个基于 Node.js 的 Elasticsearch 索引库数据统计工具,可以利用 Elasticsearch 的聚合功能,生成各种图表,如柱形图,线状图,饼;而且还提供了操作 Elasticsearch 索引数据的控制台,并且提供了一定的 API 提示。

安装

因为 Kibana 依赖于 node,需要在 windows 下先安装 Node.js,直接双击运行 node.js 的安装包:node-v10.15.0-x64.msi

安装成功后在任意 DOS 窗口输入:node -v,即可查看到 node 版本。

然后安装 kibana,版本与 Elasticsearch 保持一致,也是 6.2.4。

直接解压安装包即可:kibana-6.2.4-windows-x86_64.zip

配置运行

配置

进入安装目录下的 config 目录,修改 kibana.yml 文件的第 21 行(注释放开)。

确保 elasticsearch 服务器的地址如下:

elasticsearch.url: "http://localhost:9200"

运行

进入安装目录下的 bin 目录,双击 kibana.bat 启动。

  log   [14:05:55.974] [info][listening] Server running at http://localhost:5601
  log   [14:05:56.031] [info][status][plugin:elasticsearch@6.2.4] Status changed from yellow to green - Ready

可以看到 Kibana 的监听端口是 5601,于是直接访问:http://127.0.0.1:5601

控制台

成功访问 Kibana 后,选择左侧的 DevTools 菜单,即可进入控制台页面。

在页面右侧,就可以输入请求,访问 Elasticsearch 了。

编写 Restful 请求;这里类似于 POST 或者浏览器,可以向 ES 发送请求,但是不用写 ES 的地址,因为在 config/kibana.yml 文件中已经定义了 ES 的地址,剩下的只需要填写对应的 uri 和参数即可:

GET _search
{
  "query": {
    "match_all": {}
  }
}

点击按钮执行请求,返回执行结果,显示的格式是 JSON,请求格式和响应格式一样都是 JSON 格式:

{
  "took": 6,
  "timed_out": false,
  "_shards": {
    "total": 0,
    "successful": 0,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 0,
    "max_score": 0,
    "hits": []
  }
}

安装 ik 分词器

Lucene 的 IK 分词器早在 2012 年已经没有维护了,现在要使用的是在其基础上维护升级的版本,并且开发为 Elasticsearch 的集成插件了,与 Elasticsearch 一起维护升级,版本也保持一致。

https://github.com/medcl/elasticsearch-analysis-ik

安装

1、 解压 elasticsearch-analysis-ik-6.2.4.zip 后,将解压后的文件夹拷贝到 elasticsearch-6.2.4\plugins 下,并重命名文件夹为 ik

2、重新启动 ElasticSearch,即可加载 IK 分词器。

测试

在 kibana 控制台输入下面的请求:

GET /_analyze
{
  "analyzer": "ik_max_word",
  "text": "我是中国人"
}

运行得到结果:

{
  "tokens": [
    {
      "token": "我",
      "start_offset": 0,
      "end_offset": 1,
      "type": "CN_CHAR",
      "position": 0
    },
    {
      "token": "是",
      "start_offset": 1,
      "end_offset": 2,
      "type": "CN_CHAR",
      "position": 1
    },
    {
      "token": "中国人",
      "start_offset": 2,
      "end_offset": 5,
      "type": "CN_WORD",
      "position": 2
    },
    {
      "token": "中国",
      "start_offset": 2,
      "end_offset": 4,
      "type": "CN_WORD",
      "position": 3
    },
    {
      "token": "国人",
      "start_offset": 3,
      "end_offset": 5,
      "type": "CN_WORD",
      "position": 4
    }
  ]
}

使用 smart 分词:

GET /_analyze
{
  "analyzer": "ik_smart",
  "text": "我是中国人"
}

结果:

{
  "tokens": [
    {
      "token": "我",
      "start_offset": 0,
      "end_offset": 1,
      "type": "CN_CHAR",
      "position": 0
    },
    {
      "token": "是",
      "start_offset": 1,
      "end_offset": 2,
      "type": "CN_CHAR",
      "position": 1
    },
    {
      "token": "中国人",
      "start_offset": 2,
      "end_offset": 5,
      "type": "CN_WORD",
      "position": 2
    }
  ]
}

安装 Head 插件

简介

elasticsearch-head 是一个界面化的集群操作和管理工具,可以对集群进行傻瓜式操作。可以通过插件把它集成到 es(首选方式),也可以安装成一个独立 webapp。

es-head 主要有三个方面的操作:

  1. 显示集群的拓扑,并且能够执行索引和节点级别操作。
  2. 搜索接口能够查询集群中原始 JSON 或表格格式的检索数据。
  3. 能够快速访问并显示集群的状态。

官方的文档:https://github.com/mobz/elasticsearch-head

安装

基于谷歌浏览器:

1)直接下载压缩包 elasticsearch-head.7z

2)解压。

3)在谷歌浏览器中点击“加载已解压的压缩程序”,找到 elasticsearch-head 文件夹,点击打开即可进行安装。

使用 kibana 对索引库操作

基本概念

节点、集群、分片及副本

1、节点 (node)

一个节点是一个 Elasticsearch 的实例。

在服务器上启动 Elasticsearch 之后,就拥有了一个节点。如果在另一台服务器上启动 Elasticsearch,这就是另一个节点。甚至可以通过启动多个 Elasticsearch 进程,在同一台服务器上拥有多个节点。

2、集群(cluster)

多个协同工作的 Elasticsearch 节点的集合被称为集群。

在多节点的集群上,同样的数据可以在多台服务器上传播。这有助于性能。这同样有助于稳定性,如果每个分片至少有一个副本分片,那么任何一个节点宕机后,Elasticsearch 依然可以进行服务,返回所有数据。

但是它也有缺点:必须确定节点之间能够足够快速地通信,并且不会产生脑裂效应(集群的 2 个部分不能彼此交流,都认为对方宕机了)。

3、分片 (shard)

索引可能会存储大量数据,这些数据可能超过单个节点的硬件限制。例如,十亿个文档的单个索引占用了 1 TB 的磁盘空间,可能不适合单个节点的磁盘,或者可能太慢而无法单独满足来自单个节点的搜索请求。

为了解决此问题,Elasticsearch 提供了将索引细分为多个碎片的功能。创建索引时,只需定义所需的分片数量即可。每个分片本身就是一个功能齐全且独立的“索引”,可以托管在群集中的任何节点上。

分片很重要,主要有两个原因:

  • 它允许水平分割 / 缩放内容量。
  • 它允许跨碎片(可能在多个节点上)分布和并行化操作,从而提高性能 / 吞吐量。

分片如何分布以及其文档如何聚合回到搜索请求中的机制完全由 Elasticsearch 管理,并且对用户是透明的。

在随时可能发生故障的网络 / 云环境中,非常有用,强烈建议使用故障转移机制,以防碎片 / 节点因某种原因脱机或消失。为此,Elasticsearch 允许将索引分片的一个或多个副本制作为所谓的副本分片(简称副本)。

4、副本(replica)

分片处理允许用户推送超过单机容量的数据至 Elasticsearch 集群。副本则解决了访问压力过大时单机无法处理所有请求的问题。

分片可以是主分片,也可以是副本分片,其中副本分片是主分片的完整副本。副本分片用于搜索,或者是在原有的主分片丢失后成为新的主分片。

注意:可以在任何时候改变每个分片的副本分片的数量,因为副本分片总是可以被创建和移除的。这并不适用于索引划分为主分片的数量,在创建索引之前,必须决定主分片的数量。过少的分片将限制可扩展性,但是过多的分片会影响性能。默认设置的5份是一个不错的开始。

文档、类型、索引及映射

1、文档 (document)

Elasticsearch 是面向文档的,这意味着索引和搜索数据的最小单位是文档。

在 Elasticsearch 中文档有几个重要的属性:

  • 它是自我包含的,一篇文档同时包含字段和它们的取值。
  • 它可以是层次的。文档中还包含新的文档,字段还可以包含其他字段和取值。例如 “location” 字段可以同时包含 “city” 和 “street“ 两个字段。
  • 它拥有灵活的结构。文档不依赖于预先定义的模式。并非所有的文档都需要拥有相同的字段,它们不受限于同一个模式。

2、类型 (type)

类型是文档的逻辑容器,类似于表格是行的容器。在不同的类型中,最好放入不同结构的文档。例如,可以用一个类型定义聚会时的分组,而另一个类型定义人们参加的活动。

3、索引 (index)

索引是映射类型的容器。一个 Elasticsearch 索引是独立的大量的文档集合。 每个索引存储在磁盘上的同组文件中,索引存储了所有映射类型的字段,还有一些设置。

4、映射(mapping)

所有文档在写入索引前都将被分析,用户可以设置一些参数,决定如何将输入文本分割为词条,哪些词条应该被过滤掉,或哪些附加处理有必要被调用(比如移除 HTML 标签)。这就是映射扮演的角色:存储分析链所需的所有信息。

Elasticsearch 也是基于 Lucene 的全文检索库,本质也是存储数据,很多概念与 MySQL 类似的。

对比关系:

索引库 indices ----- Database 数据库
类型 type ---- Table 数据表
文档 Document ----- Row 行
域字段 Field ----- Columns 列 

映射配置 mappings ----- 每个列的约束(类型、长度) 

详细说明:

  • 类型(type)是模拟 mysql 中的 table 概念,一个索引库下可以有不同类型的索引(目前 6.X 以后的版本只能有一个类型),类似数据库中的表概念。数据库表中有表结构,也就是表中每个字段的约束信息;索引库的类型中对应表结构的叫做映射 (mapping) ,用来定义每个字段的约束。
  • 文档(document)存入索引库原始的数据;比如每一条商品信息,就是一个文档。
  • 字段(field)文档中的属性。
  • 映射配置(mappings)字段的数据类型、属性、是否索引、是否存储等特性。

创建索引库

语法

Elasticsearch 采用 Rest 风格 API,因此其 API 就是一次 http 请求,可以用任何工具发起 http 请求。

创建索引的请求格式:

  • 请求方式:PUT
  • 请求路径:/索引库名
  • 请求参数:json 格式
{
    "settings": {
        "属性名": "属性值"
    }
}

settings:就是索引库设置,其中可以定义索引库的各种属性,目前可以不设置,都走默认。

使用 kibana 创建

Kibana 的控制台,可以对 http 请求进行简化,示例:

PUT /renda

响应数据:

{
  "acknowledged": true,
  "shards_acknowledged": true,
  "index": "renda"
}

相当于是省去了 elasticsearch 的服务器地址,而且还有语法提示。

查看索引库

Get 请求可以查看索引信息:

GET /renda

响应数据:

{
  "renda": {
    "aliases": {},
    "mappings": {},
    "settings": {
      "index": {
        "creation_date": "1604847321532",
        "number_of_shards": "5",
        "number_of_replicas": "1",
        "uuid": "ZlK7XTn6S8OIOAdhPCvnxA",
        "version": {
          "created": "6020499"
        },
        "provided_name": "renda"
      }
    }
  }
}

删除索引库

删除索引使用 DELETE请 求:

DELETE /renda

响应信息:

{
  "acknowledged": true
}

再次查看 renda:

GET /renda

响应信息:

{
  "error": {
    "root_cause": [
      {
        "type": "index_not_found_exception",
        "reason": "no such index",
        "resource.type": "index_or_alias",
        "resource.id": "renda",
        "index_uuid": "_na_",
        "index": "renda"
      }
    ],
    "type": "index_not_found_exception",
    "reason": "no such index",
    "resource.type": "index_or_alias",
    "resource.id": "renda",
    "index_uuid": "_na_",
    "index": "renda"
  },
  "status": 404
}

使用 kibana 对类型及映射操作

有了索引库 ,等于有了数据库中的 database。接下来就需要索引库中的类型了,也就是数据库中的表。创建数据库表需要设置字段约束,索引库也一样,在创建索引库的类型时,需要知道这个类型下有哪些字段,每个字段有哪些约束信息,这就叫做字段映射 (mapping)。

注意:Elasticsearch7.x 取消了索引 type 类型的设置,不允许指定类型,默认为 _doc;但字段仍然是有类型的,还需要设置字段的约束信息,叫做字段映射(mapping)。

字段的约束在 Lucene 中见到过,包括到不限于:

  • 字段的数据类型
  • 是否要存储
  • 是否要索引
  • 是否分词
  • 分词器是什么

创建字段映射

请求方式依然是 PUT:

PUT /索引库名/_mapping/typeName
{
  "properties": {
    "字段名": {
      "type": "类型",
      "index": true,
      "store": true,
      "analyzer": "分词器"
    }
  }
}

typeName 类型名称:就是前面的 type 的概念,类似于数据库中的表。

字段名:任意填写,下面指定许多属性,例如:

  • type – 类型,可以是 text、keyword、long、short、date、integer、object 等。
  • index – 是否索引,默认为 true。
  • store – 是否存储,默认为 false。
  • analyzer – 分词器,这里的 ik_max_word 即使用 ik 分词器。

发起请求:

PUT renda/_mapping/goods
{
  "properties": {
    "title": {
      "type": "text",
      "store": true, 
      "analyzer": "ik_max_word"
    },
    "images": {
      "type": "keyword", 
      "store": true, 
      "index": false
    },
    "price": {
      "type": "float"
    }
  }
}

响应结果:

{
  "acknowledged": true
}

上述案例中,就给 renda 这个索引库添加了一个名为 goods 的类型,并且在类型中设置了 3 个字段:

  • title – 商品标题
  • images – 商品图片
  • price – 商品价格

并且给这些字段设置了一些属性,至于这些属性对应的含义,后面会详细介绍。

查看映射关系

查看某个索引库中的所有类型的映射语法:

GET /索引库名/_mapping

如果要查看某个类型映射,可以再路径后面跟上类型名称:

GET /索引库名/_mapping/类型名

示例请求:

GET /renda/_mapping/goods

响应:

{
  "renda": {
    "mappings": {
      "goods": {
        "properties": {
          "images": {
            "type": "keyword",
            "index": false,
            "store": true
          },
          "price": {
            "type": "float"
          },
          "title": {
            "type": "text",
            "store": true,
            "analyzer": "ik_max_word"
          }
        }
      }
    }
  }
}

映射属性详解

1)type

Elasticsearch 中支持的数据类型非常丰富:

核心类型: 
字符串类型 - text,keyword - 结构化搜索,全文文本搜索、聚合、排序等
整数类型 - integer,long,short,byte - 字段的长度越短,索引和搜索的效率越高
浮点类型 - double,float,half_float,scaled_float
逻辑类型 - boolean
日期类型 - date
范围类型 - range
二进制类型 - binary 
  该 binary 类型接受二进制值作为 Base64 编码的字符串。
  该字段默认情况下不存储(store),并且不可搜索。

复合类型:
数组类型 - array
对象类型 - object - 用于单个 JSON 对象
嵌套类型 - nested - 用于 JSON 对象数组

地理类型:
地理坐标类型 - geo_point - 纬度/经度积分
地理地图 - geo_shape - 用于多边形等复杂形状

特殊类型:
IP 类型 - ip - 用于 IPv4 和 IPv6 地址
范围类型 - completion - 提供自动完成建议
令牌计数类型 - token_count - 计算字符串中令牌的数量

String 类型分两种:

  • text:使用文本数据类型的字段,它们会被分词,文本字段不用于排序,很少用于聚合,如文章标题、正文。
  • keyword:关键字数据类型,用于索引结构化内容的字段,不会被分词,必须完整匹配的内容,如邮箱,身份证号;支持聚合。

这两种类型都是比较常用的,但有的时候,对于一个字符串字段,可能希望两种都支持,此时,可以利用其多字段特性。

"properties": {
    "title":{
        "type": "text",
        "analyzer": "ik_max_word",
        "fields": {
            "sort": {
                "type": "keyword"
            }
        },
        "index": true
    }
}

Numerical:数值类型,分两类

  • 基本数据类型:longintergershortbytedoublefloathalf_float
  • double 双精度 64 位
  • float 单精度 32 位
  • half_float 半精度 16 位
  • 浮点数的高精度类型:scaled_float。带有缩放因子的缩放类型浮点数,依靠一个 long 数字类型通过一个固定的 (double 类型) 缩放因数进行缩放;需要指定一个精度因子,比如 10 或 100。Elasticsearch 会把真实值乘以这个因子后存储,取出时再还原。

Date:日期类型,Elasticsearch 可以对日期格式化为字符串存储,但是建议存储为毫秒值,存储为 long,节省空间。

Array:数组类型

  • 进行匹配时,任意一个元素满足,都认为满足。
  • 排序时,如果升序则用数组中的最小值来排序,如果降序则用数组中的最大值来排序。
字符串数组:["one", "two"]
整数数组:[1, 2]
数组的数组:[1, [2, 3]],等价于 [1,2,3]
对象数组:[{"name": "Mary", "age": 12}, {"name": "John", "age": 10}]

Object:对象,JSON 文档本质上是分层的 – 文档包含内部对象,内部对象本身还包含内部对象。

{
    "region": "US",
    "manager.age": 30,
    "manager.name ": "John Smith"
}

索引方法如下:

{
    "mappings": {
        "properties": {
            "region": { "type": "keyword" },
            "manager": {
                "properties": {
                    "age": { "type": "integer" },
                    "name": { "type": "text" }
                }
            }
        }
    }
}

如果存储到索引库的是对象类型,例如上面的 manager,会把 manager 编成两个字段:manager.namemanager.age

IP 地址:

PUT my_index
{
    "mappings": {
        "_doc": {
            "properties": {
                "ip_addr": {
                    "type": "ip"
                }
            }
        }
    }
}

PUT my_index/_doc/1
{
    "ip_addr": "192.168.1.1"
}

GET my_index/_search
{
    "query": {
        "term": {
            "ip_addr": "192.168.0.0/16"
        }
    }
}

2)index

index 影响字段的索引情况。

  • true:字段会被索引,则可以用来进行搜索过滤。默认值就是 true,只有当某一个字段的 index 值设置为 true 时,检索 ES 才可以作为条件去检索。
  • false:字段不会被索引,不能用来搜索。

index 的默认值就是 true,也就是说不进行任何配置,所有字段都会被索引。

但是有些字段是不希望被索引的,比如商品的图片信息 URL,就需要手动设置 index 为 false。

3)store

是否将数据进行额外存储。

在 lucene 中,如果一个字段的 store 设置为 false,那么在文档列表中就不会有这个字段的值,用户的搜索结果中不会显示出来。

但是在 Elasticsearch 中,即便 store 设置为 false,也可以搜索到结果。

原因是 Elasticsearch 在创建文档索引时,会将文档中的原始数据备份,保存到一个叫做 _source 的属性中。而且我们可以通过过滤 _source 来选择哪些要显示,哪些不显示。

如果设置 store 为 true,就会在 _source 以外额外存储一份数据,多余,因此一般都会将 store 设置为 false,事实上,store 的默认值就是 false。

在某些情况下,这对 store 某个领域可能是有意义的。例如,如果文档包含一个 title ,一个 date 和一个非常大的 content 字段,则可能只想检索 the title 和 the date 而不必从一个大 _source 字段中提取这些字段:

PUT my_index
{
    "mappings": {
        "_doc": {
            "properties": {
                "title": {
                    "type": "text",
                    "store": true 
                },
                "date": {
                    "type": "date",
                    "store": true 
                },
                "content": {
                    "type": "text"
                }
            }
        }
    }
}

4)boost

网站权重:是指搜索引擎给网站(包括网页)赋予一定的权威值,对网站(含网页)权威的评估评价。一个网站权重越高,在搜索引擎所占的份量越大,在搜索引擎排名就越好。提高网站权重,不但利于网站(包括网页)在搜索引擎的排名更靠前,还能提高整站的流量,提高网站信任度。所以提高网站的权重具有相当重要的意义。 权重即网站在 SEO 中的重要性,权威性。Page Strength:1、权重不等于排名;2、权重对排名有着非常大的影响;3、整站权重的提高有利于内页的排名。

权重,新增数据时,可以指定该数据的权重,权重越高,得分越高,排名越靠前。

PUT my_index
{
    "mappings": {
        "_doc": {
            "properties": {
                "title": {
                    "type": "text",
                    "boost": 2 
                },
                "content": {
                    "type": "text"
                }
            }
        }
    }
}

title 字段上的匹配项的权重是字段上的匹配项的权重的两倍 content,默认 boost 值为 1.0

提升仅适用于 Term 查询;不提升 prefix,range 和模糊查询。

一次创建索引库和类型

第一步:
PUT /renda

第二步:
PUT renda/_mapping/goods
{
  "properties": {
    "title": {
      "type": "text",
      "analyzer": "ik_max_word"
    },
    "images": {
      "type": "keyword",
      "index": "false"
    },
    "price": {
      "type": "float"
    }
  }
}

刚才的案例中是把创建索引库和类型分开来做,其实也可以在创建索引库的同时,直接制定索引库中的类型,基本语法:

put /索引库名
{
    "settings":{
        "索引库属性名":"索引库属性值"
    },
    "mappings":{
        "类型名":{
            "properties":{
                "字段名":{
                    "映射属性名":"映射属性值"
                }
            }
        }
    }
}

示例:

PUT /renda2
{
    "settings": {}, 
    "mappings": {
        "goods": {
            "properties": {
                "title": {
                    "type": "text",
                    "analyzer": "ik_max_word"
                }
            }
        }
    }
}

结果:

{
    "acknowledged": true,
    "shards_acknowledged": true,
    "index": "renda2"
}

使用 Kibana 对文档操作

文档,即索引库中某个类型下的数据,会根据规则创建索引,将来用来搜索。可以类比做数据库中的每一行数据。

新增文档

新增并随机生成 id。

通过 POST 请求,可以向一个已经存在的索引库中添加文档数据。

语法:

POST /索引库名/类型名
{
    "key":"value"
}

示例:

POST /renda/goods/
{
    "title": "小米手机",
    "images": "http://image.renda.com/12479122.jpg",
    "price": 2699.00
}

响应:

{
  "_index": "renda",
  "_type": "goods",
  "_id": "gPeQqHUB-UTJAEEuqOm9",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 0,
  "_primary_term": 1
}

可以看到结果显示为:created,是创建成功了。

另外,需要注意的是,在响应结果中有个 _id 字段,这个就是这条文档数据的唯一标示 ,以后的增删改查都依赖这个 id 作为唯一标示。

可以看到 id 的值为:gPeQqHUB-UTJAEEuqOm9,这里新增时没有指定 id,所以是 ES 随机生成的 id

查看文档

根据 Rest 风格,新增是 post,查询应该是 get,不过查询一般都需要条件,这里把刚刚生成数据的 id 带上。

通过 kibana 查看数据:

GET /renda/goods/gPeQqHUB-UTJAEEuqOm9

查看结果:

{
  "_index": "renda",
  "_type": "goods",
  "_id": "gPeQqHUB-UTJAEEuqOm9",
  "_version": 1,
  "found": true,
  "_source": {
    "title": "小米手机",
    "images": "http://image.renda.com/12479122.jpg",
    "price": 2699
  }
}
  • _source:源文档信息,所有的数据都在里面。
  • _id:这条文档的唯一标示。
  • 自动生成的 id,长度为 20 个字符,URL 安全,base64 编码,GUID 全局唯一标识符,分布式系统并行生成时不可能会发生冲突。
  • 在实际开发中不建议使用 ES 生成的 ID,太长且为字符串类型,检索时效率低。建议:将数据表中唯一的 ID,作为 ES 的文档 ID。

新增文档并自定义 id

如果想要自己新增的时候指定 id,可以这么做:

POST /索引库名/类型/id值
{
    ...
}

示例:

POST /renda/goods/2
{
    "title": "白米手机",
    "images": "http://image.renda.com/12479122.jpg",
    "price": 2699.00
}

得到的数据:

{
  "_index": "renda",
  "_type": "goods",
  "_id": "2",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 0,
  "_primary_term": 1
}

修改数据

PUT – 修改文档。

POST – 新增文档。

把刚才新增的请求方式改为 PUT,就是修改了,不过修改必须指定id。

  • id 对应文档存在,则修改。
  • id 对应文档不存在,则新增。

比如,使用 id 为 3,不存在,则应该是新增:

PUT /renda/goods/3
{
    "title": "黑米手机",
    "images": "http://image.renda.com/12479122.jpg",
    "price": 3999.00,
    "stock": 99,
    "saleable": true
}

结果:

{
  "_index": "renda",
  "_type": "goods",
  "_id": "3",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 0,
  "_primary_term": 1
}

可以看到是 created,是新增。

再次执行刚才的请求,不过把数据改一下:

PUT /renda/goods/3
{
    "title": "X米手机",
    "images": "http://image.renda.com/12479122.jpg",
    "price": 4999.00,
    "stock": 199,
    "saleable": true
}

结果:

{
  "_index": "renda",
  "_type": "goods",
  "_id": "3",
  "_version": 2,
  "result": "updated",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 1,
  "_primary_term": 1
}

可以看到结果是:updated,显然是更新数据。

删除数据

删除使用 DELETE 请求,同样,需要根据 id 进行删除。

语法:

DELETE /索引库名/类型名/id值

示例:

DELETE /renda/goods/3

结果:

{
  "_index": "renda",
  "_type": "goods",
  "_id": "3",
  "_version": 3,
  "result": "deleted",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 2,
  "_primary_term": 1
}

智能判断

Elasticsearch 非常智能,不需要给索引库设置任何 mapping 映射,它也可以根据输入的数据来判断类型,动态添加数据映射。

测试一下:

POST /renda/goods/3
{
    "title":"超大米手机",
    "images":"http://image.renda.com/12479122.jpg",
    "price":3299.00,
    "stock": 200,
    "saleable":true, 
    "subTitle":"大米"
}

额外添加了 stock 库存,saleable 是否上架,subtitle 副标题,3 个字段。

响应结果:

{
  "_index": "renda",
  "_type": "goods",
  "_id": "3",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 3,
  "_primary_term": 2
}

执行 GET renda 查看索引库的映射关系:

{
  "renda": {
    "aliases": {},
    "mappings": {
      "goods": {
        "properties": {
          "images": {
            "type": "keyword",
            "index": false,
            "store": true
          },
          "price": {
            "type": "float"
          },
          "saleable": {
            "type": "boolean"
          },
          "stock": {
            "type": "long"
          },
          "subTitle": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "title": {
            "type": "text",
            "store": true,
            "analyzer": "ik_max_word"
          }
        }
      }
    },
    "settings": {
      "index": {
        "creation_date": "1604847840423",
        "number_of_shards": "5",
        "number_of_replicas": "1",
        "uuid": "w-Q9ingUTjm2MsfaQ0730g",
        "version": {
          "created": "6020499"
        },
        "provided_name": "renda"
      }
    }
  }
}

stocksaleablesubtitle 都被成功映射了。

如果没有事先定义对应的 Mapping,那么就会根据文档中的字段数据推断类型并创建;而且 ES 会在匹配类型中选择范围最大的作为新建的 Mapping 字段的类型。

subtitle 是 String 类型数据,ES 无法智能判断是否应该分词,它就会存入两个字段,从而满足分词和不分词这两种情况。例如:

  • subtitle – text 类型
  • subtitle.keyword – keyword 类型
  • keyword 的 ignore_above 为 256,就是说如果此字段值超过了 256 字符,此时将不被索引

这种智能映射,底层原理是动态模板映射,如果想修改这种智能映射的规则,其实只要修改动态模板即可。

动态映射模板

动态模板的语法:

1)模板名称,随便起。

2)匹配条件,凡是符合条件的未定义字段,都会按照这个规则来映射。

3)映射规则,匹配成功后的映射规则。

可以把所有未映射的 String 类型数据自动映射为 keyword 类型:

PUT renda3
{
    "mappings": {
        "goods": {
            "properties": {
                "title": {
                    "type": "text",
                    "analyzer": "ik_max_word",
                    "index": true,
                    "store": true
                }
            },
            "dynamic_templates": [
                {
                    "strings": {
                        "match_mapping_type": "string",
                        "mapping": {
                            "type": "keyword",
                            "index": false,
                            "store": true
                        }
                    }
                }
            ]
        }
    }
}

响应结果:

{
  "acknowledged": true,
  "shards_acknowledged": true,
  "index": "renda3"
}

在这个案例中,做了两个映射配置:

  • title 字段 – 统一映射为 text 类型,并制定分词器。
  • 其它字段 – 只要是 string 类型,统一都处理为 keyword 类型。

这样,未知的 string 类型数据就不会被映射为 text 和 keyword 并存,而是统一以 keyword 来处理。

新增一个数据:

POST /renda3/goods/1
{
    "title":"X米手机",
    "images":"http://image.renda.com/12479122.jpg",
    "price":3299.00
}

响应结果:

{
  "_index": "renda3",
  "_type": "goods",
  "_id": "1",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 0,
  "_primary_term": 1
}

查看 imagesprice 会被映射为什么类型:

GET /renda3/_mapping

结果:

{
  "renda3": {
    "mappings": {
      "goods": {
        "dynamic_templates": [
          {
            "strings": {
              "match_mapping_type": "string",
              "mapping": {
                "index": false,
                "store": true,
                "type": "keyword"
              }
            }
          }
        ],
        "properties": {
          "images": {
            "type": "keyword",
            "index": false,
            "store": true
          },
          "price": {
            "type": "float"
          },
          "title": {
            "type": "text",
            "store": true,
            "analyzer": "ik_max_word"
          }
        }
      }
    }
  }
}

可以看到 images 被映射成了 keyword,而非之前的 text 和 keyword 并存,说明动态模板生效了。

ElasticSearch 查询

基本查询

基本语法:

GET /索引库名/_search
{
    "query":{
        "查询类型":{
            "查询条件":"查询条件值"
        }
    }
}

这里的 query 代表一个查询对象,里面可以有不同的查询属性。

查询类型:match_all、match、term、range 等等。

查询条件:查询条件会根据类型的不同,写法也有差异。

查询所有(match_all)

示例:

GET /renda/_search
{
    "query":{
        "match_all": {}
    }
}

query:代表查询对象。

match_all:代表查询所有。

结果:

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 3,
    "max_score": 1,
    "hits": [
      {
        "_index": "renda",
        "_type": "goods",
        "_id": "2",
        "_score": 1,
        "_source": {
          "title": "白米手机",
          "images": "http://image.renda.com/12479122.jpg",
          "price": 2699
        }
      },
      {
        "_index": "renda",
        "_type": "goods",
        "_id": "gPeQqHUB-UTJAEEuqOm9",
        "_score": 1,
        "_source": {
          "title": "小米手机",
          "images": "http://image.renda.com/12479122.jpg",
          "price": 2699
        }
      },
      {
        "_index": "renda",
        "_type": "goods",
        "_id": "3",
        "_score": 1,
        "_source": {
          "title": "超大米手机",
          "images": "http://image.renda.com/12479122.jpg",
          "price": 3299,
          "stock": 200,
          "saleable": true,
          "subTitle": "大米"
        }
      }
    ]
  }
}

结果解析:

took - 检索所耗费的时间,单位是毫秒。

timed_out - 是否超时。

_shards - 分片信息。

hits - 命中结果,检索结果信息。
  total - 搜索到的总条数。
  max_score - 所有结果中文档得分的最高分。
  hits - 搜索结果的文档对象数组,每个元素是一条搜索到的文档信息。
    _index - 索引库。
    _type - 文档类型。
    _id - 文档 id。
    _score - 评分;索引库的一个概念;关联度。
    _source - 原始数据。

文档得分:使用 ES 时,对于查询出的文档无疑会有文档相似度之别;而理想的排序是和查询条件相关性越高排序越靠前,而这个排序的依据就是 _score

匹配查询(match)

加入一条数据用于测试:

PUT /renda/goods/3
{
    "title": "小米电视4A",
    "images": "http://image.renda.com/12479122.jpg",
    "price": 3899.00
}

索引库中有 3 部手机,1 台电视。

match 类型查询,会把查询条件进行分词,然后进行查询,多个词条之间是 or 的关系:

GET /renda/_search
{
  "query": {
    "match": {
      "title": "小米电视"
    }
  }
}

响应结果:

{
  "took": 15,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 2,
    "max_score": 0.5753642,
    "hits": [
      {
        "_index": "renda",
        "_type": "goods",
        "_id": "3",
        "_score": 0.5753642,
        "_source": {
          "title": "小米电视4A",
          "images": "http://image.renda.com/12479122.jpg",
          "price": 3899
        }
      },
      {
        "_index": "renda",
        "_type": "goods",
        "_id": "gPeQqHUB-UTJAEEuqOm9",
        "_score": 0.2876821,
        "_source": {
          "title": "小米手机",
          "images": "http://image.renda.com/12479122.jpg",
          "price": 2699
        }
      }
    ]
  }
}

在上面的案例中,不仅会查询到电视,而且与小米相关的都会查询到,多个词之间是 or 的关系。

某些情况下,需要更精确查找,即 and 关系。比如在电商平台精确搜索商品时,希望这个关系(查询条件切分词之后的关系)变成 and,可以这样做:

GET /renda/_search
{
  "query": {
    "match": {
      "title": {
        "query": "小米电视",
        "operator": "and"
      }
    }
  }
}

响应结果:

{
  "took": 8,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 0.5753642,
    "hits": [
      {
        "_index": "renda",
        "_type": "goods",
        "_id": "3",
        "_score": 0.5753642,
        "_source": {
          "title": "小米电视4A",
          "images": "http://image.renda.com/12479122.jpg",
          "price": 3899
        }
      }
    ]
  }
}

此时,只有同时包含小米和电视的词条才会被搜索到。

词条匹配(term)

term 查询被用于精确值匹配,这些精确值可能是数字、时间、布尔,或者那些未分词的字符串、keyword 类型的字符串。

效果类似于:select * from tableName where colName='value';

GET /renda/_search
{
    "query":{
        "term":{
            "price": 2699.00
        }
    }
}

响应结果:

{
  "took": 6,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 2,
    "max_score": 1,
    "hits": [
      {
        "_index": "renda",
        "_type": "goods",
        "_id": "2",
        "_score": 1,
        "_source": {
          "title": "白米手机",
          "images": "http://image.renda.com/12479122.jpg",
          "price": 2699
        }
      },
      {
        "_index": "renda",
        "_type": "goods",
        "_id": "gPeQqHUB-UTJAEEuqOm9",
        "_score": 1,
        "_source": {
          "title": "小米手机",
          "images": "http://image.renda.com/12479122.jpg",
          "price": 2699
        }
      }
    ]
  }
}

布尔组合(bool)

bool 把各种其它查询通过 must - 与must_not - 非should - 或 的方式进行组合。

GET /renda/_search
{
    "query":{
        "bool":{
            "must": {
              "match": {
                "title": "小米"
              }
            },
            "must_not": {
              "match": {
                "title": "电视"
              }
            },
            "should": {
              "match": {
                "title": "手机"
              }
            }
        }
    }
}

响应结果:

{
  "took": 2,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 0.5753642,
    "hits": [
      {
        "_index": "renda",
        "_type": "goods",
        "_id": "gPeQqHUB-UTJAEEuqOm9",
        "_score": 0.5753642,
        "_source": {
          "title": "小米手机",
          "images": "http://image.renda.com/12479122.jpg",
          "price": 2699
        }
      }
    ]
  }
}

范围查询(range)

range 查询找出那些落在指定区间内的数字或者时间。

GET /renda/_search
{
    "query": {
        "range": {
            "price": {
                "gte": 3000.0,
                "lt": 4000.00
            }
        }
    }
}

响应结果:

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 1,
    "hits": [
      {
        "_index": "renda",
        "_type": "goods",
        "_id": "3",
        "_score": 1,
        "_source": {
          "title": "小米电视4A",
          "images": "http://image.renda.com/12479122.jpg",
          "price": 3899
        }
      }
    ]
  }
}

range 查询允许以下字符:

  • gt – 大于
  • gte – 大于等于
  • lt – 小于
  • lte – 小于等于

模糊查询(fuzzy)

fuzzy 查询是 term 查询的模糊等价,很少直接使用它。

新增一个商品:

POST /renda/goods/5
{
    "title": "Apple手机",
    "images": "http://image.renda.com/12479122.jpg",
    "price": 6899.00
}

响应结果:

{
  "_index": "renda",
  "_type": "goods",
  "_id": "5",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 0,
  "_primary_term": 2
}

fuzzy 查询是 term 查询的模糊等价,它允许用户搜索词条与实际词条的拼写出现偏差,但是偏差的编辑距离不得超过 2

GET /renda/_search
{
    "query": {
        "fuzzy": {
            "title": "applas"
        }
    }
}

上面的查询,也能查询到 apple 手机:

{
  "took": 4,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 0.17260925,
    "hits": [
      {
        "_index": "renda",
        "_type": "goods",
        "_id": "5",
        "_score": 0.17260925,
        "_source": {
          "title": "Apple手机",
          "images": "http://image.renda.com/12479122.jpg",
          "price": 6899
        }
      }
    ]
  }
}

结果过滤

默认情况下,Elasticsearch 在搜索的结果中,会把文档中保存在 _source 的所有字段都返回。

如果只想获取其中的部分字段,可以添加 _source 的过滤。

直接指定字段

示例:

GET /renda/_search
{
    "_source": ["title","price"],
    "query": {
        "term": {
            "price": 2699
        }
    }
}

返回的结果:

{
  "took": 2,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 2,
    "max_score": 1,
    "hits": [
      {
        "_index": "renda",
        "_type": "goods",
        "_id": "2",
        "_score": 1,
        "_source": {
          "price": 2699,
          "title": "白米手机"
        }
      },
      {
        "_index": "renda",
        "_type": "goods",
        "_id": "gPeQqHUB-UTJAEEuqOm9",
        "_score": 1,
        "_source": {
          "price": 2699,
          "title": "小米手机"
        }
      }
    ]
  }
}

指定 includes 和 excludes

includes:来指定想要显示的字段。

excludes:来指定不想要显示的字段。

二者都是可选的。

示例:

GET /renda/_search
{
    "_source": {
        "includes":["title", "price"]
    },
    "query": {
        "term": {
            "price": 2699
        }
    }
}

与下面的结果将是一样的:

GET /renda/_search
{
    "_source": {
        "excludes": ["images"]
    },
    "query": {
        "term": {
            "price": 2699
        }
    }
}

响应结果:

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 2,
    "max_score": 1,
    "hits": [
      {
        "_index": "renda",
        "_type": "goods",
        "_id": "2",
        "_score": 1,
        "_source": {
          "price": 2699,
          "title": "白米手机"
        }
      },
      {
        "_index": "renda",
        "_type": "goods",
        "_id": "gPeQqHUB-UTJAEEuqOm9",
        "_score": 1,
        "_source": {
          "price": 2699,
          "title": "小米手机"
        }
      }
    ]
  }
}

过滤(Filter)

Elasticsearch 使用的查询语言(DSL)拥有一套查询组件,这些组件可以以无限组合的方式进行搭配。

这套组件可以在以下两种情况下使用:过滤情况 – filtering context 和查询情况 – query context。

如何选择查询与过滤:

通常的规则是,使用查询(query)语句来进行全文搜索或者其它任何需要影响相关性得分的搜索;除此以外的情况都使用过滤(filters)。

条件查询中进行过滤:

所有的查询都会影响到文档的评分及排名。如果需要在查询结果中进行过滤,并且不希望过滤条件影响评分,那么就不要把过滤条件作为查询条件来用,而是使用 filter 方式:

GET /renda/_search
{
    "query":{
        "bool":{
            "must":{ "match": { "title": "小米手机" }},
            "filter":{
                "range":{"price":{"gt":2000.00,"lt":3800.00}}
            }
        }
    }
}

无查询条件,直接过滤:

如果一次查询只有过滤,没有查询条件,不希望进行评分,可以使用 constant_score 取代只有 filter 语句的 bool 查询。在性能上是完全相同的,但对于提高查询简洁性和清晰度有很大帮助。

GET /renda/_search
{
    "query":{
        "constant_score": {
            "filter": {
                "range": {"price":{"gt":2000.00, "lt":3000.00}}
            }
        }
    }
}

排序

单字段排序

sort 可以按照不同的字段进行排序,并且通过 order 指定排序的方式。

GET /renda/_search
{
    "query": {
        "match": {
            "title": "小米手机"
        }
    },
    "sort": [
        {
            "price": {
                "order": "desc"
            }
        }
    ]
}

多字段排序

假定想要结合使用 price_score 进行查询,并且匹配的结果首先按照价格排序,然后按照相关性得分排序:

GET /renda/_search
{
    "query":
    {
        "bool":
        {
            "must":
            {
              "match":
              {
                "title": "小米手机"
              }
            },
            "filter":{
                "range":
                {
                  "price":
                  {
                    "gt":2000,
                    "lt":4000
                  }
                }
            }
        }
    },
    "sort": [
        {
          "price":
          {
            "order": "desc"
          }
        },
        {
          "_score":
          {
            "order": "desc"
          }
          
        }
    ]
}

分页

Elasticsearch 中数据都存储在分片中,当执行搜索时每个分片独立搜索后,数据再经过整合返回。那么,如何实现分页查询呢?

Elasticsearch 的分页与 MySQL 数据库非常相似,都是指定两个值:

  • from – 目标数据的偏移值(开始位置),默认 from 为 0。
  • size – 每页大小。
GET /renda/_search
{
    "query": {
        "match_all": {}
    },
    "sort": [
        {
            "price": {
                "order": "asc"
            }
        }
    ],
    "from": 3,
    "size": 3
}

结果:

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 4,
    "max_score": null,
    "hits": [
      {
        "_index": "renda",
        "_type": "goods",
        "_id": "5",
        "_score": null,
        "_source": {
          "title": "Apple手机",
          "images": "http://image.renda.com/12479122.jpg",
          "price": 6899
        },
        "sort": [
          6899
        ]
      }
    ]
  }
}

高亮

高亮原理:

  • 服务端搜索数据,得到搜索结果。
  • 把搜索结果中,搜索关键字都加上约定好的标签。
  • 前端页面提前写好标签的 CSS 样式,即可高亮。

Elasticsearch 中实现高亮的语法比较简单:

GET /renda/_search
{
    "query": {
        "match": {
            "title": "手机"
        }
    },
    "highlight": {
        "pre_tags": "<em>",
        "post_tags": "</em>", 
        "fields": {
            "title": {}
        }
    }
}

在使用 match 查询的同时,加上一个 highlight 属性:

pre_tags:前置标签
post_tags:后置标签
fields:需要高亮的字段
  title:这里声明 title 字段需要高亮

结果:

{
  "took": 2,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 3,
    "max_score": 0.2876821,
    "hits": [
      {
        "_index": "renda",
        "_type": "goods",
        "_id": "5",
        "_score": 0.2876821,
        "_source": {
          "title": "Apple手机",
          "images": "http://image.renda.com/12479122.jpg",
          "price": 6899
        },
        "highlight": {
          "title": [
            "Apple<em>手机</em>"
          ]
        }
      },
      {
        "_index": "renda",
        "_type": "goods",
        "_id": "2",
        "_score": 0.2876821,
        "_source": {
          "title": "白米手机",
          "images": "http://image.renda.com/12479122.jpg",
          "price": 2699
        },
        "highlight": {
          "title": [
            "白米<em>手机</em>"
          ]
        }
      },
      {
        "_index": "renda",
        "_type": "goods",
        "_id": "gPeQqHUB-UTJAEEuqOm9",
        "_score": 0.2876821,
        "_source": {
          "title": "小米手机",
          "images": "http://image.renda.com/12479122.jpg",
          "price": 2699
        },
        "highlight": {
          "title": [
            "小米<em>手机</em>"
          ]
        }
      }
    ]
  }
}

评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注