ElasticSearch详解

一、全文检索基础

1、什么是全文检索?

将非结构化数据中的一部分信息提取出来,重新组织,使其变得有一定结构,然后对此有一定结构的数 据进行搜索,从而达到搜索相对较快的目的。这部分从非结构化数据中提取出的然后重新组织的信息, 我们称之索引。

例如:字典。字典的拼音表和部首检字表就相当于字典的索引,对每一个字的解释是非结构化的,如果 字典没有音节表和部首检字表,在茫茫辞海中找一个字只能顺序扫描。然而字的某些信息可以提取出来 进行结构化处理,比如读音,就比较结构化,分声母和韵母,分别只有几种可以一一列举,于是将读音 拿出来按一定的顺序排列,每一项读音都指向此字的详细解释的页数。我们搜索时按结构化的拼音搜到 读音,然后按其指向的页数,便可找到我们的非结构化数据——也即对字的解释。

这种先建立索引,再对索引进行搜索的过程就叫全文检索(Full-text Search)。

虽然创建索引的过程也是非常耗时的,但是索引一旦创建就可以多次使用,全文检索主要处理的是查 询,所以耗时间创建索引是值得的。

2、全文检索流程

image-20211212111851104

3、相关概念

①索引库

索引库就是存储索引的保存在磁盘上的一系列的文件。里面存储了建立好的索引信息以及文档对象

image-20211212111945908

一个索引库相当于数据库中的一张表。

②document对象

获取原始内容的目的是为了索引,在索引前需要将原始内容创建成文档(Document),文档中包括一 个一个的域(Field),域中存储内容。每个文档都有一个唯一的编号,就是文档id。

image-20211212112015863

document对象相当于表中的一条记录。

③field对象

如果我们把document看做是数据库中一条记录的话,field相当于是记录中的字段。field是索引库中存 储数据的最小单位。field的数据类型大致可以分为数值类型和文本类型,一般需要查询的字段都是文本 类型的,field的还有如下属性:

  • 是否分词:是否对域的内容进行分词处理。前提是我们要对域的内容进行查询。

  • 是否索引:将Field分析后的词或整个Field值进行索引,只有索引方可搜索到。

比如:商品名称、商品简介分析后进行索引,订单号、身份证号不用分词但也要索引,这些将来都 要作为查询条件。

  • 是否存储:将Field值存储在文档中,存储在文档中的Field才可以从Document中获取 比如:商品名称、订单号,凡是将来要从Document中获取的Field都要存储。

④term对象

从文档对象中拆分出来的每个单词叫做一个Term,不同的域中拆分出来的相同的单词是 不同的term。term中包含两部分一部分是文档的域名,另一部分是单词的内容。term是创建索引 的关键词对象

二、ElasticSearch简介

1、什么是ElasticSearch

Elaticsearch,简称为es, es是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储、检 索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别的数据。es也使用Java开发并使用 Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏 Lucene的复杂性,从而让全文搜索变得简单。

2、ElasticSearch的使用案例

  • 2013年初,GitHub抛弃了Solr,采取ElasticSearch 来做PB级的搜索。 “GitHub使用ElasticSearch搜索20TB的数据,包括13亿文件和1300亿行代码”

  • 维基百科:启动以elasticsearch为基础的核心搜索架构

  • SoundCloud:“SoundCloud使用ElasticSearch为1.8亿用户提供即时而精准的音乐搜索服务”

  • 百度:百度目前广泛使用ElasticSearch作为文本数据分析,采集百度所有服务器上的各类指标数据及用户自定义数据,通过对各种数据进行多维分析展示,辅助定位分析实例异常或业务层面异常。目前覆盖百度内部20多个业务线(包括casio、云分析、网盟、预测、文库、直达号、钱包、风控等),单集群最大100台机器,200个ES节点,每天导入30TB+数据

  • 新浪使用ES 分析处理32亿条实时日志

  • 阿里使用ES 构建挖财自己的日志采集和分析体系

3、ElasticSearch对比Solr

  • Solr 利用 Zookeeper 进行分布式管理,而 Elasticsearch 自身带有分布式协调管理功能;

  • Solr 支持更多格式的数据,而 Elasticsearch 仅支持json文件格式;

  • Solr 官方提供的功能更多,而 Elasticsearch 本身更注重于核心功能,高级功能多有第三方插件提供;

  • Solr 在传统的搜索应用中表现好于 Elasticsearch,但在处理实时搜索应用时效率明显低于Elasticsearch

三、ElasticSearch相关概念(术语)

1、概述

Elasticsearch是面向文档(document oriented)的,这意味着它可以存储整个对象或文档(document)。 然而它不仅仅是存储,还会索引(index)每个文档的内容使之可以被搜索。在Elasticsearch中,你可以对 文档(而非成行成列的数据)进行索引、搜索、排序、过滤。Elasticsearch比传统关系型数据库如下:

Relational DB -> Databases -> Tables -> Rows -> Columns
Elasticsearch -> Indices -> Types -> Documents -> Fields

2、Elasticsearch核心概念

①索引 index

一个索引就是一个拥有几分相似特征的文档的集合。比如说,你可以有一个客户数据的索引,另一个产 品目录的索引,还有一个订单数据的索引。一个索引由一个名字来标识(必须全部是小写字母的),并 且当我们要对对应于这个索引中的文档进行索引、搜索、更新和删除的时候,都要使用到这个名字。在 一个集群中,可以定义任意多的索引。

②类型 type

在一个索引中,你可以定义一种或多种类型。一个类型是你的索引的一个逻辑上的分类/分区,其语义 完全由你来定。通常,会为具有一组共同字段的文档定义一个类型。比如说,我们假设你运营一个博客 平台并且将你所有的数据存储到一个索引中。在这个索引中,你可以为用户数据定义一个类型,为博客 数据定义另一个类型,当然,也可以为评论数据定义另一个类型。

③字段Field

相当于是数据表的字段,对文档数据根据不同属性进行的分类标识

④映射 mapping

mapping是处理数据的方式和规则方面做一些限制,如某个字段的数据类型、默认值、分析器、是否被 索引等等,这些都是映射里面可以设置的,其它就是处理es里面数据的一些使用规则设置也叫做映射, 按着最优规则处理数据对性能提高很大,因此才需要建立映射,并且需要思考如何建立映射才能对性能 更好。

⑤文档 document

一个文档是一个可被索引的基础信息单元。比如,你可以拥有某一个客户的文档,某一个产品的一个文 档,当然,也可以拥有某个订单的一个文档。文档以JSON(Javascript Object Notation)格式来表 示,而JSON是一个到处存在的互联网数据交互格式。

在一个index/type里面,你可以存储任意多的文档。注意,尽管一个文档,物理上存在于一个索引之 中,文档必须被索引/赋予一个索引的type。

⑥接近实时 NRT

Elasticsearch是一个接近实时的搜索平台。这意味着,从索引一个文档直到这个文档能够被搜索到有一 个轻微的延迟(通常是1秒以内)

⑦集群 cluster

一个集群就是由一个或多个节点组织在一起,它们共同持有整个的数据,并一起提供索引和搜索功能。 一个集群由一个唯一的名字标识,这个名字默认就是“elasticsearch”。这个名字是重要的,因为一个节 点只能通过指定某个集群的名字,来加入这个集群

⑧节点 node

一个节点是集群中的一个服务器,作为集群的一部分,它存储数据,参与集群的索引和搜索功能。和集 群类似,一个节点也是由一个名字来标识的,默认情况下,这个名字是一个随机的漫威漫画角色的名 字,这个名字会在启动的时候赋予节点。这个名字对于管理工作来说挺重要的,因为在这个管理过程 中,你会去确定网络中的哪些服务器对应于Elasticsearch集群中的哪些节点。

一个节点可以通过配置集群名称的方式来加入一个指定的集群。默认情况下,每个节点都会被安排加入 到一个叫做“elasticsearch”的集群中,这意味着,如果你在你的网络中启动了若干个节点,并假定它们 能够相互发现彼此,它们将会自动地形成并加入到一个叫做“elasticsearch”的集群中。

在一个集群里,只要你想,可以拥有任意多个节点。而且,如果当前你的网络中没有运行任何 Elasticsearch节点,这时启动一个节点,会默认创建并加入一个叫做“elasticsearch”的集群。

⑨分片和复制 shards&replicas

一个索引可以存储超出单个结点硬件限制的大量数据。比如,一个具有10亿文档的索引占据1TB的磁盘 空间,而任一节点都没有这样大的磁盘空间;或者单个节点处理搜索请求,响应太慢。为了解决这个问 题,Elasticsearch提供了将索引划分成多份的能力,这些份就叫做分片。当你创建一个索引的时候,你 可以指定你想要的分片的数量。每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被 放置到集群中的任何节点上。分片很重要,主要有两方面的原因: 1)允许你水平分割/扩展你的内容容 量。 2)允许你在分片(潜在地,位于多个节点上)之上进行分布式的、并行的操作,进而提高性能/吞 吐量。

至于一个分片怎样分布,它的文档怎样聚合回搜索请求,是完全由Elasticsearch管理的,对于作为用户 的你来说,这些都是透明的。

在一个网络/云的环境里,失败随时都可能发生,在某个分片/节点不知怎么的就处于离线状态,或者由 于任何原因消失了,这种情况下,有一个故障转移机制是非常有用并且是强烈推荐的。为此目的, Elasticsearch允许你创建分片的一份或多份拷贝,这些拷贝叫做复制分片,或者直接叫复制。

复制之所以重要,有两个主要原因: 在分片/节点失败的情况下,提供了高可用性。因为这个原因,注 意到复制分片从不与原/主要(original/primary)分片置于同一节点上是非常重要的。扩展你的搜索量/ 吞吐量,因为搜索可以在所有的复制上并行运行。总之,每个索引可以被分成多个分片。一个索引也可 以被复制0次(意思是没有复制)或多次。一旦复制了,每个索引就有了主分片(作为复制源的原来的 分片)和复制分片(主分片的拷贝)之别。分片和复制的数量可以在索引创建的时候指定。在索引创建 之后,你可以在任何时候动态地改变复制的数量,但是你事后不能改变分片的数量。

默认情况下,Elasticsearch中的每个索引被分片5个主分片和1个复制,这意味着,如果你的集群中至少 有两个节点,你的索引将会有5个主分片和另外5个复制分片(1个完全拷贝),这样的话每个索引总共 就有10个分片。

四、Elasticsearch 安装

本章节将采用Docker安装,由于windows安装es非常简单,这里不再重复

(1) docker镜像下载

docker pull elasticsearch:5.6.8

注意:由于镜像有570MB,所以提供的虚拟机里已经下载好了该镜像,如下图:

(2)安装es容器

docker run -di --name=kkb_es --network=host -p 9200:9200 -p 9300:9300 elasticsearch:5.6.8

9200端口(Web管理平台端口) 9300(服务默认端口)

浏览器输入地址访问: http://192.168.88.128:9200/

(3)开启远程连接

上面完成安装后,es并不能正常使用,elasticsearch从5版本以后默认不开启远程连接,程序直接连接 会报如下错误:

failed to load elasticsearch nodes :
org.elasticsearch.client.transport.NoNodeAvailableException: None of the
configured nodes are available: [{#transport#-1}{5ttLpMhkRjKLkvoY7ltUWg}
{192.168.211.132}{192.168.211.132:9300}]

我们需要修改es配置开启远程连接,代码如下:

登录容器

docker exec -it kkb_es /bin/bash

查看目录结构 输入: dir

root@07f22eb41bb5:/usr/share/elasticsearch# dir
NOTICE.txt README.textile bin config data lib logs modules plugins

进入config目录

cd config

查看文件

root@07f22eb41bb5:/usr/share/elasticsearch/config# ls
elasticsearch.yml log4j2.properties scripts

修改elasticsearch.yml文件

root@07f22eb41bb5:/usr/share/elasticsearch/config# vi elasticsearch.yml
bash: vi: command not found

vi命令无法识别,因为docker容器里面没有该命令,我们可以安装该编辑器。

从课件拷贝sources.list并设置apt下载源

docker cp ~/sources.list kkb_es:/etc/apt/sources.list

重新进入es容器,安装vim编辑器

apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 40976EAF437D05B5 //(这个公钥根据提示来写的)
apt-get update
apt-get install vim

安装好了后,修改elasticsearch.yml配置,如下图:

vi elasticsearch.yml

将transport.host: 0.0.0.0前面的#删除

同时添加下面一行代码:

cluster.name: my-elasticsearch

重启docker

docker restart kkb_es

(4)系统参数配置

重启后发现重启启动失败了,这时什么原因呢?这与我们刚才修改的配置有关,因为elasticsearch在启 动的时候会进行一些检查,比如最多打开的文件的个数以及虚拟内存区域数量等等,如果你放开了此配 置,意味着需要打开更多的文件以及虚拟内存,所以我们还需要系统调优

修改vi /etc/security/limits.conf ,追加内容 (nofile是单个进程允许打开的最大文件个数 soft nofile 是 软限制 hard nofile是硬限制 )

* soft nofile 65536
* hard nofile 65536

修改vi /etc/sysctl.conf,追加内容 (限制一个进程可以拥有的VMA(虚拟内存区域)的数量 )

vm.max_map_count=655360

执行下面命令 修改内核参数马上生效

sysctl -p

重新启动虚拟机,再次启动容器,发现已经可以启动并远程访问

reboot

(5)跨域配置 修改elasticsearch/config下的配置文件:elasticsearch.yml,增加以下三句命令,并重启:

http.cors.enabled: true
http.cors.allow-origin: "*"
network.host: 192.168.88.128

其中: http.cors.enabled: true:此步为允许elasticsearch跨域访问,默认是false。 http.cors.alloworigin: "":表示跨域访问允许的域名地址(表示任意)。

重启

docker restart kkb_es

小提示:如果想让容器开机重启,可以执行下面命令

docker update --restart=always 容器名称或者容器id

五、ElasticSearch的客户端操作

实际开发中,主要有三种方式可以作为elasticsearch服务的客户端:

  • 第一种,elasticsearch-head插件

  • 第二种,使用elasticsearch提供的Restful接口直接访问

  • 第三种,使用elasticsearch提供的API进行访问

1、elasticsearch-head

ElasticSearch不同于Solr自带图形化界面,我们可以通过安装ElasticSearch的head插件,完成图形化 界面的效果,完成索引数据的查看。安装插件的方式有两种,在线安装和本地安装。本文档采用本地安 装方式进行head插件的安装。elasticsearch-5-*以上版本安装head需要安装node和grunt

1)下载head插件:https://github.com/mobz/elasticsearch-head 在资料中已经提供了elasticsearch-head-master插件压缩包:

image-20211212120944618

2)将elasticsearch-head-master压缩包解压到任意目录,但是要和elasticsearch的安装目录区别开 3)下载nodejs:https://nodejs.org/en/download/ 在资料中已经提供了nodejs安装程序: 4)双击安装程序,步骤截图如下: 安装完毕,可以通过cmd控制台输入:node -v 查看版本号

5)将grunt安装为全局命令 ,Grunt是基于Node.js的项目构建工具 在cmd控制台中输入如下执行命令:

npm install -g grunt-cli

执行结果如下图:

image-20211214090123004

6)进入elasticsearch-head-master目录启动head,在命令提示符下输入命令:

>npm install
>grunt server

image-20211214090153195

7)打开浏览器,输入 http://localhost:9100,看到如下页面:

image-20211214090502650

如果不能成功连接到es服务,需要修改ElasticSearch的config目录下的配置文件: config/elasticsearch.yml,增加以下两句命令:

http.cors.enabled: true
http.cors.allow-origin: "*"

然后重新启动ElasticSearch服务。

2、使用Postman工具进行Restful接口访问

①ElasticSearch的接口语法

curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>

其中:

image-20211214094527872

②创建索引index和映射mapping

请求url:

PUT 192.168.88.128:9200/blog1

请求体:

{
  "mappings": {
      "article": {
          "properties": {
              "id": {
                  "type": "long",
                  "store": true,
                  "index":"not_analyzed"
              },
              "title": {
                  "type": "text",
                  "store": true,
                  "index":"analyzed",
                  "analyzer":"standard"
              },
              "content": {
                  "type": "text",
                  "store": true,
                  "index":"analyzed",
                  "analyzer":"standard"
              }
          }
      }
  }
}

post man 截图:

image-20211214094644307

elasticsearch-head查看:

image-20211214094700643

③创建索引后设置Mapping

我们可以在创建索引时设置mapping信息,当然也可以先创建索引然后再设置mapping。

在上一个步骤中不设置maping信息,直接使用put方法创建一个索引,然后设置mapping信息。

请求的url:

POST http://192.168.88.128:9200/blog2/hello/_mapping

请求体:

{
  "hello": {
      "properties": {
          "id":{
              "type":"long",
              "store":true
          },
          "title":{
              "type":"text",
              "store":true,
              "index":true,
              "analyzer":"standard"
          },
          "content":{
              "type":"text",
              "store":true,
              "index":true,
              "analyzer":"standard"
          }
      }
  }
}

PostMan截图

image-20211214095341210

image-20211214095353921

④删除索引index

请求url:

DELETE 192.168.88.128:9200/blog1

postman截图:

image-20211214100405937

elasticsearch-head查看:

image-20211214100423738

⑤创建文档document

请求url:

POST 192.168.88.128:9200/blog1/article/1

请求体:

{
  "id":1,
  "title":"ElasticSearch是一个基于Lucene的搜索服务器",
  "content":"它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。"
}

postman截图:

image-20211214100541154

elasticsearch-head查看:

image-20211214100559080

⑥修改文档document

请求url:

POST 192.168.88.128:9200/blog1/article/1

请求体:

{
  "id":1,
  "title":"【修改】ElasticSearch是一个基于Lucene的搜索服务器",
  "content":"【修改】它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。"
}

postman截图:

image-20211214145622314

elasticsearch-head查看:

image-20211214145639329

⑦删除文档document

请求url:

DELETE 192.168.88.128:9200/blog1/article/1

postman截图:

image-20211214145759299

elasticsearch-head查看:

⑧查询文档-根据id查询

请求url:

GET 192.168.88.128:9200/blog1/article/1

postman截图:

image-20211214151741464

⑨查询文档-querystring查询

请求url:

POST 192.168.88.128:9200/blog1/article/_search

请求体:

{
  "query": {
      "query_string": {
          "default_field": "title",
          "query": "搜索服务器"
      }
  }
}

postman截图:

image-20211214151903394

注意: 将搜索内容"搜索服务器"修改为"钢索",同样也能搜索到文档,该原因会在下面讲解中得到答案

{
  "query": {
      "query_string": {
          "default_field": "title",
          "query": "钢索"
      }
  }
}

钢索 -> "钢", “索”,搜索是分成两个词,注意Standard标准分词器,会把汉字每个字一个词 存到索引库中的tilte,也是按照Standard进行的分词 所以搜索钢索能搜到这个document

⑩查询文档-term查询

请求url:

POST 192.168.88.128:9200/blog1/article/_search

请求体:

{
  "query": {
  "term": {
      "title": "搜索"
      }
  }
}

postman截图:

image-20211214152216202

query_string:搜索之前对搜索的关键词分词 term:对搜索的关键词不分词

六、IK分词器

1、IK分词器简介

IKAnalyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包。从2006年12月推出1.0版开 始,IKAnalyzer已经推出 了3个大版本。最初,它是以开源项目Lucene为应用主体的,结合词典分词和 文法分析算法的中文分词组件。新版本的IKAnalyzer3.0则发展为 面向Java的公用分词组件,独立于 Lucene项目,同时提供了对Lucene的默认优化实现。

IK分词器3.0的特性如下:

1)采用了特有的“正向迭代最细粒度切分算法“,具有60万字/秒的高速处理能力。

2)采用了多子处理 器分析模式,支持:英文字母(IP地址、Email、URL)、数字(日期,常用中文数量词,罗马数字,科 学计数法),中文词汇(姓名、地名处理)等分词处理。

3)对中英联合支持不是很好,在这方面的处理 比较麻烦.需再做一次查询,同时是支持个人词条的优化的词典存储,更小的内存占用。

4)支持用户词 典扩展定义。

5)针对Lucene全文检索优化的查询分析器IKQueryParser;采用歧义分析算法优化查询 关键字的搜索排列组合,能极大的提高Lucene检索的命中率。

2、IK分词器安装

(1)安装ik分词器 IK分词器下载地址https://github.com/medcl/elasticsearch-analysis-ik/releases 将ik分词器上传到服务器上,然后解压,并改名字为ik

unzip elasticsearch-analysis-ik-5.6.8.zip
mv elasticsearch ik

将ik目录拷贝到docker容器的plugins目录下

docker cp ./ik kkb_es:/usr/share/elasticsearch/plugins

(2)重启kkb_es容器,进行IK分词器测试

访问: http://192.168.88.128:9200/_analyze?analyzer=ik_smart&pretty=true&text=我是程序员

image-20211214154610457

访问: http://192.168.88.128:9200/_analyze?analyzer=ik_max_word&pretty=true&text=我是程序员

image-20211214154638399

对比standard分词器,访问: http://192.168.88.128:9200/_analyze?analyzer=standard&pretty=true&text=我是程序员

image-20211214154721664

  • ik_max_word:会将文本做最细粒度的拆分 比如会将“中华人民共和国人民大会堂”拆分为“中华人民共和国、中华人民、中华、华人、人民共和国、人民、共和国、大会堂、大会、会堂等词语。

  • ik_smart:会做最粗粒度的拆分 比如会将“中华人民共和国人民大会堂”拆分为中华人民共和国、人民大会堂。

新建索引blog3,对title和content采用ik_max_word分词方式

image-20211214155049000

对索引blog3创建文档document

image-20211214155224707

查询文档-querystring查询

image-20211214155405917

image-20211214155438815

查询文档-term查询

image-20211214155537180

七、Kibana使用-掌握DSL语句

我们上面使用的是elasticsearch-head插件实现数据查找的,但是elasticsearch-head的功能比较单 一,我们这里需要一个更专业的工具实现对日志的实时分析,也就是我们接下来要讲的kibana。

Kibana 是一款开源的数据分析和可视化平台,它是 Elastic Stack 成员之一,设计用于和 Elasticsearch 协作。您可以使用 Kibana 对 Elasticsearch 索引中的数据进行搜索、查看、交互操作。您可以很方便的 利用图表、表格及地图对数据进行多元化的分析和呈现。

Kibana 可以使大数据通俗易懂。它很简单,基于浏览器的界面便于您快速创建和分享动态数据仪表板 来追踪 Elasticsearch 的实时数据变化。

搭建 Kibana 非常简单。您可以分分钟完成 Kibana 的安装并开始探索 Elasticsearch 的索引数据 — 没 有代码、不需要额外的基础设施。

1、Kibana下载安装

我们项目中不再使用linux,直接使用Docker,所有这里就不演示在windows的下载安装了。

(1)镜像下载

docker pull docker.io/kibana:5.6.8

为了节省时间,虚拟机中已经存在该版本的镜像了. (2)安装kibana容器 执行如下命令,开始安装kibana容器

docker run -it -d -e ELASTICSEARCH_URL=http://192.168.88.128:9200 --name kibana --restart=always --network=host -p 5601:5601 kibana:5.6.8

--restart=always表示当虚拟机启动时自动启动这个容器

ELASTICSEARCH_URL=http://192.168.88.128:9200:是指链接的ES地址 restart=always:每次服务都会重启,也就是开启启动 5601:5601:端口号

快捷键

ctrl+i 自动缩进
ctrl+enter 提交请求
down 打开自动补全菜单
enter或tab 选中项自动补全
esc 关闭补全菜单

(3)访问测试 访问 http://192.168.88.128:5601 如下:

2、Kibana使用

①配置索引

要使用Kibana,您必须至少配置一个索引。索引用于标识Elasticsearch索引以运行搜索和分析。它们还 用于配置字段。

我们修改索引名称的匹配方式即可,下面2个选项不用勾选。点击create,会展示出当前配置的索引的 域信息,如下图:

域的每个标题选项分别代表如下意思:

②数据搜索

Discover为数据搜索部分,可以对日志信息进行搜索操作。

可以使用Discover实现数据搜索过滤和搜索条件显示以及关键词搜索,如下图:

③DSL语句使用

Ⅰ Query DSL结构化查询介绍

Query DSL是一个Java开源框架用于构建类型安全的SQL查询语句。采用API代替传统的拼接字符串来构 造查询语句。目前Querydsl支持的平台包括JPA,JDO,SQL,Java Collections,RDF,Lucene, Hibernate Search。elasticsearch提供了一整套基于JSON的DSL语言来定义查询。

Ⅱ 索引操作

(1)查询所有索引

GET /_cat/indices?v

(2)删除某个索引

DELETE /blog1

(3)新增索引

PUT /user

(4)创建映射

PUT /user/userinfo/_mapping
{
  "properties": {
      "name":{
          "type": "text",
          "analyzer": "ik_smart",
          "search_analyzer": "ik_smart"
      },
      "city":{
          "type": "text",
          "analyzer": "ik_smart",
          "search_analyzer": "ik_smart"
      },
      "age":{
          "type": "long"
      },
      "description":{
          "type": "text",
          "analyzer": "ik_smart",
          "search_analyzer": "ik_smart"
      }
  }
}

(5)新增文档数据

PUT /user/userinfo/1
{
  "name":"李四",
  "age":22,
  "city":"深圳",
  "description":"李四来自湖北武汉!"
}

我们再增加3条记录:

#新增文档数据 id=2
PUT /user/userinfo/2
{
  "name":"王五",
  "age":35,
  "city":"深圳",
  "description":"王五家住在深圳!"
}
#新增文档数据 id=3
PUT /user/userinfo/3
{
  "name":"张三",
  "age":19,
  "city":"深圳",
  "description":"在深圳打工,来自湖北武汉"
}
#新增文档数据 id=4
PUT /user/userinfo/4
{
  "name":"张三丰",
  "age":66,
  "city":"武汉",
  "description":"在武汉读书,家在武汉!"
}
#新增文档数据 id=5
PUT /user/userinfo/5
{
  "name":"赵子龙",
  "age":77,
  "city":"广州",
  "description":"赵子龙来自深圳宝安,但是在广州工作!",
  "address":"广东省茂名市"
}
#新增文档数据 id=6
PUT /user/userinfo/6
{
  "name":"赵毅",
  "age":55,
  "city":"广州",
  "description":"赵毅来自广州白云区,从事电子商务8年!"
}
#新增文档数据 id=7
PUT /user/userinfo/7
{
  "name":"赵哈哈",
  "age":57,
  "city":"武汉",
  "description":"武汉赵哈哈,在深圳打工已有半年了,月薪7500!"
}

(6)修改数据

a.替换操作 更新数据可以使用之前的增加操作,这种操作会将整个数据替换掉,代码如下:

#更新数据,id=4
PUT /user/userinfo/4
{
  "name":"张三丰",
  "description":"在武汉读书,家在武汉!在深圳工作!"
}

使用GET命令查看:

#根据ID查询
GET /user/userinfo/4

b.更新操作 我们先使用下面命令恢复数据:

#恢复文档数据 id=4
PUT /user/userinfo/4
{
  "name":"张三丰",
  "age":66,
  "city":"武汉",
  "description":"在武汉读书,家在武汉!"
}

使用POST更新某个列的数据

#使用POST更新某个域的数据
POST /user/userinfo/4/_update
{
  "doc":{
      "name":"张三丰",
      "description":"在武汉读书,家在武汉!在深圳工作!"
  }
}

使用GET命令查看:

#根据ID查询
GET /user/userinfo/4

(7)删除Document

#删除数据
DELETE user/userinfo/7

Ⅲ 数据查询

(1)查询所有数据

#查询所有
GET /user/_search

(2)根据ID查询

#根据ID查询
GET /user/userinfo/2

(3)Sort排序

#搜索排序
GET /user/_search
{
  "query":{
  "match_all": {}
  },
  "sort":{
      "age":{
      "order":"desc"
      }
  }
}

(4)分页

#分页实现
GET /user/_search
{
  "query":{
  "match_all": {}
},
  "sort":{
      "age":{
          "order":"desc"
      }
  },
  "from": 0,
  "size": 2
}

解释: from:从下N的记录开始查询 size:每页显示条数

Ⅳ 查询模式

(1)term查询 term主要用于分词精确匹配,如字符串、数值、日期等(不适合情况:1.列中除英文字符外有其它值 2.字符串值中有冒号或中文 3.系统自带属性如_version)

如下案例:

#查询-term
GET _search
{
  "query":{
      "term":{
      "city":"武汉"
  }
}

(2)terms 查询 terms 跟 term 有点类似,但 terms 允许指定多个匹配条件。 如果某个字段指定了多个值,那么文档需要一起去做匹配 。 案例如下:

#查询-terms 允许多个Term
GET _search
{
  "query":{
  "terms":{
      "city":
          [
              "武汉",
              "广州"
          ]
      }
  }
}

(3)match查询

GET _search
{
  "query": {
  "match": {
      "city": "广州武汉"
      }
  }
}

(4)query_string查询

GET _search
{
  "query": {
      "query_string": {
          "default_field": "city",
          "query": "广州武汉"
      }
  }
}

(3) range 查询 range过滤允许我们按照指定范围查找一批数据。例如我们查询年龄范围 案例如下:

#-range 范围过滤
#gt表示> gte表示=>
#lt表示< lte表示<=
GET _search
{
  "query":{
      "range": {
          "age": {
              "gte": 30,
              "lte": 57
          }
      }
  }
}

(4)exists exists 过滤可以用于查找拥有某个域的数据 案例如下:

#搜索 exists:是指包含某个域的数据检索
GET _search
{
  "query": {
      "exists":{
          "field":"address"
      }
  }
}

(5) bool 查询 bool 可以用来合并多个条件查询结果的布尔逻辑,它包含一下操作符: must : 多个查询条件的完全匹配,相当于 and。 must_not : 多个查询条件的相反匹配,相当于 not。 should : 至少有一个查询条件匹配, 相当于 or。 这些参数可以分别继承一个过滤条件或者一个过滤条件的数组: 案例如下:

#过滤搜索 bool
#must : 多个查询条件的完全匹配,相当于 and。
#must_not : 多个查询条件的相反匹配,相当于 not。
#should : 至少有一个查询条件匹配, 相当于 or。
GET _search
{
  "query": {
      "bool": {
          "must": [
              {
                  "term": {
                      "city": {
                      "value": "深圳"
                      }
                  }
              },
              {
              "range":{
                      "age":{
                          "gte":20,
                          "lte":99
                  }
                  }
              }
          ]
      }
  }
}

(6) match_all 查询 可以查询到所有文档,是没有查询条件下的默认语句。 案例如下:

#查询所有 match_all
GET _search
{
  "query": {
  "match_all": {}
  }
}

(7) match 查询 match查询是一个标准查询,不管你需要全文本查询还是精确查询基本上都要用到它。 如果你使用 match 查询一个全文本字段,它会在真正查询之前用分析器先分析match一下查询字符: 案例如下:

#字符串匹配
GET _search
{
  "query": {
      "match": {
          "description": "武汉"
      }
  }
}

(8)prefix 查询 以什么字符开头的,可以更简单地用 prefix ,例如查询所有以张开始的用户描述 案例如下:

#前缀匹配 prefix
GET _search
{
  "query": {
      "prefix": {
          "name": {
          "value": "赵"
          }
      }
  }
}

(9)multi_match 查询 multi_match查询允许你做match查询的基础上同时搜索多个字段,在多个字段中同时查一个 案例如下:

#多个域匹配搜索
GET _search
{
  "query": {
      "multi_match": {
          "query": "深圳",
          "fields": [
              "city",
              "description"
          ]
      }
  }
}

Ⅴ 完整DSL语句代码

#查看所有索引
GET /_cat/indices?v
#删除某个索引
DELETE /skuinfo
#新增索引
PUT /user
#创建映射
PUT /user/userinfo/_mapping
{
"properties": {
"name":{
"type": "text",
"analyzer": "ik_smart",
"search_analyzer": "ik_smart",
"store": false
},
"city":{
"type": "text",
"analyzer": "ik_smart",
"search_analyzer": "ik_smart",
"store": false
},
"age":{
"type": "long",
"store": false
},
"description":{
"type": "text",
"analyzer": "ik_smart",
"search_analyzer": "ik_smart",
"store": false
}
}
}
#新增文档数据 id=1
PUT /user/userinfo/1
{
"name":"李四",
"age":22,
"city":"深圳",
"description":"李四来自湖北武汉!"
}
#新增文档数据 id=2
PUT /user/userinfo/2
{
"name":"王五",
"age":35,
"city":"深圳",
"description":"王五家住在深圳!"
}
#新增文档数据 id=3
PUT /user/userinfo/3
{
"name":"张三",
"age":19,
"city":"深圳",
"description":"在深圳打工,来自湖北武汉"
}
#新增文档数据 id=4
PUT /user/userinfo/4
{
"name":"张三丰",
"age":66,
"city":"武汉",
"description":"在武汉读书,家在武汉!"
}
#新增文档数据 id=5
PUT /user/userinfo/5
{
"name":"赵子龙",
"age":77,
"city":"广州",
"description":"赵子龙来自深圳宝安,但是在广州工作!",
"address":"广东省茂名市"
}
#新增文档数据 id=6
PUT /user/userinfo/6
{
"name":"赵毅",
"age":55,
"city":"广州",
"description":"赵毅来自广州白云区,从事电子商务8年!"
}
#新增文档数据 id=7
PUT /user/userinfo/7
{
"name":"赵哈哈",
"age":57,
"city":"武汉",
"description":"武汉赵哈哈,在深圳打工已有半年了,月薪7500!"
}
#更新数据,id=4
PUT /user/userinfo/4
{
"name":"张三丰",
"description":"在武汉读书,家在武汉!在深圳工作!"
}
#根据ID查询
GET /user/userinfo/4
#恢复文档数据 id=4
PUT /user/userinfo/4
{
"name":"张三丰",
"age":66,
"city":"武汉",
"description":"在武汉读书,家在武汉!"
}
#使用POST更新某个域的数据
POST /user/userinfo/4/_update
{
"doc":{
"name":"张三丰",
"description":"在武汉读书,家在武汉!在深圳工作!"
}
}
#根据ID查询
GET /user/userinfo/4
#删除数据
DELETE user/userinfo/4
#查询所有
GET /user/_search
#根据ID查询
GET /user/userinfo/2
#搜索排序
GET /user/_search
{
"query":{
"match_all": {}
},
"sort":{
"age":{
"order":"desc"
}
}
}
#分页实现
GET /user/_search
{
"query":{
"match_all": {}
},
"sort":{
"age":{
"order":"desc"
}
},
"from": 0,
"size": 2
}
#查询-term
GET _search
{
"query":{
"term":{
"city":"武汉"
}
}
}
#查询-terms 允许多个Term
GET _search
{
"query":{
"terms":{
"city":
[
"武汉",
"广州"
]
}
}
}
#match查询
GET _search
{
"query": {
"match": {
"description": "武汉"
}
}
}
#过滤-range 范围过滤
#gt表示> gte表示=>
#lt表示< lte表示<=
GET _search
{
"query":{
"range": {
"age": {
"gte": 30,
"lte": 57
}
}
}
}
#过滤搜索 exists:是指包含某个域的数据检索
GET _search
{
"query": {
"exists":{
"field":"address"
}
}
}
#过滤搜索 bool
#must : 多个查询条件的完全匹配,相当于 and。
#must_not : 多个查询条件的相反匹配,相当于 not
#should : 至少有一个查询条件匹配, 相当于 or。
GET _search
{
"query": {
"bool": {
"must": [
{
"term": {
"city": {
"value": "深圳"
}
}
},
{
"range":{
"age":{
"gte":20,
"lte":99
}
}
}
]
}
}
}
#查询所有 match_all
GET _search
{
"query": {
"match_all": {}
}
}
#字符串匹配
GET _search
{
"query": {
"match": {
"description": "武汉"
}
}
}
#前缀匹配 prefix
GET _search
{
"query": {
"prefix": {
"name": {
"value": "赵"
}
}
}
}
#多个域匹配搜索
GET _search
{
"query": {
"multi_match": {
"query": "深圳",
"fields": [
"city",
"description"
]
}
}
}
#过滤-range 范围过滤
#gt表示> gte表示=>
#lt表示< lte表示<=
GET _search
{
"query": {
"bool": {
"must": [
{"match": {
"city": "深圳武汉"
}}
],
"filter": {
"range": {
"age": {
"gte": 20,
"lte": 60
}
}
}
}
}
}

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

八、ElasticSearch编程操作

1、创建工程,导入坐标

pom.xml坐标

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>

   <groupId>com.lxs</groupId>
   <artifactId>es-demo</artifactId>
   <version>1.0-SNAPSHOT</version>
   <dependencies>
       <dependency>
           <groupId>org.elasticsearch</groupId>
           <artifactId>elasticsearch</artifactId>
           <version>5.6.8</version>
       </dependency>
       <dependency>
           <groupId>org.elasticsearch.client</groupId>
           <artifactId>transport</artifactId>
           <version>5.6.8</version>
       </dependency>
       <dependency>
           <groupId>org.apache.logging.log4j</groupId>
           <artifactId>log4j-to-slf4j</artifactId>
           <version>2.9.1</version>
       </dependency>
       <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-api</artifactId>
           <version>1.7.24</version>
       </dependency>
       <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-simple</artifactId>
           <version>1.7.21</version>
       </dependency>
       <dependency>
           <groupId>log4j</groupId>
           <artifactId>log4j</artifactId>
           <version>1.2.12</version>
       </dependency>
       <dependency>
           <groupId>junit</groupId>
           <artifactId>junit</artifactId>
           <version>4.12</version>
       </dependency>
   </dependencies>

</project>

2、创建索引index

package com.lxs.es;

import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.junit.Test;

import java.net.InetAddress;

public class ElasticSearchClientTest {
   @Test
   //创建索引
   public void test1() throws Exception{
       // 创建Client连接对象
       Settings settings = Settings.builder().put("cluster.name", "elasticsearch").build();
       TransportClient client = new PreBuiltTransportClient(settings).addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("192.168.88.128"), 9300));
       //创建名称为blog2的索引
       client.admin().indices().prepareCreate("blog1").get();
       //释放资源
       client.close();
  }
}

image-20211215092916895

3、创建映射mapping

package com.lxs.es;

import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.client.Requests;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.junit.Test;

import java.net.InetAddress;

public class ElasticSearchClientTest {
   @Test
   //创建索引
   public void test1() throws Exception {
       // 创建Client连接对象
       Settings settings = Settings.builder().put("cluster.name", "elasticsearch").build();
       TransportClient client = new PreBuiltTransportClient(settings).addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("192.168.88.128"), 9300));
       //创建名称为blog2的索引
       client.admin().indices().prepareCreate("blog1").get();
       //释放资源
       client.close();
  }

   @Test
   //创建映射
   public void test3() throws Exception {
       // 创建Client连接对象
       Settings settings = Settings.builder().put("cluster.name", "elasticsearch").build();
       TransportClient client = new PreBuiltTransportClient(settings).addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("192.168.88.128"), 9300));
       // 添加映射
       /**
        * 格式:
        * "mappings" : {
        "article" : {
        "dynamic" : "false",
        "properties" : {
        "id" : { "type" : "string" },
        "content" : { "type" : "string" },
        "author" : { "type" : "string" }
        }
        }
        }
        */
       XContentBuilder builder = XContentFactory.jsonBuilder()
              .startObject()
              .startObject("article")
              .startObject("properties")
              .startObject("id")
              .field("type", "integer").field("store", "yes")
              .endObject()
              .startObject("title")
              .field("type", "string").field("store", "yes").field("analyzer",
                       "ik_smart")
              .endObject()
              .startObject("content")
              .field("type", "string").field("store", "yes").field("analyzer",
                       "ik_smart")
              .endObject()
              .endObject()
              .endObject()
              .endObject();
       // 创建映射
       PutMappingRequest mapping = Requests.putMappingRequest("blog1").type("article").source(builder);
       client.admin().indices().putMapping(mapping).get();
       //释放资源
       client.close();
  }
}

image-20211215093349990

image-20211215093404862

4、建立文档document

①建立文档(通过XContentBuilder)

package com.lxs.es;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.junit.Before;
import org.junit.Test;

import java.net.InetAddress;

public class ElasticSearchClientTest {

   TransportClient client;

   @Before
   public void init() throws Exception {
       //1:配置
       Settings settings = Settings.builder().put("cluster.name", "elasticsearch").build();
       //2:客户端
       client = new PreBuiltTransportClient(settings);
       client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("192.168.88.128"), 9300));
  }

   @Test
   public void createIndex() throws Exception {
       client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("192.168.88.128"), 9300));
       //3使用api创建索引
       client.admin().indices().prepareCreate("index_hello2").get();

       //4:关闭client
       client.close();
  }


   @Test
   public void setMappings() throws Exception {
       XContentBuilder builder = XContentFactory.jsonBuilder()
              .startObject()
              .startObject("article")
              .startObject("properties")
              .startObject("id")
              .field("type", "integer").field("store", "yes")
              .endObject()
              .startObject("title")
              .field("type", "string").field("store", "yes").field("analyzer", "ik_smart")
              .endObject()
              .startObject("content")
              .field("type", "string").field("store", "yes").field("analyzer", "ik_smart")
              .endObject()
              .endObject()
              .endObject()
              .endObject();

       //3使用api创建索引
       client.admin().indices().preparePutMapping("index_hello2").setType("article").setSource(builder).get();

       //4:关闭client
       client.close();
  }

   /**
    * 使用XContentBuilder构建Document对象
    * @throws Exception
    */
   @Test
   public void testAddDocument() throws Exception {
       //创建一个client对象
       //创建一个文档对象
       XContentBuilder builder = XContentFactory.jsonBuilder()
              .startObject()
              .field("id",2l)
              .field("title","北方入秋速度明显加快 多地降温幅度最多可达10度22222")
              .field("content", "阿联酋一架客机在纽约机场被隔离 10名乘客病倒")
              .endObject();
       //把文档对象添加到索引库
       client.prepareIndex()
               //设置索引名称
              .setIndex("index_hello")
               //设置type
              .setType("article")
               //设置文档的id,如果不设置的话自动的生成一个id
              .setId("2")
               //设置文档信息
              .setSource(builder)
               //执行操作
              .get();
       //关闭客户端
       client.close();
  }
}

image-20211215100348577

②建立文档(使用Jackson转换实体)

1)创建Article实体

package com.lxs.es;

public class Article {
   private Integer id;
   private String title;
   private String content;

   public Integer getId() {
       return id;
  }

   public void setId(Integer id) {
       this.id = id;
  }

   public String getTitle() {
       return title;
  }

   public void setTitle(String title) {
       this.title = title;
  }

   public String getContent() {
       return content;
  }

   public void setContent(String content) {
       this.content = content;
  }
}

2)添加jackson坐标

<dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-core</artifactId>
   <version>2.8.1</version>
</dependency>
<dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-databind</artifactId>
   <version>2.8.1</version>
</dependency>
<dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-annotations</artifactId>
   <version>2.8.1</version>
</dependency>

3)代码实现

package com.lxs.es;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.junit.Before;
import org.junit.Test;

import java.net.InetAddress;

public class ElasticSearchClientTest {

   TransportClient client;

   @Before
   public void init() throws Exception {
       //1:配置
       Settings settings = Settings.builder().put("cluster.name", "elasticsearch").build();
       //2:客户端
       client = new PreBuiltTransportClient(settings);
       client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("192.168.88.128"), 9300));
  }

   @Test
   public void createIndex() throws Exception {
       client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("192.168.88.128"), 9300));
       //3使用api创建索引
       client.admin().indices().prepareCreate("index_hello2").get();

       //4:关闭client
       client.close();
  }


   @Test
   public void setMappings() throws Exception {
       XContentBuilder builder = XContentFactory.jsonBuilder()
              .startObject()
              .startObject("article")
              .startObject("properties")
              .startObject("id")
              .field("type", "integer").field("store", "yes")
              .endObject()
              .startObject("title")
              .field("type", "string").field("store", "yes").field("analyzer", "ik_smart")
              .endObject()
              .startObject("content")
              .field("type", "string").field("store", "yes").field("analyzer", "ik_smart")
              .endObject()
              .endObject()
              .endObject()
              .endObject();

       //3使用api创建索引
       client.admin().indices().preparePutMapping("index_hello2").setType("article").setSource(builder).get();

       //4:关闭client
       client.close();
  }

   /**
    * 使用XContentBuilder构建Document对象
    * @throws Exception
    */
   @Test
   public void testAddDocument() throws Exception {
       //创建一个client对象
       //创建一个文档对象
       XContentBuilder builder = XContentFactory.jsonBuilder()
              .startObject()
              .field("id",2l)
              .field("title","北方入秋速度明显加快 多地降温幅度最多可达10度22222")
              .field("content", "阿联酋一架客机在纽约机场被隔离 10名乘客病倒")
              .endObject();
       //把文档对象添加到索引库
       client.prepareIndex()
               //设置索引名称
              .setIndex("index_hello")
               //设置type
              .setType("article")
               //设置文档的id,如果不设置的话自动的生成一个id
              .setId("2")
               //设置文档信息
              .setSource(builder)
               //执行操作
              .get();
       //关闭客户端
       client.close();
  }


   @Test
   public void testAddDocument2() throws Exception {
       //创建一个Article对象
       Article article = new Article();
       //设置对象的属性
       article.setId(3);
       article.setTitle("MH370坠毁在柬埔寨密林?中国一公司调十颗卫星去拍摄");
       article.setContent("警惕荒唐的死亡游戏!俄15岁少年输掉游戏后用电锯自杀");
       ObjectMapper objectMapper = new ObjectMapper();
       String json = objectMapper.writeValueAsString(article);

       //把文档对象添加到索引库
       client.prepareIndex()
               //设置索引名称
              .setIndex("index_hello")
               //设置type
              .setType("article")
               //设置文档的id,如果不设置的话自动的生成一个id
              .setId("3")
               //设置文档信息
              .setSource(json, XContentType.JSON)
               //执行操作
              .get();
       //关闭客户端
       client.close();
  }
}

image-20211215100528321

@Test
public void testAddDocument3() throws Exception {
   for (int i = 4; i < 100; i++) {
       //创建一个Article对象
       Article article = new Article();
       //设置对象的属性
       article.setId(i);
       article.setTitle("女护士路遇昏迷男子跪地抢救:救人是职责更是本能" + i);
       article.setContent("这是一个美丽的女护士妹妹" + i);
       //把article对象转换成json格式的字符串。
       ObjectMapper objectMapper = new ObjectMapper();
       String jsonDocument = objectMapper.writeValueAsString(article);
       System.out.println(jsonDocument);
       //使用client对象把文档写入索引库
       client.prepareIndex("index_hello","article", i + "")
              .setSource(jsonDocument, XContentType.JSON)
              .get();

  }
   //关闭客户端
   client.close();
}

5、查询文档操作

①termQuery

package com.lxs.es;

import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.junit.Before;
import org.junit.Test;

import java.net.InetAddress;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;

public class IndexSearch {

   TransportClient client;

   @Before
   public void init() throws Exception {
       //1:配置
       Settings settings = Settings.builder().put("cluster.name", "elasticsearch").build();
       //2:客户端
       client = new PreBuiltTransportClient(settings);
       client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("192.168.88.128"), 9300));
  }
   @Test
   public void testQueryByTeam() throws Exception {
       //构件queryBuilder
       QueryBuilder queryBuilder = QueryBuilders.termQuery("title", "女护士");
       search(queryBuilder);
  }
}

②queryString

package com.lxs.es;

import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.junit.Before;
import org.junit.Test;

import java.net.InetAddress;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;

public class IndexSearch {

   TransportClient client;

   @Before
   public void init() throws Exception {
       //1:配置
       Settings settings = Settings.builder().put("cluster.name", "elasticsearch").build();
       //2:客户端
       client = new PreBuiltTransportClient(settings);
       client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("192.168.88.128"), 9300));
  }
   @Test
   public void testQueryByTeam() throws Exception {
       //构件queryBuilder
       QueryBuilder queryBuilder = QueryBuilders.termQuery("title", "女护士");
       search(queryBuilder);
  }


   @Test
   public void testQueryByQueryString() throws Exception {
       //构件queryBuilder
       QueryBuilder queryBuilder = QueryBuilders.queryStringQuery("美丽的女护士").defaultField("title");
       //执行查询得到
       search(queryBuilder);
  }
}

③MatchQuery

package com.lxs.es;

import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.junit.Before;
import org.junit.Test;

import java.net.InetAddress;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;

public class IndexSearch {

   TransportClient client;

   @Before
   public void init() throws Exception {
       //1:配置
       Settings settings = Settings.builder().put("cluster.name", "elasticsearch").build();
       //2:客户端
       client = new PreBuiltTransportClient(settings);
       client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("192.168.88.128"), 9300));
  }
   @Test
   public void testQueryByTeam() throws Exception {
       //构件queryBuilder
       QueryBuilder queryBuilder = QueryBuilders.termQuery("title", "女护士");
       search(queryBuilder);
  }


   @Test
   public void testQueryByQueryString() throws Exception {
       //构件queryBuilder
       QueryBuilder queryBuilder = QueryBuilders.queryStringQuery("美丽的女护士").defaultField("title");
       //执行查询得到
       search(queryBuilder);
  }


   @Test
   public void testQueryByMathQuery() throws Exception {
       //构件queryBuilder
       QueryBuilder queryBuilder = QueryBuilders.matchQuery("title", "美丽的女护士");
       //执行查询得到
       search(queryBuilder);
  }
}

④重构查询方法

package com.lxs.es;

import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.junit.Before;
import org.junit.Test;

import java.net.InetAddress;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;

public class IndexSearch {

   TransportClient client;

   @Before
   public void init() throws Exception {
       //1:配置
       Settings settings = Settings.builder().put("cluster.name", "elasticsearch").build();
       //2:客户端
       client = new PreBuiltTransportClient(settings);
       client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("192.168.88.128"), 9300));
  }

   private void search(QueryBuilder queryBuilder) {
       //执行查询得到
       SearchResponse searchResponse = client.prepareSearch("index_hello")
              .setTypes("article")
              .setQuery(queryBuilder)
              .get();

       //处理结果
       SearchHits searchHits = searchResponse.getHits();
       System.out.println("总行数:" + searchHits.getTotalHits());
       Iterator<SearchHit> it = searchHits.iterator();
       while (it.hasNext()) {
           SearchHit searchHit = it.next();
           //source->document的json输出
           System.out.println(searchHit.getSourceAsString());
           System.out.println("---文档属性-----");
           Map<String, Object> document = searchHit.getSource();
           System.out.println(document.get("id"));
           System.out.println(document.get("title"));
           System.out.println(document.get("content"));
      }
  }

   @Test
   public void testQueryByTeam() throws Exception {
       //构件queryBuilder
       QueryBuilder queryBuilder = QueryBuilders.termQuery("title", "女护士");
       search(queryBuilder);
  }


   @Test
   public void testQueryByQueryString() throws Exception {
       //构件queryBuilder
       QueryBuilder queryBuilder = QueryBuilders.queryStringQuery("美丽的女护士").defaultField("title");
       //执行查询得到
       search(queryBuilder);
  }


   @Test
   public void testQueryByMathQuery() throws Exception {
       //构件queryBuilder
       QueryBuilder queryBuilder = QueryBuilders.matchQuery("title", "美丽的女护士");
       //执行查询得到
       search(queryBuilder);
  }
}

⑤使用文档ID查询文档

package com.lxs.es;

import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.junit.Before;
import org.junit.Test;

import java.net.InetAddress;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;

public class IndexSearch {

   TransportClient client;

   @Before
   public void init() throws Exception {
       //1:配置
       Settings settings = Settings.builder().put("cluster.name", "elasticsearch").build();
       //2:客户端
       client = new PreBuiltTransportClient(settings);
       client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("192.168.88.128"), 9300));
  }

   private void search(QueryBuilder queryBuilder) {
       //执行查询得到
       SearchResponse searchResponse = client.prepareSearch("index_hello")
              .setTypes("article")
              .setQuery(queryBuilder)
              .get();

       //处理结果
       SearchHits searchHits = searchResponse.getHits();
       System.out.println("总行数:" + searchHits.getTotalHits());
       Iterator<SearchHit> it = searchHits.iterator();
       while (it.hasNext()) {
           SearchHit searchHit = it.next();
           //source->document的json输出
           System.out.println(searchHit.getSourceAsString());
           System.out.println("---文档属性-----");
           Map<String, Object> document = searchHit.getSource();
           System.out.println(document.get("id"));
           System.out.println(document.get("title"));
           System.out.println(document.get("content"));
      }
  }

   @Test
   public void testQueryByTeam() throws Exception {
       //构件queryBuilder
       QueryBuilder queryBuilder = QueryBuilders.termQuery("title", "女护士");
       search(queryBuilder);
  }


   @Test
   public void testQueryByQueryString() throws Exception {
       //构件queryBuilder
       QueryBuilder queryBuilder = QueryBuilders.queryStringQuery("美丽的女护士").defaultField("title");
       //执行查询得到
       search(queryBuilder);
  }


   @Test
   public void testQueryByMathQuery() throws Exception {
       //构件queryBuilder
       QueryBuilder queryBuilder = QueryBuilders.matchQuery("title", "美丽的女护士");
       //执行查询得到
       search(queryBuilder);
  }

   @Test
   public void testSearchById() throws Exception {
       //创建一个client对象
       //创建一个查询对象
       QueryBuilder queryBuilder = QueryBuilders.idsQuery().addIds("1", "2");
       search(queryBuilder);
  }

   @Test
   public void testSearchByMathAll() throws Exception {
       //创建一个client对象
       //创建一个查询对象
       QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();

       //执行查询得到
       SearchResponse searchResponse = client.prepareSearch("index_hello")
              .setTypes("article")
              .setQuery(queryBuilder)
              .setFrom(0)
              .setSize(5)
              .get();

       //处理结果
       SearchHits searchHits = searchResponse.getHits();
       System.out.println("总行数:" + searchHits.getTotalHits());
       Iterator<SearchHit> it = searchHits.iterator();
       while (it.hasNext()) {
           SearchHit searchHit = it.next();
           //source->document的json输出
           System.out.println(searchHit.getSourceAsString());
           System.out.println("---文档属性-----");
           Map<String, Object> document = searchHit.getSource();
           System.out.println(document.get("id"));
           System.out.println(document.get("title"));
           System.out.println(document.get("content"));
      }

  }
}

⑥查询文档分页操作

package com.lxs.es;

import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.junit.Before;
import org.junit.Test;

import java.net.InetAddress;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;

public class IndexSearch {

   TransportClient client;

   @Before
   public void init() throws Exception {
       //1:配置
       Settings settings = Settings.builder().put("cluster.name", "elasticsearch").build();
       //2:客户端
       client = new PreBuiltTransportClient(settings);
       client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("192.168.88.128"), 9300));
  }

   private void search(QueryBuilder queryBuilder) throws Exception {
       //执行查询
       SearchResponse searchResponse = client.prepareSearch("index_hello")
              .setTypes("article")
              .setQuery(queryBuilder)
               //设置分页信息
              .setFrom(0)
               //每页显示的行数
              .setSize(5)
              .get();
       //取查询结果
       SearchHits searchHits = searchResponse.getHits();
       //取查询结果的总记录数
       System.out.println("查询结果总记录数:" + searchHits.getTotalHits());
       //查询结果列表
       Iterator<SearchHit> iterator = searchHits.iterator();
       while(iterator.hasNext()) {
           SearchHit searchHit = iterator.next();
           //打印文档对象,以json格式输出
           System.out.println(searchHit.getSourceAsString());
           //取文档的属性
           System.out.println("-----------文档的属性");
           Map<String, Object> document = searchHit.getSource();
           System.out.println(document.get("id"));
           System.out.println(document.get("title"));
           System.out.println(document.get("content"));
      }
       //关闭client
       client.close();
  }

   @Test
   public void testQueryByTeam() throws Exception {
       //构件queryBuilder
       QueryBuilder queryBuilder = QueryBuilders.termQuery("title", "女护士");
       search(queryBuilder);
  }


   @Test
   public void testQueryByQueryString() throws Exception {
       //构件queryBuilder
       QueryBuilder queryBuilder = QueryBuilders.queryStringQuery("美丽的女护士").defaultField("title");
       //执行查询得到
       search(queryBuilder);
  }


   @Test
   public void testQueryByMathQuery() throws Exception {
       //构件queryBuilder
       QueryBuilder queryBuilder = QueryBuilders.matchQuery("title", "美丽的女护士");
       //执行查询得到
       search(queryBuilder);
  }

   @Test
   public void testSearchById() throws Exception {
       //创建一个client对象
       //创建一个查询对象
       QueryBuilder queryBuilder = QueryBuilders.idsQuery().addIds("1", "2");
       search(queryBuilder);
  }

   @Test
   public void testSearchByMathAll() throws Exception {
       //创建一个client对象
       //创建一个查询对象
       QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();

       //执行查询得到
       SearchResponse searchResponse = client.prepareSearch("index_hello")
              .setTypes("article")
              .setQuery(queryBuilder)
              .setFrom(0)
              .setSize(5)
              .get();

       //处理结果
       SearchHits searchHits = searchResponse.getHits();
       System.out.println("总行数:" + searchHits.getTotalHits());
       Iterator<SearchHit> it = searchHits.iterator();
       while (it.hasNext()) {
           SearchHit searchHit = it.next();
           //source->document的json输出
           System.out.println(searchHit.getSourceAsString());
           System.out.println("---文档属性-----");
           Map<String, Object> document = searchHit.getSource();
           System.out.println(document.get("id"));
           System.out.println(document.get("title"));
           System.out.println(document.get("content"));
      }

  }


   @Test
   public void testSearchByHighlight() throws Exception {
       //创建一个查询对象
       QueryBuilder queryBuilder = QueryBuilders.multiMatchQuery("北京市民", "title", "content");

       HighlightBuilder highlightBuilder = new HighlightBuilder();
       highlightBuilder.field("title");
       highlightBuilder.field("content");
       highlightBuilder.preTags("<em>");
       highlightBuilder.postTags("</em>");

       //执行查询得到
       SearchResponse searchResponse = client.prepareSearch("index_hello")
              .setTypes("article")
              .setQuery(queryBuilder)
              .highlighter(highlightBuilder)
              .get();

       //处理结果
       SearchHits searchHits = searchResponse.getHits();
       System.out.println("总行数:" + searchHits.getTotalHits());
       Iterator<SearchHit> it = searchHits.iterator();
       while (it.hasNext()) {
           SearchHit searchHit = it.next();
           //source->document的json输出
           System.out.println("-----文档内容-------");
           System.out.println(searchHit.getSourceAsString());
           System.out.println("----高亮结果-----");
           Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
           for (Map.Entry<String, HighlightField> entry : highlightFields.entrySet()) {
               System.out.println(entry.getKey() + ":\t" + Arrays.toString(entry.getValue().getFragments()));
          }
      }

  }
}

image-20211215102539032

⑦查询结果高亮操作

Ⅰ 什么是高亮显示

在进行关键字搜索时,搜索出的内容中的关键字会显示不同的颜色,称之为高亮 百度搜索关键字"开课吧"

image-20211215103251501

Ⅱ 高亮显示的html分析

通过开发者工具查看高亮数据的html代码实现:

image-20211215103323228

ElasticSearch可以对查询出的内容中关键字部分进行标签和样式的设置,但是你需要告诉ElasticSearch 使用什么标签对高亮关键字进行包裹

Ⅲ 高亮显示代码实现

package com.lxs.es;

import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.junit.Before;
import org.junit.Test;

import java.net.InetAddress;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;

public class IndexSearch {

   TransportClient client;

   @Before
   public void init() throws Exception {
       //1:配置
       Settings settings = Settings.builder().put("cluster.name", "elasticsearch").build();
       //2:客户端
       client = new PreBuiltTransportClient(settings);
       client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("192.168.88.128"), 9300));
  }

   private void search(QueryBuilder queryBuilder) throws Exception {
       //执行查询
       SearchResponse searchResponse = client.prepareSearch("index_hello")
              .setTypes("article")
              .setQuery(queryBuilder)
               //设置分页信息
              .setFrom(0)
               //每页显示的行数
              .setSize(5)
              .get();
       //取查询结果
       SearchHits searchHits = searchResponse.getHits();
       //取查询结果的总记录数
       System.out.println("查询结果总记录数:" + searchHits.getTotalHits());
       //查询结果列表
       Iterator<SearchHit> iterator = searchHits.iterator();
       while(iterator.hasNext()) {
           SearchHit searchHit = iterator.next();
           //打印文档对象,以json格式输出
           System.out.println(searchHit.getSourceAsString());
           //取文档的属性
           System.out.println("-----------文档的属性");
           Map<String, Object> document = searchHit.getSource();
           System.out.println(document.get("id"));
           System.out.println(document.get("title"));
           System.out.println(document.get("content"));
      }
       //关闭client
       client.close();
  }

   @Test
   public void testQueryByTeam() throws Exception {
       //构件queryBuilder
       QueryBuilder queryBuilder = QueryBuilders.termQuery("title", "女护士");
       search(queryBuilder);
  }


   @Test
   public void testQueryByQueryString() throws Exception {
       //构件queryBuilder
       QueryBuilder queryBuilder = QueryBuilders.queryStringQuery("美丽的女护士").defaultField("title");
       //执行查询得到
       search(queryBuilder);
  }


   @Test
   public void testQueryByMathQuery() throws Exception {
       //构件queryBuilder
       QueryBuilder queryBuilder = QueryBuilders.matchQuery("title", "美丽的女护士");
       //执行查询得到
       search(queryBuilder);
  }

   @Test
   public void testSearchById() throws Exception {
       //创建一个client对象
       //创建一个查询对象
       QueryBuilder queryBuilder = QueryBuilders.idsQuery().addIds("1", "2");
       search(queryBuilder);
  }

   @Test
   public void testSearchByMathAll() throws Exception {
       //创建一个client对象
       //创建一个查询对象
       QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();

       //执行查询得到
       SearchResponse searchResponse = client.prepareSearch("index_hello")
              .setTypes("article")
              .setQuery(queryBuilder)
              .setFrom(0)
              .setSize(5)
              .get();

       //处理结果
       SearchHits searchHits = searchResponse.getHits();
       System.out.println("总行数:" + searchHits.getTotalHits());
       Iterator<SearchHit> it = searchHits.iterator();
       while (it.hasNext()) {
           SearchHit searchHit = it.next();
           //source->document的json输出
           System.out.println(searchHit.getSourceAsString());
           System.out.println("---文档属性-----");
           Map<String, Object> document = searchHit.getSource();
           System.out.println(document.get("id"));
           System.out.println(document.get("title"));
           System.out.println(document.get("content"));
      }

  }


   @Test
   public void testSearchByHighlight() throws Exception {
       //创建一个查询对象
       QueryBuilder queryBuilder = QueryBuilders.multiMatchQuery("北京市民", "title", "content");

       HighlightBuilder highlightBuilder = new HighlightBuilder();
       highlightBuilder.field("title");
       highlightBuilder.field("content");
       highlightBuilder.preTags("<em>");
       highlightBuilder.postTags("</em>");

       //执行查询得到
       SearchResponse searchResponse = client.prepareSearch("index_hello")
              .setTypes("article")
              .setQuery(queryBuilder)
              .highlighter(highlightBuilder)
              .get();

       //处理结果
       SearchHits searchHits = searchResponse.getHits();
       System.out.println("总行数:" + searchHits.getTotalHits());
       Iterator<SearchHit> it = searchHits.iterator();
       while (it.hasNext()) {
           SearchHit searchHit = it.next();
           //source->document的json输出
           System.out.println("-----文档内容-------");
           System.out.println(searchHit.getSourceAsString());
           System.out.println("----高亮结果-----");
           Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
           for (Map.Entry<String, HighlightField> entry : highlightFields.entrySet()) {
               System.out.println(entry.getKey() + ":\t" + Arrays.toString(entry.getValue().getFragments()));
          }
      }

  }
}

image-20211215103409977

九、Spring Data ElasticSearch

1、简介

①Spring Data

Spring Data是一个用于简化数据库访问,并支持云服务的开源框架。其主要目标是使得对数据的访问 变得方便快捷,并支持map-reduce框架和云计算数据服务。 Spring Data可以极大的简化JPA的写法, 可以在几乎不用写实现的情况下,实现对数据的访问和操作。除了CRUD外,还包括如分页、排序等一 些常用的功能。

Spring Data的官网:http://projects.spring.io/spring-data/ Spring Data常用的功能模块如下:

image-20211215104056327

②Spring Data ElasticSearch

Spring Data ElasticSearch 基于 spring data API 简化 elasticSearch操作,将原始操作elasticSearch的 客户端API 进行封装 。Spring Data为Elasticsearch项目提供集成搜索引擎。Spring Data Elasticsearch POJO的关键功能区域为中心的模型与Elastichsearch交互文档和轻松地编写一个存储库数据访问层。

官方网站:http://projects.spring.io/spring-data-elasticsearch/

2、入门案例

image-20211215104220871

image-20211215104330163

image-20211215104446284

编辑application.yml

spring:
data:
  elasticsearch:
    cluster-name: elasticsearch
    cluster-nodes: 192.168.88.128:9300

编写实体Article

package com.lxs.esdemo2.domain;

import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

@Document(indexName = "lxs_blog", type = "article")
public class Article {

   @Id
   @Field(type = FieldType.Long, store = true)
   private Long id;
   @Field(type = FieldType.Text, store = true, analyzer = "ik_smart")
   private String title;
   @Field(type = FieldType.Text, store = true, analyzer = "ik_smart")
   private String content;

   public Long getId() {
       return id;
  }

   public void setId(Long id) {
       this.id = id;
  }

   public String getTitle() {
       return title;
  }

   public void setTitle(String title) {
       this.title = title;
  }

   public String getContent() {
       return content;
  }

   public void setContent(String content) {
       this.content = content;
  }

   @Override
   public String toString() {
       return "Article{" +
               "id=" + id +
               ", title='" + title + '\'' +
               ", content='" + content + '\'' +
               '}';
  }
}

4)编写Dao 方法命名规则查询的基本语法findBy + 属性 + 关键词 + 连接符

image-20211215152640271

package com.lxs.esdemo2.dao;

import com.lxs.esdemo2.domain.Article;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

import java.util.List;

public interface ArticleDao extends ElasticsearchRepository<Article, Long> {

   public List<Article> findByTitleLike(String title);

   public List<Article> findByTitle(String title);

   public List<Article> findByTitleLikeOrContent(String title, String content);

   public List<Article> findByTitleOrContent(String title, String content, Pageable pageable);

}

5)创建测试类

package com.lxs.esdemo2;


import com.lxs.esdemo2.dao.ArticleDao;
import com.lxs.esdemo2.domain.Article;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
public class EsDemo2ApplicationTests {

   @Autowired
   private ArticleDao dao;

   @Autowired
   private ElasticsearchTemplate template;

   @Test
   public void createIndex() {
//       template.createIndex(Article.class);
       //配置mapping
       template.putMapping(Article.class);
  }

   @Test
   public void addDocument() throws Exception {
       for (int i = 1; i <= 20; i++) {
           //创建一个Article对象
           Article article = new Article();
           article.setId(new Long(i));
           article.setTitle("女护士路遇昏迷男子跪地抢救:救人是职责更是本能" + i);
           article.setContent("这是一个美丽的女护士妹妹" + i);
           //把文档写入索引库
           dao.save(article);
      }
  }

   @Test
   public void deleteDocumentById() throws Exception {
       dao.deleteById(3l);
       //全部删除
//       articleRepository.deleteAll();
  }

   @Test
   public void findAll() {
       dao.findAll().forEach(System.out :: println);
  }

   @Test
   public void findById() {
       System.out.println(dao.findById(1l));
  }

   @Test
   public void findByTitle() {
       dao.findByTitleLike("美丽女护士").forEach(System.out :: println);
  }

   @Test
   public void findByTitleOrContent() {
       dao.findByTitleLikeOrContent("美丽女护士", "男护士").forEach(System.out :: println);
  }

   @Test
   public void findByTitlePage() {
       Pageable pageable = PageRequest.of(1, 5);
       dao.findByTitleOrContent("女护士","女护士", pageable).forEach(System.out :: println);
  }

   @Test
   public void testNativeSearchQuery() throws  Exception {
       NativeSearchQuery query = new NativeSearchQueryBuilder()
              .withQuery(QueryBuilders.queryStringQuery("女护士").defaultField("title"))
              .withPageable(PageRequest.of(1, 5))
              .build();
       template.queryForList(query, Article.class).forEach(System.out :: println);
  }
}

3、聚合查询

实体类

package com.lxs.esdemo2.domain;

import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

@Document(indexName = "car_index", type = "car")
public class Car {

   @Id
   @Field(type = FieldType.Long, store = true)
   private Long id;
   @Field(type = FieldType.Text, store = true, analyzer = "ik_smart")
   private String name;
   @Field(type = FieldType.Text, store = true, analyzer = "ik_smart", fielddata = true)
   private String brand;
   @Field(type = FieldType.Text, store = true, analyzer = "ik_smart", fielddata = true)
   private String color;
   @Field(type = FieldType.Double, store = true)
   private Double price;

   public Car(Long id, String name,  String color, String brand, Double price) {
       this.id = id;
       this.name = name;
       this.brand = brand;
       this.color = color;
       this.price = price;
  }

   public Car() {
  }

   public Long getId() {
       return id;
  }

   public void setId(Long id) {
       this.id = id;
  }

   public String getName() {
       return name;
  }

   public void setName(String name) {
       this.name = name;
  }

   public String getBrand() {
       return brand;
  }

   public void setBrand(String brand) {
       this.brand = brand;
  }

   public String getColor() {
       return color;
  }

   public void setColor(String color) {
       this.color = color;
  }

   public Double getPrice() {
       return price;
  }

   public void setPrice(Double price) {
       this.price = price;
  }
}

Dao

package com.lxs.esdemo2.dao;

import com.lxs.esdemo2.domain.Car;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

public interface CarDao extends ElasticsearchRepository<Car, Long> {

}

初始化索引库

package com.lxs.esdemo2.dao;

import com.lxs.esdemo2.domain.Car;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.aggregations.metrics.avg.InternalAvg;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

import static org.junit.Assert.*;

@RunWith(SpringRunner.class)
@SpringBootTest
public class CarDaoTest {

   @Autowired
   private CarDao carDao;

   @Test
   public void initIndex() {
       carDao.save(new Car(1l, "比亚迪A1", "红色", "比亚迪", 50000d));
       carDao.save(new Car(2l, "比亚迪A2", "白色", "比亚迪", 70000d));
       carDao.save(new Car(3l, "比亚迪A3", "白色", "比亚迪", 80000d));
       carDao.save(new Car(4l, "比亚迪A4", "红色", "比亚迪", 60000d));
       carDao.save(new Car(5l, "比亚迪A5", "红色", "比亚迪", 90000d));

       carDao.save(new Car(6l, "宝马A1", "红色", "宝马", 10000d));
       carDao.save(new Car(7l, "宝马A2", "黑色", "宝马", 20000d));
       carDao.save(new Car(8l, "宝马A3", "黑色", "宝马", 30000d));
       carDao.save(new Car(9l, "宝马A4", "红色", "宝马", 40000d));
       carDao.save(new Car(10l, "宝马A5", "红色", "宝马", 50000d));

       carDao.save(new Car(11l, "奔驰A1", "红色", "奔驰", 10000d));
       carDao.save(new Car(12l, "奔驰A2", "黑色", "奔驰", 20000d));
       carDao.save(new Car(13l, "奔驰A3", "黑色", "奔驰", 30000d));
       carDao.save(new Car(14l, "奔驰A4", "红色", "奔驰", 40000d));
       carDao.save(new Car(15l, "奔驰A5", "红色", "奔驰", 50000d));
  }

划分桶

GET /car_index/car/_search
{
"query": {
  "bool": {
    "should": [
      {
        "match_all": {}
      }
    ]
  }
},
"aggs": {
  "group_by_bland": {
    "terms": {
      "field": "color"
    }
  }
}
}

桶内度量

GET /car_index/car/_search
{
"query": {
  "bool": {
    "should": [
      {
        "match_all": {}
      }
    ]
  }
},
"aggs": {
  "group_by_bland": {
    "terms": {
      "field": "color"
    },
    "aggs": {
      "avg_price": {
        "avg": {
          "field": "price"
        }
      }
    }
  }
}
}

Spring Data ElasticSearch代码实现

划分桶

package com.lxs.esdemo2.dao;

import com.lxs.esdemo2.domain.Car;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.aggregations.metrics.avg.InternalAvg;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

import static org.junit.Assert.*;

@RunWith(SpringRunner.class)
@SpringBootTest
public class CarDaoTest {

   @Autowired
   private CarDao carDao;

   @Test
   public void initIndex() {
       carDao.save(new Car(1l, "比亚迪A1", "红色", "比亚迪", 50000d));
       carDao.save(new Car(2l, "比亚迪A2", "白色", "比亚迪", 70000d));
       carDao.save(new Car(3l, "比亚迪A3", "白色", "比亚迪", 80000d));
       carDao.save(new Car(4l, "比亚迪A4", "红色", "比亚迪", 60000d));
       carDao.save(new Car(5l, "比亚迪A5", "红色", "比亚迪", 90000d));

       carDao.save(new Car(6l, "宝马A1", "红色", "宝马", 10000d));
       carDao.save(new Car(7l, "宝马A2", "黑色", "宝马", 20000d));
       carDao.save(new Car(8l, "宝马A3", "黑色", "宝马", 30000d));
       carDao.save(new Car(9l, "宝马A4", "红色", "宝马", 40000d));
       carDao.save(new Car(10l, "宝马A5", "红色", "宝马", 50000d));

       carDao.save(new Car(11l, "奔驰A1", "红色", "奔驰", 10000d));
       carDao.save(new Car(12l, "奔驰A2", "黑色", "奔驰", 20000d));
       carDao.save(new Car(13l, "奔驰A3", "黑色", "奔驰", 30000d));
       carDao.save(new Car(14l, "奔驰A4", "红色", "奔驰", 40000d));
       carDao.save(new Car(15l, "奔驰A5", "红色", "奔驰", 50000d));
  }

   @Test
   public void testQueryByAggs() {
       NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder().withQuery(QueryBuilders.matchAllQuery());
       queryBuilder.withSourceFilter(new FetchSourceFilter(new String[] {}, new String[] {"brand"}));

       //添加聚合
       queryBuilder.addAggregation(AggregationBuilders.terms("group_by_color").field("color"));

       //执行结果,转换成聚合page
       AggregatedPage<Car> aggPage = (AggregatedPage<Car>) carDao.search(queryBuilder.build());

       //从聚合结果中获得bucket的名字对应的聚合
       StringTerms agg = (StringTerms) aggPage.getAggregation("group_by_color");

       List<StringTerms.Bucket> buckets = agg.getBuckets();

       buckets.forEach(b -> {
           String color = b.getKeyAsString();
           Long docCount = b.getDocCount();
           System.out.println("color = " + color + " 总数" + docCount);
      });
  }
}

桶内度量

package com.lxs.esdemo2.dao;

import com.lxs.esdemo2.domain.Car;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.aggregations.metrics.avg.InternalAvg;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

import static org.junit.Assert.*;

@RunWith(SpringRunner.class)
@SpringBootTest
public class CarDaoTest {

   @Autowired
   private CarDao carDao;

   @Test
   public void initIndex() {
       carDao.save(new Car(1l, "比亚迪A1", "红色", "比亚迪", 50000d));
       carDao.save(new Car(2l, "比亚迪A2", "白色", "比亚迪", 70000d));
       carDao.save(new Car(3l, "比亚迪A3", "白色", "比亚迪", 80000d));
       carDao.save(new Car(4l, "比亚迪A4", "红色", "比亚迪", 60000d));
       carDao.save(new Car(5l, "比亚迪A5", "红色", "比亚迪", 90000d));

       carDao.save(new Car(6l, "宝马A1", "红色", "宝马", 10000d));
       carDao.save(new Car(7l, "宝马A2", "黑色", "宝马", 20000d));
       carDao.save(new Car(8l, "宝马A3", "黑色", "宝马", 30000d));
       carDao.save(new Car(9l, "宝马A4", "红色", "宝马", 40000d));
       carDao.save(new Car(10l, "宝马A5", "红色", "宝马", 50000d));

       carDao.save(new Car(11l, "奔驰A1", "红色", "奔驰", 10000d));
       carDao.save(new Car(12l, "奔驰A2", "黑色", "奔驰", 20000d));
       carDao.save(new Car(13l, "奔驰A3", "黑色", "奔驰", 30000d));
       carDao.save(new Car(14l, "奔驰A4", "红色", "奔驰", 40000d));
       carDao.save(new Car(15l, "奔驰A5", "红色", "奔驰", 50000d));
  }

   @Test
   public void testQueryByAggs() {
       NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder().withQuery(QueryBuilders.matchAllQuery());
       queryBuilder.withSourceFilter(new FetchSourceFilter(new String[] {}, new String[] {"brand"}));

       //添加聚合
       queryBuilder.addAggregation(AggregationBuilders.terms("group_by_color").field("color"));

       //执行结果,转换成聚合page
       AggregatedPage<Car> aggPage = (AggregatedPage<Car>) carDao.search(queryBuilder.build());

       //从聚合结果中获得bucket的名字对应的聚合
       StringTerms agg = (StringTerms) aggPage.getAggregation("group_by_color");

       List<StringTerms.Bucket> buckets = agg.getBuckets();

       buckets.forEach(b -> {
           String color = b.getKeyAsString();
           Long docCount = b.getDocCount();
           System.out.println("color = " + color + " 总数" + docCount);
      });
  }

   @Test
   public void testQueryBySubAggs() {
       NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder().withQuery(QueryBuilders.matchAllQuery());
       queryBuilder.withSourceFilter(new FetchSourceFilter(new String[] {}, new String[] {"brand"}));

       //添加聚合
       queryBuilder.addAggregation(AggregationBuilders.terms("group_by_color").field("color")
              .subAggregation(AggregationBuilders.avg("avg_price").field("price")));

       //执行结果,转换成聚合page
       AggregatedPage<Car> aggPage = (AggregatedPage<Car>) carDao.search(queryBuilder.build());

       //从聚合结果中获得bucket的名字对应的聚合
       StringTerms agg = (StringTerms) aggPage.getAggregation("group_by_color");

       List<StringTerms.Bucket> buckets = agg.getBuckets();

       buckets.forEach(b -> {
           String color = b.getKeyAsString();
           Long docCount = b.getDocCount();

           //取得内部聚合
           InternalAvg avg = (InternalAvg) b.getAggregations().asMap().get("avg_price");

           System.out.println("color = " + color + " 总数" + docCount + " 平均价格:" + avg.getValue());
      });
  }
}
posted @ 2021-12-15 15:36  马世凯  阅读(70)  评论(0)    收藏  举报