今天来学习一种基于智能搜索,分布式的搜索引擎ElasticSearch
1. 全文检索lucene
1.1 什么是全文检索
全文检索是指计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。这个过程类似于通过字典中的检索字表查字的过程。
全文检索的方法主要分为按字检索和按词检索两种。按字检索是指对于文章中的每一个字都建立索引,检索时将词分解为字的组合。对于各种不同的语言而言,字有不同的含义,比如英文中字与词实际上是合一的,而中文中字与词有很大分别。按词检索指对文章中的词,即语义单位建立索引,检索时按词检索,并且可以处理同义项等。英文等西方文字由于按照空白切分词,因此实现上与按字处理类似,添加同义处理也很容易。中文等东方文字则需要切分字词,以达到按词索引的目的,关于这方面的问题,是当前全文检索技术尤其是中文全文检索技术中的难点,在此不做详述。
Lucene官方对自己的优势总结为几点:
- Scalable, High-Performance Indexing
- Powerful, Accurate and Efficient Search Algorithms
1.2 索引流程
1.3 相关概念
Index(索引)
类似数据库的表的概念,但是与传统表的概念会有很大的不同。传统关系型数据库或者NoSQL数据库的表,在创建时至少要定义表的Scheme,定义表的主键或列等,会有一些明确定义的约束。而Lucene的Index,则完全没有约束。Lucene的Index可以理解为一个文档收纳箱,你可以往内部塞入新的文档,或者从里面拿出文档,但如果你要修改里面的某个文档,则必须先拿出来修改后再塞回去。这个收纳箱可以塞入各种类型的文档,文档里的内容可以任意定义,Lucene都能对其进行索引。
Document(文档)
类似数据库内的行或者文档数据库内的文档的概念,一个Index内会包含多个Document。写入Index的Document会被分配一个唯一的ID,即Sequence Number(更多被叫做DocId),关于Sequence Number后面会再细说。
索引库
就是保存在磁盘上的一些列文件,里面存储了索引信息和文档
Field(字段)
一个Document会由一个或多个Field组成,Field是Lucene中数据索引的最小定义单位。Lucene提供多种不同类型的Field,例如StringField、TextField、LongFiled或NumericDocValuesField等,Lucene根据Field的类型(FieldType)来判断该数据要采用哪种类型的索引方式(Invert Index、Store Field、DocValues或N-dimensional等),关于Field和FieldType后面会再细说。
Term和Term Dictionary
Lucene中索引和搜索的最小单位,一个Field会由一个或多个Term组成,Term是由Field经过Analyzer(分词)产生。Term Dictionary即Term词典,是根据条件查找Term的基本索引。
2. ElasticSearch简介
ES=elaticsearch简写, Elasticsearch是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别的数据。
Elasticsearch也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。
使用场景:
1)百度,谷歌,必应。我们可以通过他们去搜索我们需要的东西。但是我们的搜索不只是包含这些,还有京东站内搜索啊。
2)互联网的搜索:电商网站。招聘网站。新闻网站。各种APP(百度外卖,美团等等)
3)windows系统的搜索,OA软件,淘宝SSM网站,前后台的搜索功能
总结:搜索无处不在。通过一些关键字,给我们查询出来跟这些关键字相关的信息
Lucene与ES关系?
1)Lucene只是一个库。想要使用它,你必须使用Java来作为开发语言并将其直接集成到你的应用中,更糟糕的是,Lucene非常复杂,你需要深入了解检索的相关知识来理解它是如何工作的。
2)Elasticsearch也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。
ES主要解决问题:
1)检索相关数据;
2)返回统计结果;
3)速度要快。
3. ES相关术语
3.1 概述
ES和传统的关系型数据库的对应关系
1 | mysql——>database——>tables——>rows——>cloumns |
3.2 ES核心概念
3.2.1 index 索引(索引库)
我们为什么使用ES?因为想把数据存进去,然后再查询出来。
我们在使用Mysql或者Oracle的时候,为了区分数据,我们会建立不同的数据库,库下面还有表的。
其实ES功能就像一个关系型数据库,在这个数据库我们可以往里面添加数据,查询数据。
ES中的索引非传统索引的含义,ES中的索引是存放数据的地方,是ES中的一个概念词汇
index类似于我们Mysql里面的一个数据库 create database user; 好比就是一个索引库
3.2.2 type类型
类型是用来定义数据结构的
在每一个index下面,可以有一个或者多个type,好比数据库里面的一张表。
相当于表结构的描述,描述每个字段的类型。
3.2.3 Field 字段
好比关系型数据库中列的概念,一个document有一个或者多个field组成。
例如:
朝阳区:一个Mysql数据库
房子:create database chaoyaninfo
房间:create table people
3.2.4 Mapping 映射
处理数据映射的方式和规则方面做一些限制,如某个字段的数据类型,默认值,分析器,是否被索引等
3.2.5 document 文档
文档就是最终的数据了,可以认为一个文档就是一条记录。
是ES里面最小的数据单元,就好比表里面的一条数据
3.2.6 NRT(Near Realtime)近实时
3.2.7 cluster集群
ES是一个分布式的系统
ES直接解压不需要配置就可以使用,在hadoop1上解压一个ES,在hadoop2上解压了一个ES,接下来把这两个ES启动起来。他们就构成了一个集群。
在ES里面默认有一个配置,clustername 默认值就是ElasticSearch,如果这个值是一样的就属于同一个集群,不一样的值就是不一样的集群。
3.2.8 Node节点
就是集群中的一台服务器
3.2.9 shard:分片
一台服务器,无法存储大量的数据,ES把一个index里面的数据,分为多个shard,分布式的存储在各个服务器上面。
kafka:为什么支持分布式的功能,因为里面是有topic,支持分区的概念。所以topic A可以存在不同的节点上面。就可以支持海量数据和高并发,提升性能和吞吐量
3.2.10 replica:副本
一个分布式的集群,难免会有一台或者多台服务器宕机,如果我们没有副本这个概念。就会造成我们的shard发生故障,无法提供正常服务。
我们为了保证数据的安全,我们引入了replica的概念,跟hdfs里面的概念是一个意思。
可以保证我们数据的安全。
在ES集群中,我们一模一样的数据有多份,能正常提供查询和插入的分片我们叫做 primary shard,其余的我们就管他们叫做 replica shard(备份的分片)
当我们去查询数据的时候,我们数据是有备份的,它会同时发出命令让我们有数据的机器去查询结果,最后谁的查询结果快,我们就要谁的数据(这个不需要我们去控制,它内部就自己控制了)
3.3 总结
在默认情况下,我们创建一个库的时候,默认会帮我们创建5个主分片(primary shrad)和5个副分片(replica shard),所以说正常情况下是有10个分片的。
同一个节点上面,副本和主分片是一定不会在一台机器上面的,就是拥有相同数据的分片,是不会在同一个节点上面的。
所以当你有一个节点的时候,这个分片是不会把副本存在这仅有的一个节点上的,当你新加入了一台节点,ES会自动的给你在新机器上创建一个之前分片的副本。
4. ES集群的安装
1、设置max_map_count不能启动es会启动不起来
查看max_map_count的值 默认是65530
1 | cat /proc/sys/vm/max_map_count |
重新设置max_map_count的值
1 | sysctl -w vm.max_map_count=262144 |
2、下载镜像并运行
1 | #拉取镜像 |
参数说明
—name表示镜像启动后的容器名称
-d: 后台运行容器,并返回容器ID;
-e: 指定容器内的环境变量
-p: 指定端口映射,格式为:主机(宿主)端口:容器端口
3、浏览器访问ip:9200 如果出现以下界面就是安装成功
4、开启远程连接
安装完成后,es并不能正常使用,5版本以后不支持自动远程连接,需要修改配置开启
配置跨域
通过项目中的配置,发现 cluster.name 这个与刚刚启动的不一致,ElasticSearch默认的cluster.name为elasticsearch,所以,这里需要进行修改。
首先进入容器。
1 | docker exec -it es2 /bin/bash |
我们需要对红框框圈中的文件”elasticsearch.yml”进行修改。]
这里使用vim和vi命令,都提示”没有发现这个命令”,这是因为Docker容器内部没有安装。所以,这里需要进行安装
1 | apt-get update # 获取最新的软件包 |
依次使用上面的命令即可安装成功,然后对”elasticsearch.yml”这个文件修改,增加如下内容。
cluster.name: “qfcwx-cluster”
network.host: 0.0.0.0
http.cors.enabled: true
http.cors.allow-origin: “*”
cluster.name:自定义集群名称。
network.host:当前es节点绑定的ip地址,默认127.0.0.1,如果需要开放对外访问这个属性必须设置。
http.cors.enabled:是否支持跨域,默认为false。
http.cors.allow-origin:当设置允许跨域,默认为*,表示支持所有域名,如果我们只是允许某些网站能访问,那么可以使用正则表达式。重启ElasticSearch容器
使用exit
命令从容器内部退出。
注意,这里是重启ElasticSearch容器,并不是重新启动一个新的容器。
1 | docker restart es2 |
使用浏览器进行访问:
5. ES的客户端操作
实际开发中,主要有三种方式可以作为elasticsearch服务的客户端:
第一种,elasticsearch-head插件
第二种,使用elasticsearch提供的Restful接口直接访问(可以使用第三方工具,这里使用Postman工具)
第三种,使用elasticsearch提供的API进行访问
5.1 es-head管理界面
安装:
安装Head 插件
Elasticsearch Head Plugin: 对ES进行各种操作,如查询、删除、浏览索引等。
1、下载elasticsearch-head并解压
在线下载:wget https://github.com/mobz/elasticsearch-head/archive/master.zip
或者到github下载:https://github.com/mobz/elasticsearch-head
unzip elasticsearch-head-master.zip //解压zip文件
mv elasticsearch-head-master.zip /home/ntc/code/elasticsearch-head //解压到自定义目录并修改文件夹名为elasticsearch-head
2、安装node
由于head插件本质上还是一个nodejs的工程,因此需要安装node,使用npm来安装依赖的包。(npm可以理解为maven)
wget https://npm.taobao.org/mirrors/node/latest-v4.x/node-v4.4.7-linux-x64.tar.gz
tar -zxvf node-v4.4.7-linux-x64.tar.gz
解压完node的安装文件后,需要配置下环境变量,编辑/etc/profile,添加
export NODE_HOME=/export/servers/node-v4.4.7-linux-x64
export PATH=$NODE_HOME/bin:$PATH
保存后别忘记立即执行以下
source /etc/profile
这个时候可以测试一下node是否生效:node -v
5.2 postman进行restful访问
ElasticSearch的接口语法:
curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'
ES Restful API 中的VERB
创建索引index和映射mapping
请求url:PUT localhost:9200/blog1
请求体:
1 | { |
通过head中的复合查询创建:
通过postman创建:
在head插件中查看索引信息:
创建索引后设置Mapping
直接使用put方法创建一个没有设置请求体的索引,然后设置mapping信息
请求的url:POST http://localhost:9200/blog2/hello/_mapping
1 | { |
通过head插件中的复合查询创建:
通过postman创建
删除索引index
请求url:DELETE http://localhost:9200/blog1
直接在head插件中删除:
通过head插件符合查询删除:
通过postman删除:
创建文档document
请求url:POST http://localhost:9200/blog1/hello/1
请求体:
1 | { |
修改文档document
请求url:POST http://localhost:9200/blog1/hello/1
请求体:
1 | { |
删除文档document
请求url:DELETE http://localhost:9200/blog1/hello/1
查询文档
查询文档-根据id查询
查询文档-term查询
对搜索的关键词不分词
请求url:POST http://localhost:9200/blog1/hello/_search
请求体:
1 | { |
查询文档-querystring查询
搜索之前对搜索的关键词分词
请求url:POST http://localhost:9200/blog1/hello/_search
请求体:
1 | { |
6. IK中文分词器
IKAnalyzer是一个开源的,基于java开发的轻量级的中文分词工具包。ES默认没有携带IK分词器,需要下载,下载地址:
https://github.com/medcl/elasticsearch-analysis-ik/releases
6.1 下载安装
1.下载IK压缩包,本文使用ES是7.3.0,下载的IK也是7.3.0;
2.解压放到es安装目录中的plugin目录中,随便创建的文件目录
重启es即可
6.2 测试效果
IK分词器,支持两种算法。分别为:
- ik_smart :最少切分
- ik_max_word :最细粒度切分
下面看效果,还是测试 “正在学习elastic search” 这个字符串。
1.ik_smart
1 | { |
2.ik_max_word
1 | { |
对比结果,就能看出ik_max_word和 ik_smart 算法的区别,ik_max_word分出的词数更多更细一些。
7. kibana
Kibana 的版本需要和 Elasticsearch 的版本一致。这是官方支持的配置。
运行不同主版本号的 Kibana 和 Elasticsearch 是不支持的(例如 Kibana 5.x 和 Elasticsearch 2.x),若主版本号相同,运行 Kibana 子版本号比 Elasticsearch 子版本号新的版本也是不支持的(例如 Kibana 5.1 和 Elasticsearch 5.0)。
运行一个 Elasticsearch 子版本号大于 Kibana 的版本基本不会有问题,这种情况一般是便于先将 Elasticsearch 升级(例如 Kibana 5.0 和 Elasticsearch 5.1)。在这种配置下,Kibana 启动日志中会出现一个警告,所以一般只是使用于 Kibana 即将要升级到和 Elasticsearch 相同版本的场景。
运行不同的 Kibana 和 Elasticsearch 补丁版本一般是支持的(例如:Kibana 5.0.0 和 Elasticsearch 5.0.1)。
7.1 下载链接
下载链接
1、官网链接:
https://www.elastic.co/guide/en/kibana/5.2/targz.html
2、其他安装方式:https://www.elastic.co/guide/cn/kibana/current/install.html
例如有:
a、 https://www.elastic.co/guide/cn/kibana/current/deb.html
deb 包用来在 Debian、Ubuntu 和其他基于 Debian 的系统下安装,Debian 包可以从 Elastic 官网或者我们的 Debian 仓库中下载。
b、elastic.co/guide/cn/kibana/current/rpm.html
rpm 包用来在 Red Hat、Centos、SLES、OpenSuSe 以及其他基于 RPM 的系统下安装。RPM 包可以从 Elastic 官网或者我们的 RPM 仓库下载。
c、https://www.elastic.co/guide/cn/kibana/current/docker.html
Elastic Docker 仓库中有现有的可以运行 Kibana 的 Docker 镜像,并预装了 X-Pack 。
7.2 下载及安装
1.下载安装包
[admin@admin01 modules]wgethttps://artifacts.elastic.co/downloads/kibana/kibana−5.2.2−linux−x8664.tar.gz[admin@admin01modules] wget https://artifacts.elastic.co/downloads/kibana/kibana-5.2.2-linux-x86_64.tar.gz
[admin@admin01 modules] tar -xzf kibana-5.2.2-linux-x86_64.tar.gz
[admin@admin01 modules]$ cd ../softwares/kibana-5.2.2-linux-x86_64/
2.配置相关的信息
[admin@admin01 kibana-5.2.2-linux-x86_64]$ vi config/kibana.yml
server.port: 5601
server.host: 192.168.47.101
elasticsearch.url: "http://192.168.47.101:9200"
3.外部访问,添加防火墙端口 5601
[root@admin01 ~]# iptables -N RH-Firewall-1-INPUT
[root@admin01 ~]# service iptables save
iptables: Saving firewall rules to /etc/sysconfig/iptables:[ OK ]
[root@admin01 ~]# vi /etc/sysconfig/iptables
-ARH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 5601 -j ACCEPT # 添加到该目录下
[root@admin01 ~]# service iptables restart # 重新启动端口
iptables: Setting chains to policy ACCEPT: filter [ OK ]
iptables: Flushing firewall rules: [ OK ]
iptables: Unloading modules: [ OK ]
iptables: Applying firewall rules: [ OK ]
4.启动
[admin@admin01 softwares]cdkibana−5.2.2−linux−x8664/[admin@admin01kibana−5.2.2−linux−x8664] cd kibana-5.2.2-linux-x86_64/
[admin@admin01 kibana-5.2.2-linux-x86_64] bin/kibana
验证 Kibana是否正常启动,则通过之前配置的端口信息,则可以进行访问,具体的访问如下所示:
7.3 DSL语句使用
查看集群
查看集群健康信息
GET /_cat/health?v
集群状态(status)
Green(正常)
Yellow(正常,但是一些副本还没有分配)
Red(非正常)
可以使用GET /_cat/health?help查看每个操作返回结果字段的意义
注意:
这里的GET是RESTful API的请求方式
/_cat/health?help 是RESTful API接口
你也可以使用PostMan这样的RESTful API测试工具,但是没有提示
查看每个操作返回结果字段的意义
GET /_cat/health?help
注意:
- 这里的
GET
是RESTful API的请求方式/_cat/health?help
是RESTful API接口- 你也可以使用PostMan这样的RESTful API测试工具,但是没有提示
查看集群中节点信息
GET /_cat/nodes?v
ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
192.168.23.141 9 91 7 0.10 0.08 0.13 mdi * x0vIhEF
查看集群中的索引信息
GET /_cat/indices?v
health status index uuid pri rep docs.count docs.deleted store.size
yellow open baizhi BYzhTHMzQIKEiyaKXklueQ 5 1 0 0 1.2kb
简化写法
GET /_cat/indices?v&h=health,status,index
1 | health status index |
索引操作
创建索引
PUT /baizhi
1 | #! Deprecation: the default number of shards will change from [5] to [1] in 7.0.0; if you wish to continue using the default of [5] shards, you must manage this on the create index request or with an index template |
上面的操作使用默认的配置信息创建一个索引
删除索引
DELETE /baizhi
{
"acknowledged": true
}
创建类型Mapping
PUT /baizhi # 创建index(baizhi)并添加类型mapping(doc)
{
"mappings": {
"doc": {
"properties": {
"title": { "type": "text" }, # 注意: 字符串常用类型:text类型会分词 keyword类型不会分词
"name": { "type": "text" },
"age": { "type": "integer" },
"created": {
"type": "date",
"format": "strict_date_optional_time||epoch_millis"
}
}
}
}
}
或
POST /baizhi/user # 创建index(baizhi)后,在指定index中添加类型mapping(user)
1 | { |
Mapping Type:
- 简单类型:
text
,keyword
,date
,long
,double
,boolean
or ip- 其它类型:
object
,geo_point
,geo_shape
等查看类型mapping
GET /baizhi/_mapping/_doc # 语法:GET /索引名/_mapping/类型名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 -------------------------------------------
{
"baizhi": {
"mappings": {
"_doc": {
"properties": {
"age": {
"type": "integer"
},
"created": {
"type": "date"
},
"name": {
"type": "text"
},
"title": {
"type": "text"
}
}
}
}
}
}
文档操作
新增单个文档
PUT /baizhi/_doc/1 # put /索引名/类型名/id
1
2
3
4
5
6
7
{
"name":"zs",
"title":"张三",
"age":18,
"created":"2018-12-25"
}
或
POST /baizhi/_doc
1 |
|
查询单个文档
GET /baizhi/_doc/1 # 语法: GET /索引名/类型名/id
1 |
|
修改单个文档
PUT /baizhi/_doc/KbOj6GcBVEuCC3JSh18Y # 语法: PUT /索引名/类型名/id
1 |
|
删除单个文档
DELETE /baizhi/_doc/1 # 语法: DELETE /索引名/类型名/id
1 |
|
批处理操作
除了能够索引、更新和删除单个文档外,Elasticsearch还提供了使用_bulk API
批量执行上述任何操作的能力。这个功能非常重要,因为它提供了一种非常有效的机制,可以以尽可能少的网络往返尽可能快地执行多个操作
POST /baizhi/_doc/_bulk # 批量插入多个document
1 |
|
1 | POST _doc/_bulk |
8. java api
1、导入pom
1 | <dependencies> |
2、创建客户端
1 | private TransportClient client; |
3、XcontentBuilder实现创建索引
1 | /** |
创建document对象
1、通过XContentBuilder创建文档
1 |
|
2、通过使用Jackson转换实体创建文档
创建实体类对象
1
2
3
4
5
6@Data
public class Article {
private Integer id;
private String title;
private String content;
}添加jackson依赖包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<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>代码具体实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21@Test
//创建文档(通过实体转json)
publicvoidtest5() throwsException{
// 创建Client连接对象
Settings settings=Settings.builder().put("cluster.name", "my‐elasticsearch").build();
TransportClient client=new PreBuiltTransportClient(settings) .addTransportAddress(newInetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9300));
// 描述json 数据
//{id:xxx, title:xxx,content:xxx}
Article article=new Article();
article.setId(2);
article.setTitle("搜索工作其实很快乐");
article.setContent("我们希望我们的搜索解决方案要快,我们希望有一个零配置和一个完全免费的搜索模式,我们希望能够简单地使用JSON通过HTTP的索引数据,我们希望我们的搜索服务器始终可用,我们希望能够一台开始并扩展到数百,我们要实时搜索,我们要简单的多租户,我们希望建立一个云的解决方案。Elasticsearch旨在解决所有这些问题和更多的问题。");
ObjectMapper objectMapper=new ObjectMapper();
// 建立文档
client.prepareIndex("blog2", "article", article.getId().toString()) .setSource(objectMapper.writeValueAsString(article).getBytes(),XContentType.JSON).get();
//释放资源
client.close();
}
3、查询文档操作
termQuery词条查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14/**
* 词条查询
*/
public void termQuery(){
SearchResponse searchResponse = client.prepareSearch("indexsearch").setTypes("mysearch")
.setQuery(new TermQueryBuilder("say", "熟悉"))
.get();
SearchHits hits = searchResponse.getHits();
SearchHit[] hits1 = hits.getHits();
for (SearchHit documentFields : hits1) {
System.out.println(documentFields.getSourceAsString());
}
}
分页查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 /*分页查询
*/
public void getPageIndex(){
int pageSize = 5;
int pageNum = 2;
int startNum = (pageNum-1)*5;
SearchResponse searchResponse = client.prepareSearch("indexsearch")
.setTypes("mysearch")
.setQuery(QueryBuilders.matchAllQuery())
.addSort("id",SortOrder.ASC)
.setFrom(startNum)
.setSize(pageSize)
.get();
SearchHits hits = searchResponse.getHits();
SearchHit[] hits1 = hits.getHits();
for (SearchHit documentFields : hits1) {
System.out.println(documentFields.getSourceAsString());
}
client.close();
}高亮查询
在进行关键字搜索时,搜索出的内容中的关键字会显示不同的颜色,称之为高亮
京东商城搜索”笔记本”
高亮显示的html分析通过开发者工具查看高亮数据的html代码实现:
ElasticSearch可以对查询出的内容中关键字部分进行标签和样式的设置,但是你需要告诉ElasticSearch使用什么标签对高亮关键字进行包裹
高亮显示代码实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 /**
* 高亮查询
*/
public void highLight(){
//设置我们的查询高亮字段
SearchRequestBuilder searchRequestBuilder = client.prepareSearch("indexsearch")
.setTypes("mysearch")
.setQuery(QueryBuilders.termQuery("say", "hello"));
//设置我们字段高亮的前缀与后缀
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("say").preTags("<font style='color:red'>").postTags("</font>");
//通过高亮来进行我们的数据查询
SearchResponse searchResponse = searchRequestBuilder.highlighter(highlightBuilder).get();
SearchHits hits = searchResponse.getHits();
System.out.println("查询出来一共"+ hits.totalHits+"条数据");
for (SearchHit hit : hits) {
//打印没有高亮显示的数据
System.out.println(hit.getSourceAsString());
System.out.println("=========================");
//打印我们经过高亮显示之后的数据
Text[] says = hit.getHighlightFields().get("say").getFragments();
for (Text say : says) {
System.out.println(say);
}
/* Map<String, HighlightField> highlightFields = hit.getHighlightFields();
System.out.println(highlightFields);*/
}
client.close();
}
9. Spring Data Elasticsearch
Elasticsearch提供的Java客户端有一些不太方便的地方:
很多地方需要拼接Json字符串,在java中拼接字符串有多恐怖你应该懂的 需要自己把对象序列化为json存储 查询到结果也需要自己反序列化为对象
因此,我们这里就不讲解原生的Elasticsearch客户端API了。
而是学习Spring提供的套件:Spring Data ElasticsearchSpring Data 是的使命是给各种数据访问提供统一的编程接口,不管是关系型数据库(如MySQL),还是非关系数据库(如Redis),或者类似Elasticsearch这样的索引数据库。从而简化开发人员的代码,提高开发效率。
9.1 实例
创建Demo工程
我们新建一个demo,学习Elasticsearch
pom依赖:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<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.czxy</groupId>
<artifactId>bos-es</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>bos-es</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>application.yml文件配置:
1
2
3
4
5 spring:
data:
elasticsearch:
cluster-name: my-application
cluster-nodes: 127.0.0.1:9300 # 程序连接es的端口号是9300
索引操作
创建索引和映射
SpringBoot-data-elasticsearch提供了面向对象的方式操作elasticsearch
业务:将商品的信息存入elasticsearch,并且执行搜索操作
创建一个商品对象,有这些属性:id 编号,title 标题,category 分类,brand 品牌,price 价格, 图片地址
在SpringDataElasticSearch中,只需要操作对象,就可以操作elasticsearch中的数据
实体类
首先我们准备好实体类:
public class Item { private Long id; private String title; //标题 private String category;// 分类 private String brand; // 品牌 private Double price; // 价格 private String images; // 图片地址 }
映射—注解
Spring Data通过注解来声明字段的映射属性,有下面的三个注解:@Document 作用在类,标记实体类为文档对象,一般有两个属性 indexName:对应索引库名称 type:对应在索引库中的类型 shards:分片数量,默认5 replicas:副本数量,默认1 @Id 作用在成员变量,标记一个字段作为id主键 @Field 作用在成员变量,标记为文档的字段,并指定字段映射属性: type:字段类型,是枚举:FieldType,可以是text、long、short、date、integer、object等 text:存储数据时候,会自动分词,并生成索引 keyword:存储数据时候,不会分词建立索引 Numerical:数值类型,分两类 基本数据类型:long、interger、short、byte、double、float、half_float 浮点数的高精度类型:scaled_float 需要指定一个精度因子,比如10或100。elasticsearch会把真实值乘以这个因子后存储,取出时再还原。 Date:日期类型 elasticsearch可以对日期格式化为字符串存储,但是建议我们存储为毫秒值,存储为long,节省空间。 index:是否索引,布尔类型,默认是true store:是否存储,布尔类型,默认是false analyzer:分词器名称,这里的ik_max_word即使用ik分词器
创建索引
ElasticsearchTemplate中提供了创建索引的API:
映射
映射相关的API:
一样,可以根据类的字节码信息(注解配置)来生成映射,或者手动编写映射
我们这里采用类的字节码信息创建索引并映射:
@Test public void createIndex() { // 创建索引,会根据Item类的@Document注解信息来创建 esTemplate.createIndex(Item.class); // 配置映射,会根据Item类中的id、Field等字段来自动完成映射 esTemplate.putMapping(Item.class); }
索引信息:
删除索引
删除索引的API:
可以根据类名或索引名删除。
示例:
@Test
public void deleteIndex() {
esTemplate.deleteIndex(Item.class);
// 根据索引名字删除
//esTemplate.deleteIndex(“item1”);
}1 2 3 4 5 6
结果:OK
新增文档数据
Repository接口
Spring Data 的强大之处,就在于你不用写任何DAO处理,自动根据方法名或类的信息进行CRUD操作。只要你定义一个接口,然后继承Repository提供的一些子接口,就能具备各种基本的CRUD功能。
来看下Repository的继承关系:
我们看到有一个ElasticsearchCrudRepository接口:
所以,我们只需要定义接口,然后继承它就OK了。
1 | public interface ItemRepository extends ElasticsearchRepository<Item,Long> { |
接下来,我们测试新增数据:
新增一个对象
1 |
|
去页面查询看看:
批量新增
代码:
@Test
public void indexList() {
List<Item> list = new ArrayList<>();
list.add(new Item(2L, "坚果手机R1", " 手机", "锤子", 3699.00, "http://image.baidu.com/13123.jpg"));
list.add(new Item(3L, "华为META10", " 手机", "华为", 4499.00, "http://image.baidu.com/13123.jpg"));
// 接收对象集合,实现批量新增
itemRepository.saveAll(list);
}
再次去页面查询:
修改
elasticsearch中本没有修改,它是先删除再新增
修改和新增是同一个接口,区分的依据就是id。
@Test
public void index(){
Item item = new Item(1L, "苹果XSMax", " 手机",
"小米", 3499.00, "http://image.baidu.com/13123.jpg");
itemRepository.save(item);
}
查看结果:
查询
ElasticsearchRepository提供了一些基本的查询方法:
基本查询
ElasticsearchRepository提供了一些基本的查询方法:
我们来试试查询所有:
@Test
public void testFindAll(){
//1 查找所有
// Iterable<Item> item = itemRepository.findAll();
// Iterator<Item> it = item.iterator();
// while (it.hasNext()){
// System.out.println(it.next());
// }
//2 分页查找
// Page<Item> page = itemRepository.findAll(PageRequest.of(1, 5));
//
// for(Item item:page){
// System.out.println(item);
// }
//3 排序
Iterable<Item> iterable = itemRepository.findAll(Sort.by("price").descending());
Iterator<Item> it = iterable.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
match query:
1 |
|
NativeSearchQueryBuilder:Spring提供的一个查询条件构建器,帮助构建json格式的请求体
QueryBuilders.matchQuery(“title”, “小米手机”):利用QueryBuilders来生成一个查询。QueryBuilders提供了大量的静态方法,用于生成各种不同类型的查询:
Page:默认是分页查询,因此返回的是一个分页的结果对象,包含属性:
- totalElements:总条数
- totalPages:总页数
- Iterator:迭代器,本身实现了Iterator接口,因此可直接迭代得到当前页的数据
- 其它属性:
结果:
termQuery
1 |
|
9.2 测试代码
1 | /** |
9.3 聚合查询
概念
ES聚合分析是什么?
概念
Elasticsearch除全文检索功能外提供的针对Elasticsearch数据做统计分析的功能。它的实时性高,所有的计算结果都是即时返回。
Elasticsearch将聚合分析主要分为如下4类:
Metric(指标): 指标分析类型,如计算最大值、最小值、平均值等等 (对桶内的文档进行聚合分析的操作)
Bucket(桶): 分桶类型,类似SQL中的GROUP BY语法 (满足特定条件的文档的集合)
Pipeline(管道): 管道分析类型,基于上一级的聚合分析结果进行在分析
Matrix(矩阵): 矩阵分析类型(聚合是一种面向数值型的聚合,用于计算一组文档字段中的统计信息)
ES聚合分析查询的写法
在查询请求体中以aggregations节点按如下语法定义聚合分析:
1 | "aggregations" : { |
指标(metric)和 桶(bucket)
虽然Elasticsearch有四种聚合方式,但在一般实际开发中,用到的比较多的就是Metric和Bucket。
(1) 桶(bucket)
a、简单来说桶就是满足特定条件的文档的集合。
b、当聚合开始被执行,每个文档里面的值通过计算来决定符合哪个桶的条件,如果匹配到,文档将放入相应的桶并接着开始聚合操作。
c、桶也可以被嵌套在其他桶里面。
(2)指标(metric)
a、桶能让我们划分文档到有意义的集合,但是最终我们需要的是对这些桶内的文档进行一些指标的计算。分桶是一种达到目的地的手段:它提供了一种给文档分组的方法来让
我们可以计算感兴趣的指标。
b、大多数指标是简单的数学运算(如:最小值、平均值、最大值、汇总),这些是通过文档的值来计算的。