1.Elasticsearch
1.1 介绍
ELK(Elastic Stack)是以Elastic为核心的技术栈,如下图所示:
这个Lucene使用java写成的,其实就是个jar包,我们引入之后就可以使用这个Lucene的API。而ES就是基于Lucene的二次开发,对其API进行进一步封装:
1.1.1 正排索引
基于文档id创建索引。查询词条时必须先找到文档,而后判断是否包含词条
就是传统的关系型数据中的表,通过id或通过某个值来查询记录。
1.1.2 倒排索引
对文档内容分词,对词条创建索引,并记录词条所在文档的信息。查询时先根据词条查询到文档id,而后获取到文档
- 概念
- 文档:每一条数据就是一个文档(每一条记录就是一个文档)
- 词条:文档按照语义分成词语
示例如下:
这个倒排索引其实和生活中字典相当像,你拿到一本字典的目录,肯定不会傻到先找页码,你肯定是先大略看一眼目录的关键字,然后找到关键字之后,去看关键字旁边的页码,最后再根据页码翻到书对应的那一页。
搜索过程图
先倒排索引查出文档的ID值,然后根据ID使用正排索引查出结果。
1.1.3 相关概念
- Index(索引)
其实就是相同数据类型的数据集合,长这样。
即同类型的文档集合。
- 文档(Document)
- 之前说elasticsearch是面向文档的,那么就意味着索引和搜索数据的最小单位是文档,elasticsearch中,文档有几个重要属性:
- 自我包含,一篇文档同时包含字段和对应的值,也就是同时包含key:value !
- 可以是层次型的,一个文档中包含自文档,复杂的逻辑实体就是这么来的! {就是一个json对象 ! fastjson进行自动转换 !}
- 灵活的结构,文档不依赖预先定义的模式,我们知道关系型数据库中,要提前定义字段才能使用,在elasticsearch中,对于字段是非常灵活的,有时候,我们可以忽略该字段,或者动态的添加一个新的字段。
- 之前说elasticsearch是面向文档的,那么就意味着索引和搜索数据的最小单位是文档,elasticsearch中,文档有几个重要属性:
尽管我们可以随意的新增或者忽略某个字段,但是,每个字段的类型非常重要,比如一个年龄字段类型,可以是字符串也可以是整形。因为elasticsearch会保存字段和类型之间的映射及其他的设置。这种映射具体到每个映射的每种类型,这也是为什么在elasticsearch中,类型有时候也称为映射类型。
- 就是索引中的其中一个记录,一条数据
- 约束(Mapping)
- 就是数据类型,表示文档中属性的数据类型是什么
- DSL
- 用来访问Elasticsearch的语言,JSON风格的请求语句,支持Resful风格,使用的是http协议通信
- 分片
- 因为es使用的是集群,所以对于一个索引不可能全部都存放在同一个节点上,因此要分片,即划分索引。默认是5个主分片,5个对应主分片的副本
- 因为es使用的是集群,所以对于一个索引不可能全部都存放在同一个节点上,因此要分片,即划分索引。默认是5个主分片,5个对应主分片的副本
1.1.4 数据库与elasticsearch使用场景
1.2 安装
1.2.1 安装es
创建网络
因为我们还需要部署kibana容器,因此需要让es和kibana容器互联。这里先创建一个网络:
docker network create es-net
加载es镜像
直接拉去镜像即可
docker pull elasticsearch:7.12.1
运行es
docker run -d \
--name es \
-e "ES_JAVA_OPTS=-Xms256m -Xmx256m" \ # 建议512m++ 这里因为阿里云内存有限
-e "discovery.type=single-node" \
-v es-data:/usr/share/elasticsearch/data \
-v es-plugins:/usr/share/elasticsearch/plugins \
-v es-logs:/usr/share/elasticsearch/logs \
--privileged \
--network es-net \
-p 9200:9200 \
-p 9300:9300 \
elasticsearch:7.12.1
命令解释:
-e "cluster.name=es-docker-cluster"
:设置集群名称-e "http.host=0.0.0.0"
:监听的地址,可以外网访问-e "ES_JAVA_OPTS=-Xms512m -Xmx512m"
:内存大小-e "discovery.type=single-node"
:非集群模式-v es-data:/usr/share/elasticsearch/data
:挂载逻辑卷,绑定es的数据目录-v es-logs:/usr/share/elasticsearch/logs
:挂载逻辑卷,绑定es的日志目录-v es-plugins:/usr/share/elasticsearch/plugins
:挂载逻辑卷,绑定es的插件目录--privileged
:授予逻辑卷访问权--network es-net
:加入一个名为es-net的网络中-p 9200:9200
:端口映射配置
1.2.2 安装kibana
拉镜像
docker pull kibana:7.12.1
运行
docker run -d \
--name kibana \
-e ELASTICSEARCH_HOSTS=http://es:9200 \
--network=es-net \
-p 5601:5601 \
kibana:7.12.1
--network es-net
:加入一个名为es-net的网络中,与elasticsearch在同一个网络中-e ELASTICSEARCH_HOSTS=http://es:9200"
:设置elasticsearch的地址,因为kibana已经与elasticsearch在一个网络,因此可以用容器名直接访问elasticsearch-p 5601:5601
:端口映射配置
kibana启动一般比较慢,需要多等待一会,可以通过命令:
docker logs -f kibana
打开看看
测试
1.2.3 安装IK分词器
分词:即把一段中文或者别的划分成一个个的关键字,我们在搜索时候会把自己的信息进行分词,会把数据库中或者索引库中的数据进行分词,然后进行一一个匹配操作,默认的中文分词是将每个字看成一个词(不使用用IK分词器的情况下)
es在创建倒排索引时需要对文档分词;在搜索时,需要对用户输入内容分词。但默认的分词规则对中文处理并不友好。
我们在kibana的DevTools中测试:
可以看到每一词都被分出来了,不符合预期,我们需要使用其他分词软件
1)查看数据卷目录
安装插件需要知道elasticsearch的plugins目录位置,而我们用了数据卷挂载,因此需要查看elasticsearch的数据卷目录,通过下面命令查看:
docker volume inspect es-plugins
显示结果:
[
{
"CreatedAt": "2022-05-06T10:06:34+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/es-plugins/_data",
"Name": "es-plugins",
"Options": null,
"Scope": "local"
}
]
说明plugins目录被挂载到了:/var/lib/docker/volumes/es-plugins/_data
这个目录中。
2)解压分词器安装包
下面我们需要把课前资料中的ik分词器解压缩,重命名为ik
链接:https://pan.baidu.com/s/1oS-Hf5vWFAi5mH76ehDcrg?pwd=rki9
提取码:rki9
3)上传到es容器的插件数据卷中
也就是/var/lib/docker/volumes/es-plugins/_data
:
4)重启容器
# 4、重启容器
docker restart es
# 查看es日志
docker logs -f es
5)测试
IK分词器包含两种模式:
ik_smart
:最少切分ik_max_word
:最细切分,分得词更多,空间占用率高
GET /_analyze
{
"analyzer": "ik_max_word",
"text": "阿果程序员学习java太棒了"
}
GET /_analyze
{
"analyzer": "ik_smart",
"text": "阿果程序员学习java太棒了"
}
1.3 自定义分词
docker目录下:/var/lib/docker/volumes/es-plugins/_data/ik/config/
中打开IKAnalyzer.cfg.xml文件
aguo.dic aguo.dic就可以存放在当前文件,作为自定义词汇
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>IK Analyzer 扩展配置</comment>
<!--用户可以在这里配置自己的扩展字典 -->
<entry key="ext_dict">aguo.dic</entry>
<!--用户可以在这里配置自己的扩展停止词字典-->
<entry key="ext_stopwords"></entry>
<!--用户可以在这里配置远程扩展字典 -->
<!-- <entry key="remote_ext_dict">words_location</entry> -->
<!--用户可以在这里配置远程扩展停止词字典-->
<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
aguo.dic文件示例
奥利给
阿果
更新这个词典文件后,需要docker restart es重启es服务
1.4 Rest风格操作
一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
1.4.1 基本命令
method | url地址 | 描述 |
---|---|---|
PUT(创建,修改) | localhost:9200/索引名称/类型名称/文档id | 创建文档(指定文档id) |
POST(创建) | localhost:9200/索引名称/类型名称 | 创建文档(随机文档id) |
POST(修改) | localhost:9200/索引名称/类型名称/文档id/_update | 修改文档 |
DELETE(删除) | localhost:9200/索引名称/类型名称/文档id | 删除文档 |
GET(查询) | localhost:9200/索引名称/类型名称/文档id | 查询文档通过文档ID |
POST(查询) | localhost:9200/索引名称/类型名称/文档id/_search | 查询所有数据 |
1.4.2 索引操作
1.4.2.1 创建索引
PUT /索引名/类型名/ID
{
DSL语句
}
//新建索引
PUT /aguoindex/1
{
"name":"阿果爱Java",
"age":3
}
- 数据类型
- 字符串类型
- text、
keyword- text:支持分词,全文检索,支持模糊、精确查询,不支持聚合,排序操作;text类型的最大支持的字符长度无限制,适合大字段存储;
- keyword:不进行分词,直接索引、支持模糊、支持精确匹配,支持聚合、排序操作。keyword类型的最大支持的长度为——32766个UTF-8类型的字符,可以通过设置ignore_above指定自持字符长度,超过给定长度后的数据将不被索引,无法通过term精确匹配检索返回结果。
- text、
- 数值型
- long、Integer、short、byte、double、float、half float、scaled float
- 日期类型
- date
- te布尔类型
- boolean
- 二进制类型
- binary
- 等等…
- 字符串类型
指定类型时创建规则
PUT /aguoindex
{
"mappings":{
"properties":{
"info":{
"type": "text",
"analyzer": "ik_smart"
},
"name":{ #对象类型
"type":"object",
"properties": {
"firstname":{
"type":"keyword"
},
"lastname":{
"type":"keyword"
}
}
},
"age":{ #不参与倒排索引
"type":"long",
"index": false
},
"birthday":{
"type":"date"
}
}
}
}
1.4.2.2 修改索引
禁止修改索引库,允许添加新的字段。
POST /aguoindex/_mapping
{
"properties":{
"sex":{
"type":"keyword"
}
}
}
1.4.2.4 删除索引
DElETE /aguoindex
1.4.3 文档语法
新增
POST /aguoindex/_doc/1
{
"info":"阿果爱学Java",
"age":21,
"brithday":"2000-08-31",
"name":{
"firstname":"阿",
"lastname":"果"
}
}
查询
GET /aguoindex/_doc/1
删除
DELETE /aguoindex/_doc/1
修改
#存在则修改,不存在则新增,全量修改
PUT /aguoindex/_doc/1
{
"info":"阿果爱学Java",
"age":21,
"brithday":"2000-08-31",
"name":{
"firstname":"阿",
"lastname":"果"
}
}
#局部修改
POST /aguoindex/_update/1
{
"doc": {
"age":22
}
}
1.5 RestClient
ES官方提供了各种不同语言的客户端,用来操作ES。这些客户端的本质就是组装DSL语句,通过http即请求发送给ES。
链接:https://pan.baidu.com/s/1xkAW_U7_rwAhkSeuMC8BHQ?pwd=bg9q
提取码:bg9q
准备好数据
创建索引
PUT /hotel
{
"mappings":{
"properties": {
"id":{
"type": "keyword"
},
"name":{
"type": "text",
"analyzer": "ik_max_word",
"copy_to": "all"
},
"address":{
"type": "keyword",
"index": false
},
"price":{
"type": "integer"
},
"score":{
"type": "integer"
},
"brand":{
"type": "keyword",
"copy_to": "all"
},
"city":{
"type": "keyword",
"copy_to": "all"
},
"starName":{
"type": "keyword"
},
"business":{
"type": "keyword"
},
"location":{
"type": "geo_point"
},
"pic":{
"type": "keyword",
"index": false
},
"all":{
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
“all” 字段是为了方便搜索而存在,聚集了 酒店名字、品牌、城市等信息的集合体
1.5.1 Springboot操作
导入依赖
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.17.4</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.17.4</version>
</dependency>
创建es连接
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://120.76.137.145:9200")
));
1.5.1.1 索引库操作
创建索引库
//1.创建Request对象
CreateIndexRequest request = new CreateIndexRequest("hotel");
//2、准备请求的参数:DSL语句
request.source(HoTelConstant.createIndexDSL, XContentType.JSON);
//3.发起请求
client.indices().create(request, RequestOptions.DEFAULT);
删除索引库
@Test
void deleteHotelIndex() throws IOException {
//1.创建Request对象
DeleteIndexRequest request = new DeleteIndexRequest("hotel");
//2.发起请求
client.indices().delete(request, RequestOptions.DEFAULT);
}
判断索引库
@Test
void isExitsHotelIndex() throws IOException {
//1.创建Request对象
GetIndexRequest request = new GetIndexRequest("hotel");
//2.发起请求
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
System.out.println(exists);
}
1.5.1.2 文档操作
插入文档
//获取request对象
IndexRequest request = new IndexRequest("hotel").id("123456");
//设置请求的body
request.source(JSON.toJSONString(hotelDoc),XContentType.JSON);
//发起请求
client.index(request, RequestOptions.DEFAULT);
查询文档
//获取request对象,索引与id
GetRequest request = new GetRequest("hotel","36934");
//得到response
GetResponse response = client.get(request, RequestOptions.DEFAULT);
//从response得到对象的JSON字符串形式
String hotelJson = response.getSourceAsString();
//反序列化为对象
HotelDoc hotelDoc = JSON.parseObject(hotelJson, HotelDoc.class);
System.out.println(hotelDoc);
更新文档
//获取request
UpdateRequest request = new UpdateRequest("hotel", "36934");
//封装body
request.doc(
"price","999888"
);
//发起更新
client.update(request, RequestOptions.DEFAULT);
删除文档
//获取request
DeleteRequest request = new DeleteRequest("hotel","36934");
//发起删除
client.delete(request, RequestOptions.DEFAULT);
批量导入
List<Hotel> list = iHotelService.list();
LinkedList<HotelDoc> link = new LinkedList<>();
for (Hotel hotel : list) {
link.add(new HotelDoc(hotel));
}
//以上是封装数据
//获取request对象
BulkRequest request = new BulkRequest();
//遍历对象,全部放到request中
for (HotelDoc hotelDoc : link) {
request.add(new IndexRequest("hotel").id(hotelDoc.getId().toString())
.source(JSON.toJSONString(hotelDoc),XContentType.JSON));
}
//提交
client.bulk(request, RequestOptions.DEFAULT);
1.6 DSL 语法
1.6.1 Query分类
Elasticsearch:提供了基于SON的DSL(Domain Specific Language)来定义查询。常见的查询类型包括:
- 查询所有:查询出所有数据,一般测试用。例如:match_all
- 全文检案(full text)查询:利用分词器对用户输入内容分词,然后去倒排案引库中匹配。例如:
- match_quey
- multi_match_query
- 精确查询=:根据精确词条值查找数据,一般是查找keyword、数值、日期、boolean等类型字段。例如: 与上面互斥
- ids
- range
- term
- 地理(geo)查询:根据经纬度查询。例如:
- geo_distance
- geo_bounding_box【不常用】
- 复合(compound)查询:复合查询可以将上述各种查询景件组合起来,合并查询景件。例如:
- bool
- function_score
1.6.2 全文检索
match查询
全文检索查询的一种,会对用户输入内容分词,然后去倒排索引库检索,语法:
GET /hotel/_search
{
"query":{
"match":{
"name":"李四"
}
}
}
multi_match 查询
GET /hotel/_search
{
"query":{
"multi_match":{
"query":"外滩如家",
"fields":["brand","name","business"]
}
}
}
1.6.3 精确查询
term查询:
根据词条精确匹配,一般搜索keyword类型、数值类型、布尔类型、日期类型字段
GET /hotel/_search
{
"query": {
"term": {
"city": {
"value": "上海"
}
}
}
}
1.6.4 模糊查询
range查询:
根据数值范围查询,可以是数值、日期的范围
GET /hotel/_search
{
"query": {
"range": {
"price": {
"gte": 10, # 大于等于,去掉e就只是大于
"lte": 2000
}
}
}
}
1.6.5 地理查询
distance 查询
指定圆心与半径,检索出圆形中所有的点
GET /hotel/_search
{
"query": {
"geo_distance": {
"distance":"15km",
"location":"31.21,121.5"
}
}
}
1.6.6 Function Score Query
根据结果集的命中词频进行排序
DSL的模板如下,加权模式可以忽略
示例如下:
让“如家”这个品牌更加靠前。
GET /hotel/_search
{
"query": {
"function_score": {
"query": { #query_socre
"match": {
"all": "外滩"
}
},
"functions": [
{
"filter": { #对“如家”的品牌结果施加权重
"term": {
"brand": "如家"
}
},
"weight": 10
}
]
}
}
}
1.6.7 Boolean Query
逻辑运算
- bool查询有几种逻辑关系?
- must:必须匹配的条件,可以理解为“与”
- should:选择性匹配的条件,可以理解为"或”
- must not:必须不匹配的条件,不参与打分
- filter:必须匹配的条件,不参与打分
模板如下
GET /hotel/_search
{
"query": {
"bool": {
"must": [
{}
],
"should": [
{}
],
"must_not": [
{}
],
"filter": [
{}
]
}
}
}
示例
GET /hotel/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"brand": {
"value": "如家"
}
}
}
],
"should": [
{
"match": {
"city": "北京"
}
},
{
"match": {
"city": "上海"
}
}
],
"must_not": [
{
"range": {
"price": {
"gte": 3000
}
}
}
],
"filter": [
{
"geo_distance": {
"distance": "15km",
"location": {
"lat": 31.21,
"lon": 121.5
}
}
}
]
}
}
}
1.7 结果集处理
1.7.1 排序
对结果集更加keyword、数值、地理坐标、日期排序
数值排序
GET /hotel/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"FIELD": {
"order": "asc/desc"
},
#...
}
]
}
地理排序
距离据点的距离排序
GET /hotel/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"_geo_distance": {
"location": {
"lat": 40,
"lon": -70
},
"order": "desc",
"unit": "km"
}
}
]
}
1.7.2 分页
官方限制1w条分页数据,需要在应用层保证,超出的部分需要通过其他更多条件来筛选结果
模板
GET /hotel/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"FIELD": {
"order": "asc"
}
}
],
"from": 10,
"size": 100
}
因为es是把10+100=110的结果全部检索出来,最后截取10条,所以对于大数据量的情况下,需要更多条件来筛选。
1.7.3 高亮
就是在搜索结果中把搜索关键字突出显示
原理:
- 将搜索结果中的关键字用标签标记出来
- 在页面中给标签添加css样式
- query域不能使用match_all
- 如果query的待查字段与高亮字段不一致,需要加"require_field_match": “false”
模板
GET /hotel/_search
{
"query": {
"match": {
"all": "如家"
}
},
"highlight": {
"fields": {#fields关键字
"name": {
"require_field_match": "false",
#默认是<em></em>
"pre_tags": "<aguo>"
, "post_tags": "</aguo>"
}
}
}
}
1.8 RestCilent使用DSL语法
核心就是SearchRequest
解析数据的通用方法
private void analyticalData(SearchResponse search) {
//获取一个列表数据
SearchHits hitList = search.getHits();
long total = hitList.getTotalHits().value;
System.out.printf("共找到"+total+"条数据");
//获取一个hit数组
SearchHit[] hits = hitList.getHits();
//反序列化
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
HotelDoc hotelDoc = JSON.parseObject(sourceAsString, HotelDoc.class);
System.out.println(hotelDoc);
}
}
1.8.1 match_all
示例
//获取request对象
SearchRequest request = new SearchRequest("hotel");
//封装body
request.source()
.query(QueryBuilders.matchAllQuery());
//发起请求
SearchResponse search = this.client.search(request, RequestOptions.DEFAULT);
//解析
analyticalData(search);
- request.source()
- 与DSL的一级菜单一致,其同等级的含有query、sort、highlighter
- QueryBuilders
- 含有与query相关的查询,如term、match、bool等操作
- cilent
- 在1.5.1有
1.8.2 multi_match
//request
SearchRequest request = new SearchRequest("hotel");
//封装body
request.source()
.query(QueryBuilders.multiMatchQuery("如家","brand","all"));
SearchResponse search = this.client.search(request, RequestOptions.DEFAULT);
//解析
analyticalData(search);
1.8.3 bool查询
@Test
void booleanQuery() throws IOException {
//获取request
SearchRequest request = new SearchRequest("hotel");
//使用Bool查询
BoolQueryBuilder builder = new BoolQueryBuilder();
//封装Bool
builder.must(QueryBuilders.termQuery("city","北京"));
builder.mustNot(QueryBuilders.rangeQuery("price").gt(1000));
builder.filter(QueryBuilders.termQuery("brand","如家"));
//封装body
request.source().query(builder);
//发起请求
SearchResponse search = this.client.search(request, RequestOptions.DEFAULT);
//解析请求
analyticalData(search);
}
与以下完全对应
GET /hotel/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"city": {
"value": "北京"
}
}
}
],
"must_not": [
{
"range": {
"price": {
"gt": 1000
}
}
}
],
"filter": [
{
"term": {
"brand": "如家"
}
}
]
}
}
}
1.8.4 排序与分页
source()含有query、sort、highlighter,支持链式编程,很舒服
int page = 1;
int pageSize = 10;
//获取request
SearchRequest request = new SearchRequest("hotel");
request.source()
.query(QueryBuilders.termQuery("city","北京"))
//排序
.sort("price", SortOrder.ASC)
//分页
.from((page-1)*pageSize).size(10);
//发起请求
SearchResponse search = this.client.search(request, RequestOptions.DEFAULT);
//解析请求
analyticalData(search);
1.8.5 高亮
与query同级别,所以直接在query后面链式
//获取request
SearchRequest request = new SearchRequest("hotel");
request.source()
.query(QueryBuilders.matchQuery("all","二钻"))
.highlighter(new HighlightBuilder()
.field("name")
.requireFieldMatch(false)
.field("city")
.requireFieldMatch(false)
);
//发起请求
SearchResponse search = this.client.search(request, RequestOptions.DEFAULT);
//解析请求
analyticalData(search);
自己写了个工具类
/**
*
* @param search response结果
* @param type 反序列化的对象类型
* @return 高亮标签插入后对象
* @param <T>
* @throws Exception 可能的反射异常,包括反射方法失败、调用失败
*/
private <T> Collection<T> analyticalData(SearchResponse search, Class<T> type) throws Exception {
//获取一个列表数据
SearchHits hitList = search.getHits();
long total = hitList.getTotalHits().value;
System.out.println("共找到"+total+"条数据");
//获取一个hit数组
SearchHit[] hits = hitList.getHits();
//开始反序列化
Collection<T> collection = new LinkedList<>();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
//反序列化
T bean = JSON.parseObject(sourceAsString, type);
//获取高亮map
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
//遍历高亮field,如name,city要求的高亮
for (String key : highlightFields.keySet()) {
//根据field获取高亮的对象,text为,如<em>北京</em>
HighlightField highlightField = highlightFields.get(key);
Text[] fragments = highlightField.fragments();
//经过不断测试,这里Text.length===1,时间复杂度:O(1)
for (Text fragment : fragments) {
StringBuilder sb = new StringBuilder();
sb.append("set");
sb.append(key.substring(0,1).toUpperCase());
sb.append(key.substring(1));
Method setMethod = bean.getClass().getMethod(sb.toString(), String.class);
setMethod.invoke(bean,fragment.string());
}
}
collection.add(bean);
}
return collection;
}
1.9 聚合
聚合(aggregations)可以实现对文档数据的统计、分析、运算。
聚合有三类:
- 桶(Bucket)聚合:用来对文档做分组
- TermAggregation:按照文档字段值分组
- Date Histogram:按照日期阶梯分组,例如一周为一组,或者一月为一组
- Histogram Aggregation:根据数值阶梯分组,与日期类似
- Range Aggregation:数值和日期的范围分组,指定开始和结束,然后按段分组
- 度量(Metric)聚合:用以计算一些值,比如:最大值、最小值、平均值等
- Avg:求平均值
- Max:求最大值
- Min:求最小值
- Stats:同时求max、min、avg、sum等
- 管道(pipeline)聚合:其它聚合的结果为基础做聚合【少用】
聚合三要素
- 聚合名称
- 聚合类型
- 聚合字段
聚合可配置属性有:
- size:指定聚合结果数量
- order:指定聚合结果排序方式
- field:指定聚合字段
1.9.1 桶聚合
aggs与query同级别!
参与聚合的字段必须是keyword类型
默认聚合所有数据,因此大数据时候需要使用query过滤下
语法
GET /hotel/_search
{
"size": 0,#表示不显示文档
"aggs": {
"brandeggs": {#自定义聚合名
"terms": {
"field": "name",
"size": 10,#聚合结果数量
"order": {#自定义排序规则
"_count": "asc"
}
}
}
}
}
1.9.2 Metric聚合
对桶聚合的结果再次聚合
语法
GET /hotel/_search
{
"size": 0,
"aggs": {
"brandeggs": {
"terms": {
"field": "brand",
"size": 20,
"order": {
"scoreAgg.avg": "desc"
}
},
"aggs": {
"scoreAgg": {#自定义聚合名
"stats": {#stats是max,min全计算
"field": "score"
}
}
}
}
}
}
1.9.3 概念区分
"a":{
"terms:{..}//关键字分桶
"aggs":{
"b":{}
"c":{}
}
}
- a为顶级分桶,a-terms是a分桶的依据
- a-aggs中,表示在a的基础上再次分桶
- b,c是同级关系,相互独立 互不干扰,都是在a分桶的基础上分组