[TOC]
一. ElasticSearch介绍
1.1 引言
- 在海量数据中执行搜索功能时,如果使用MySQL,效率太低。
- 如果关键字输入的不准确,一样可以搜索到想要的数据。
- 将关键字,以红色的样式展示。
1.2 ES的介绍
ES是基于Java语言并且基于Lucene编写的搜索引擎框架,他提供了分布式的全文搜索功能,提供了一个统一的基于RESTful风格的WEB接口,官方客户端也对多种语言都提供了相应的API。
Lucene:是一个搜索引擎的底层。
全文搜索:将一段词语进行分词,并且将分出的单个词语统一的放到一个分词库中,在搜索时,根据关键字去分词库中检索,找到匹配的内容。
二. ElasticSearch安装
2.1 安装ES&Kibana图形化界面
version: "3.1"
services:
elasticsearch:
image: daocloud.io/library/elasticsearch:6.5.4
restart: always
container_name: elasticsearch
ports:
- 9200:9200
kibana:
image: daocloud.io/library/kibana:6.5.4
restart: always
container_name: kibana
ports:
- 5601:5601
environment:
- elasticsearch_url=http://192.168.0.17:9200
depends_on:
- elasticsearch
2.2 安装IK分词器
下载地址:https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.5.4/elasticsearch-analysis-ik-6.5.4.zip
进入ES容器内部,并跳转到bin目录下,执行bin目录下的脚本文件:
./elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.5.4/elasticsearch-analysis-ik-6.5.4.zip
安装IK之后一定要重启ES。
三. ES的基本操作
3.1 ES的结构
3.1.1索引Index,分片和备份
ES的服务中,可以创建多个索引。
每个索引默认会被分成5片存储。
每一个分片都会存在至少一个备份分片。
备份分片默认不会帮助检索数据,当ES检索压力特别大的时候,备份分片才会帮助检索数据。
备份分片必须放在不同的服务器中。
3.1.2 类型Type
一个索引下,可以创建多个类型。
Ps:根据版本不同,类型的创建也不同
3.1.3 文档Doc
一个类型下,可以有多个文档。这个文档就类似于MySQL表中的多行数据。
3.1.4 属性field
一个文档下,可以有多个属性。这个文档就类似于MySQL表中的一行数据存在多个列。
3.2 操作ES的RESTful语法
GET请求:
http://ip:port/index:查询索引信息
http://ip:port/index/type/doc_id:查询指定的文档信息
POST请求:
http://ip:port/index/type/_search:查询文档,可以在请求体中添加json字符串来代表查询条件
http://ip:port/index/type/doc_id/_update:修改文档,可以在请求体中指定json字符串代表修改的具体信息
PUT请求:
http://ip:port/index:创建一个索引,需要在请求体中指定索引的信息、类型和结构
http://ip:port/index/type/_mappings:代表创建索引时,指定索引文档存储的属性信息
DELETE请求:
http://ip:port/index:删除跑路
http://ip:port/index/type/doc_id:删除指定的文档
3.3 索引的操作
3.3.1 创建索引
# 创建索引
PUT /person
{
"settings": {
"number_of_shards": 5,
"number_of_replicas": 1
}
}
3.3.2 查看索引信息
# 查看索引信息
GET /person
3.3.3 删除索引
# 删除索引
DELETE /person
3.4 ES中Field可以指定的类型
字符串类型
text:一般被用于全文检索,将当前Field进行分词。
keyword:当前Field不会被分词。
数值类型:
long:
- integer:
- short:
- byte:
- double:
- float:
- half_float:精度比float小一半。
scaled_float:根据一个long和scaled来表达一个浮点型。
时间类型:
date类型:针对时间类型指定具体的格式。
布尔类型:
boolean:表达true和false。
二进制类型:
binary类型暂时支持Base64 encode string
范围类型:
https://www.elastic.co/guide/en/elasticsearch/reference/6.5/range.html
经纬度类型:
https://www.elastic.co/guide/en/elasticsearch/reference/6.5/geo-point.html
3.5 创建索引并指定数据结构
# 创建索引,指定数据结构
PUT /book # 索引名
{
"settings": {
"number_of_shards": 5, # 分片数
"number_of_replicas": 1 # 备份数
},
# 指定数据结构
"mappings": {
"novel": { # 类型 type
# 文档存储的Field
"properties": {
# Field属性名
"name": {
"type": "text", # 指定类型
"analyzer": "ik_max_word", # 指定类型
"index": true, # 指定当前的Field可以作为查询的条件
"store": false # 是否需要额外存储
},
"author": {
"type": "keyword"
},
"count": {
"type": "long"
},
"on-sale": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
},
"descr": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
}
3.6 文档的操作
文档在ES服务中的唯一标识,
_index
,_type
,_id
三个个内容为组合,锁定一个文档。
3.6.1 新建文档
自动生成_id
# 添加文档,自动生成id
POST /book/novel
{
"name": "盘龙",
"author": "whoami",
"count": 100000,
"on-sale": "2000-01-01",
"descr": "哈哈哈哈哈哈"
}
手动指定id
# 添加文档,手动指定id
PUT /book/novel/1
{
"name": "红楼梦",
"author": "曹雪芹",
"count": 10000000,
"on-sale": "1985-01-01",
"descr": "哈哈哈哈哈哈嘻嘻嘻嘻嘻"
}
3.6.2 修改文档
- 覆盖式修改
PUT /book/novel/1
{
"name": "红楼梦",
"author": "曹雪芹",
"count": 34344554,
"on-sale": "1985-01-01",
"descr": "哈哈哈哈哈哈嘻嘻嘻嘻嘻"
}
- doc修改方式
# 修改文档,基于doc方式
POST /book/novel/1/_update
{
"doc": {
# 指定需要修改的field和对应的值即可
"count": 1234567
}
}
3.6.3 删除文档
# 根据id删除文档
DELETE /book/novel/_id
四. Java操作elasticsearch
4.1 测试连接ES
创建Maven工程
导入依赖
<dependencies>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>6.5.4</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>6.5.4</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.22</version>
</dependency>
</dependencies>
创建测试类,连接ES
public class ESClient {
public static RestHighLevelClient getClient(){
// 创建HttpHost对象
HttpHost httpHost = new HttpHost("192.168.0.17",9200);
// 创建RestClientBuilder
RestClientBuilder clientBuilder = RestClient.builder(httpHost);
// 创建RestHighLevelClient
RestHighLevelClient client = new RestHighLevelClient(clientBuilder);
//返回
return client;
}
}
public class Demo1 {
public static void main(String[] args) {
RestHighLevelClient client = ESClient.getClient();
System.out.println("OK");
}
}
4.2 Java创建索引
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
String index = "person";
String type = "man";
// 1.准备关于索引的settings
Settings.Builder settings = Settings.builder()
.put("number_of_shards", 3)
.put("number_of_replicas", 1);
// 2.准备关于索引的结构mappings
XContentBuilder mappings = JsonXContent.contentBuilder()
.startObject()
.startObject("properties")
.startObject("name")
.field("type","text")
.endObject()
.startObject("age")
.field("type","integer")
.endObject()
.startObject("birthday")
.field("type","date")
.field("format","yyyy-MM-dd")
.endObject()
.endObject()
.endObject();
// 3.将settings和mappings封装到一个Request对象
CreateIndexRequest request = new CreateIndexRequest(index)
.settings(settings)
.mapping(type,mappings);
// 4.通过client对象去连接ES并执行创建索引
CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
// 5.输出测试
System.out.println(request.toString());
}
4.2.1 检查索引是否存在,删除索引
检查索引是否存在
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
String index = "person";
// 1. 准备request对象
GetIndexRequest request = new GetIndexRequest();
request.indices(index);
// 2.通过client去操作
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
// 3.输出
System.out.println(exists);
}
删除索引
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
String index = "person";
// 1. 准备request对象
DeleteIndexRequest request = new DeleteIndexRequest();
request.indices(index);
// 2.通过client去操作
AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
// 3.输出
System.out.println(delete.isAcknowledged());
}
4.3 Java操作文档
4.3.1 添加文档
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
ObjectMapper mapper = new ObjectMapper();
String index = "person";
String type = "man";
// 1.准备一个json数据
Person person = new Person(1,"张三",23,new Date());
String json = mapper.writeValueAsString(person);
// 2.准备一个request对象(手动指定id)
IndexRequest request = new IndexRequest(index,type,person.getId().toString());
request.source(json, XContentType.JSON);
// 3.通过client对象执行添加
IndexResponse resp = client.index(request, RequestOptions.DEFAULT);
// 4.输出返回结果
System.out.println(resp.getResult().toString());
}
4.3.2 修改文档
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
String index = "person";
String type = "man";
String docId = "1";
// 1.创建一个Map,指定需要修改的内容
Map<String,Object> doc = new HashMap<String, Object>();
doc.put("name","张大帅");
// 2.创建热区对象,封装数据
UpdateRequest request = new UpdateRequest(index,type,docId);
request.doc(doc);
// 3.通过client对象执行操作
UpdateResponse update = client.update(request, RequestOptions.DEFAULT);
// 4.输出返回结果
System.out.println(update.getResult().toString());
}
4.3.3 删除文档
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
String index = "person";
String type = "man";
// 1.封装Request对象
DeleteRequest request = new DeleteRequest(index,type,"1");
// 2.client执行
DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
// 3。输出结果
System.out.println(response.getResult().toString());
}
4.4 Java批量操作文档
4.4.1 批量添加文档
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
ObjectMapper mapper = new ObjectMapper();
String index = "person";
String type = "man";
// 1.准备多个json数据
Person p1 = new Person(1,"张三",23,new Date());
Person p2 = new Person(2,"李四",24,new Date());
Person p3 = new Person(3,"王五",25,new Date());
String json1 = mapper.writeValueAsString(p1);
String json2 = mapper.writeValueAsString(p2);
String json3 = mapper.writeValueAsString(p3);
// 2.创建Request,将准备好的数据封装进去
BulkRequest request = new BulkRequest();
request.add(new IndexRequest(index,type,p1.getId().toString()).source(json1, XContentType.JSON));
request.add(new IndexRequest(index,type,p2.getId().toString()).source(json2, XContentType.JSON));
request.add(new IndexRequest(index,type,p3.getId().toString()).source(json3, XContentType.JSON));
// 3.用client执行
BulkResponse resp = client.bulk(request, RequestOptions.DEFAULT);
// 4.输出结果
System.out.println(resp.toString());
}
4.4.2 批量删除文档
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
String index = "person";
String type = "man";
// 1.封装request对象
BulkRequest request = new BulkRequest();
request.add(new DeleteRequest(index,type,"1"));
request.add(new DeleteRequest(index,type,"2"));
request.add(new DeleteRequest(index,type,"3"));
// 2.执行client操作
BulkResponse resp = client.bulk(request, RequestOptions.DEFAULT);
// 3.输出结果
System.out.println(resp);
}
五. ES练习
索引:sms-logs-index
类型:sms-logs-type
字段名称 | 备注 |
---|---|
createDate | 创建时间 |
sendDate | 发送时间 |
longCode | 发送长号码,如"1069886622" |
mobile | 如:13800000000 |
corpName | 发送公司名称,需要分词检索 |
smsContent | 下发短信内容,需要分词检索 |
state | 短信下发状态 0 成功 1 失败 |
operatorId | 运营商编号 1 移动 2 联通 3 电信 |
proince | 省份 |
IpAddr | 下发服务器IP地址 |
replyTotal | 短信状态报告返回时长(秒) |
fee | 扣费(分) |
六. ES的各种查询
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SmsLogs {
private String id; //唯一ID
private Date createDate; //创建时间
private Date sendDate; //发送时间
private String longCode; //发送的长号码
private String mobile; //下发手机号
private String corpName; //发送公司名称
private String smsContent; //下发短信内容
private Integer state; //短信下发状态 0 成功 1 失败
private Integer operatorId; //运营商编号 1 移动 2 联通 3 电信
private String province; //省份
private String ipAddr; //下发服务器IP地址
private Integer replyTotal; //短信状态报告返回时长
private Integer fee; //费用
}
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
String index = "sms-logs-index";
String type = "sms-logs-type";
// 1.准备关于索引的settings
Settings.Builder settings = Settings.builder()
.put("number_of_shards", 3)
.put("number_of_replicas", 1);
// 2.准备关于索引的结构mappings
XContentBuilder mappings = JsonXContent.contentBuilder()
.startObject()
.startObject("properties")
.startObject("corpName")
.field("type","keyword")
.endObject()
.startObject("createDate")
.field("type","date")
.endObject()
.startObject("fee")
.field("type","long")
.endObject()
.startObject("ipAddr")
.field("type","ip")
.endObject()
.startObject("longCode")
.field("type","keyword")
.endObject()
.startObject("mobile")
.field("type","keyword")
.endObject()
.startObject("operatorId")
.field("type","integer")
.endObject()
.startObject("province")
.field("type","keyword")
.endObject()
.startObject("replyTotal")
.field("type","integer")
.endObject()
.startObject("sendDate")
.field("type","date")
.endObject()
.startObject("smsContent")
.field("type","text")
.field("analyzer","ik_max_word")
.endObject()
.startObject("state")
.field("type","integer")
.endObject()
.endObject()
.endObject();
// 3.将settings和mappings封装到一个Request对象
CreateIndexRequest request = new CreateIndexRequest(index)
.settings(settings)
.mapping(type,mappings);
// 4.通过client对象去连接ES并执行创建索引
CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
// 5.输出测试
System.out.println(request.toString());
}
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
ObjectMapper mapper = new ObjectMapper();
String index = "sms-logs-index";
String type = "sms-logs-type";
// 1.准备多个json数据
SmsLogs s1 = new SmsLogs("",new Date(),new Date(),"10660000988","13800000000","盒马鲜生","盒马鲜生盒马鲜生盒马鲜生盒马鲜生",
0,2,"上海","10.126.2.9",15,5);
SmsLogs s2 = new SmsLogs("",new Date(),new Date(),"10660000988","13800000000","嘀嘀打车","嘀嘀打车嘀嘀打车嘀嘀打车嘀嘀打车",
0,3,"北京","10.126.2.10",15,5);
SmsLogs s3 = new SmsLogs("",new Date(),new Date(),"10660000988","13800000000","中国移动","中国移动中国移动中国移动中国移动",
0,1,"沈阳","10.126.2.11",14,5);
SmsLogs s4 = new SmsLogs("",new Date(),new Date(),"10660000988","13800000000","招商银行","招商银行招商银行招商银行招商银行",
0,2,"大连","10.126.2.9",15,5);
SmsLogs s5 = new SmsLogs("",new Date(),new Date(),"10660000988","13800000000","途虎养车","途虎养车途虎养车途虎养车途虎养车",
0,2,"重庆","10.126.2.9",13,5);
SmsLogs s6 = new SmsLogs("",new Date(),new Date(),"10660000988","13800000000","建设银行","建设银行建设银行建设银行建设银行",
0,2,"鄂尔多斯","10.126.2.9",13,5);
SmsLogs s7 = new SmsLogs("",new Date(),new Date(),"10660000988","13800000000","中国移动","中国移动中国移动中国移动中国移动",
0,2,"赤峰","10.126.2.9",15,5);
SmsLogs s8 = new SmsLogs("",new Date(),new Date(),"10660000988","13800000000","盒马鲜生","盒马鲜生盒马鲜生盒马鲜生盒马鲜生",
0,2,"武汉","10.126.2.9",17,5);
SmsLogs s9 = new SmsLogs("",new Date(),new Date(),"10660000988","13800000000","嘀嘀打车","嘀嘀打车嘀嘀打车嘀嘀打车嘀嘀打车",
0,2,"上海","10.126.2.9",12,5);
SmsLogs s10 = new SmsLogs("",new Date(),new Date(),"10660000988","13800000000","途虎养车","途虎养车途虎养车途虎养车途虎养车",
0,2,"沈阳","10.126.2.9",11,5);
SmsLogs s11 = new SmsLogs("",new Date(),new Date(),"10660000988","13800000000","建设银行","建设银行建设银行建设银行建设银行",
0,2,"大连","10.126.2.9",10,5);
SmsLogs s12 = new SmsLogs("",new Date(),new Date(),"10660000988","13800000000","嘀嘀打车","嘀嘀打车嘀嘀打车嘀嘀打车嘀嘀打车",
0,2,"鄂尔多斯","10.126.2.9",19,5);
String json1 = mapper.writeValueAsString(s1);
String json2 = mapper.writeValueAsString(s2);
String json3 = mapper.writeValueAsString(s3);
String json4 = mapper.writeValueAsString(s4);
String json5 = mapper.writeValueAsString(s5);
String json6 = mapper.writeValueAsString(s6);
String json7 = mapper.writeValueAsString(s7);
String json8 = mapper.writeValueAsString(s8);
String json9 = mapper.writeValueAsString(s9);
String json10 = mapper.writeValueAsString(s10);
String json11 = mapper.writeValueAsString(s11);
String json12 = mapper.writeValueAsString(s12);
// 2.创建Request,将准备好的数据封装进去
BulkRequest request = new BulkRequest();
request.add(new IndexRequest(index,type,"1").source(json1, XContentType.JSON));
request.add(new IndexRequest(index,type,"2").source(json2, XContentType.JSON));
request.add(new IndexRequest(index,type,"3").source(json3, XContentType.JSON));
request.add(new IndexRequest(index,type,"4").source(json4, XContentType.JSON));
request.add(new IndexRequest(index,type,"5").source(json5, XContentType.JSON));
request.add(new IndexRequest(index,type,"6").source(json6, XContentType.JSON));
request.add(new IndexRequest(index,type,"7").source(json7, XContentType.JSON));
request.add(new IndexRequest(index,type,"8").source(json8, XContentType.JSON));
request.add(new IndexRequest(index,type,"9").source(json9, XContentType.JSON));
request.add(new IndexRequest(index,type,"10").source(json10, XContentType.JSON));
request.add(new IndexRequest(index,type,"11").source(json11, XContentType.JSON));
request.add(new IndexRequest(index,type,"12").source(json12, XContentType.JSON));
// 3.用client执行
BulkResponse resp = client.bulk(request, RequestOptions.DEFAULT);
// 4.输出结果
System.out.println(resp.toString());
}
6.1 term&terms查询
6.1.1 term查询java
term查询是代表完全匹配,搜索之前不会对你的搜索关键字进行分词,直接去文档分词库中去匹配内容。
# term查询
POST /sms-logs-index/sms-logs-type/_search
{
"from": 0,
"size": 5,
"query": {
"term": {
"province": {
"value": "北京"
}
}
}
}
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
ObjectMapper mapper = new ObjectMapper();
String index = "sms-logs-index";
String type = "sms-logs-type";
// 1.创建request对象
SearchRequest request = new SearchRequest(index);
request.types(type);
// 2.指定查询条件
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.from(0);
builder.size(5);
builder.query(QueryBuilders.termQuery("province","北京"));
request.source(builder);
// 3.执行查询
SearchResponse resp = client.search(request, RequestOptions.DEFAULT);
// 4.获取数据
for (SearchHit hit : resp.getHits().getHits()) {
Map<String, Object> result = hit.getSourceAsMap();
System.out.println(result);
}
}
6.1.2 terms查询
terms查询的机制跟term是一样的。
terms是针对一个字段包含多个值的时候用。
term:where province = 北京;
terms:where province = 北京 or province = ?or province = ?
# terms查询
POST /sms-logs-index/sms-logs-type/_search
{
"query": {
"terms": {
"province": [
"北京",
"上海"
]
}
}
}
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
ObjectMapper mapper = new ObjectMapper();
String index = "sms-logs-index";
String type = "sms-logs-type";
// 1.创建request对象
SearchRequest request = new SearchRequest(index);
request.types(type);
// 2.封装查询条件
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(QueryBuilders.termsQuery("province","北京","上海"));
request.source(builder);
// 3.执行查询
SearchResponse resp = client.search(request, RequestOptions.DEFAULT);
// 4.获取数据
for (SearchHit hit : resp.getHits().getHits()) {
Map<String, Object> result = hit.getSourceAsMap();
System.out.println(result);
}
}
6.2 match查询
match查询属于高层查询,他会根据你查询的字段类型不一样,采用不同的查询方式。
- 查询的是日期或者是数值的话,它会将你基于的字符串查询内容转换为日期或者数值对待。
- 查询的内容是一个不能被分词的内容(keyword),match查询不会对你指定的查询关键字进行分词。
- 如果是可以被分词的内容(text),match会将你指定的查询内容根据一定的方式分词,去分词库中匹配指定的内容。
match查询,实际底层是多个term查询,将多个term查询的结果给你封装到一起。
6.2.1 match_all查询
查询全部内容。不指定任何查询条件。
# match_all查询
POST /sms-logs-index/sms-logs-type/_search
{
"query": {
"match_all": {}
}
}
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
ObjectMapper mapper = new ObjectMapper();
String index = "sms-logs-index";
String type = "sms-logs-type";
// 1.创建request对象
SearchRequest request = new SearchRequest(index);
request.types(type);
// 2.指定查询条件
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(QueryBuilders.matchAllQuery());
builder.size(20); //ES默认查10条数据
request.source(builder);
// 3.执行查询
SearchResponse resp = client.search(request, RequestOptions.DEFAULT);
// 4.获取数据
for (SearchHit hit : resp.getHits().getHits()) {
Map<String, Object> result = hit.getSourceAsMap();
System.out.println(result);
}
}
6.2.2 match查询
指定一个Field作为筛选的条件
# match查询
POST /sms-logs-index/sms-logs-type/_search
{
"query": {
"match": {
"smsContent": "嘀嘀打车建设银行"
}
}
}
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
ObjectMapper mapper = new ObjectMapper();
String index = "sms-logs-index";
String type = "sms-logs-type";
// 1.创建request对象
SearchRequest request = new SearchRequest(index);
request.types(type);
// 2.指定查询条件
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(QueryBuilders.matchQuery("smsContent","嘀嘀打车建设银行"));
request.source(builder);
// 3.执行查询
SearchResponse resp = client.search(request, RequestOptions.DEFAULT);
// 4.获取数据
for (SearchHit hit : resp.getHits().getHits()) {
Map<String, Object> result = hit.getSourceAsMap();
System.out.println(result);
}
}
6.2.3 布尔match查询
基于一个Field匹配的内容,采用and或者or的方式连接
# 布尔match查询
POST /sms-logs-index/sms-logs-type/_search
{
"query": {
"match": {
"smsContent": {
"query": "嘀嘀 打车",
"operator": "and"
}
}
}
}
# 布尔match查询
POST /sms-logs-index/sms-logs-type/_search
{
"query": {
"match": {
"smsContent": {
"query": "嘀嘀 建设",
"operator": "or"
}
}
}
}
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
ObjectMapper mapper = new ObjectMapper();
String index = "sms-logs-index";
String type = "sms-logs-type";
// 1.创建request对象
SearchRequest request = new SearchRequest(index);
request.types(type);
// 2.指定查询条件
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(QueryBuilders.matchQuery("smsContent","嘀嘀 建设").operator(Operator.OR)); //Operator.AND
request.source(builder);
// 3.执行查询
SearchResponse resp = client.search(request, RequestOptions.DEFAULT);
// 4.获取数据
for (SearchHit hit : resp.getHits().getHits()) {
Map<String, Object> result = hit.getSourceAsMap();
System.out.println(result);
}
}
6.2.4 multi_match查询
match针对一个field做检索,multi_match针对多个field进行检索,多个field对应一个text。
# multi_match查询
POST /sms-logs-index/sms-logs-type/_search
{
"query": {
"multi_match": {
"query": "银行", # 指定text
"fields": ["smsContent","province"] # 指定field们
}
}
}
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
ObjectMapper mapper = new ObjectMapper();
String index = "sms-logs-index";
String type = "sms-logs-type";
// 1.创建request对象
SearchRequest request = new SearchRequest(index);
request.types(type);
// 2.指定查询条件
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(QueryBuilders.multiMatchQuery("银行","province","smsContent"));
request.source(builder);
// 3.执行查询
SearchResponse resp = client.search(request, RequestOptions.DEFAULT);
// 4.获取数据
for (SearchHit hit : resp.getHits().getHits()) {
Map<String, Object> result = hit.getSourceAsMap();
System.out.println(result);
}
}
6.3 其他查询
6.3.1 id查询
# id查询
GET /sms-logs-index/sms-logs-type/_id # 最后面添加要查询的id
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
ObjectMapper mapper = new ObjectMapper();
String index = "sms-logs-index";
String type = "sms-logs-type";
// 1.创建request对象
GetRequest request = new GetRequest(index,type,"3");
// 2.执行查询
GetResponse resp = client.get(request, RequestOptions.DEFAULT);
// 3.获取数据
System.out.println(resp.getSourceAsMap());
}
6.3.2 ids查询
根据多个id查询,类似于MySQL中的where id in (id1, id2, ....)
# ids查询
POST /sms-logs-index/sms-logs-type/_search
{
"query": {
"ids": {
"values": ["1","2","3"]
}
}
}
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
ObjectMapper mapper = new ObjectMapper();
String index = "sms-logs-index";
String type = "sms-logs-type";
// 1.创建request对象
SearchRequest request = new SearchRequest(index);
request.types(type);
// 2.指定查询条件
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(QueryBuilders.idsQuery().addIds("1","2","3"));
request.source(builder);
// 3.执行查询
SearchResponse resp = client.search(request, RequestOptions.DEFAULT);
// 4.获取数据
for (SearchHit hit : resp.getHits().getHits()) {
Map<String, Object> result = hit.getSourceAsMap();
System.out.println(result);
}
}
6.3.3 prefix查询
前缀查询,可以通过一个关键字去指定一个Field的前缀,从而查询到指定的文档。
# prefix查询
POST /sms-logs-index/sms-logs-type/_search
{
"query": {
"prefix": {
"corpName": {
"value": "中国"
}
}
}
}
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
ObjectMapper mapper = new ObjectMapper();
String index = "sms-logs-index";
String type = "sms-logs-type";
// 1.创建request对象
SearchRequest request = new SearchRequest(index);
request.types(type);
// 2.指定查询条件
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(QueryBuilders.prefixQuery("corpName","中国"));
request.source(builder);
// 3.执行查询
SearchResponse resp = client.search(request, RequestOptions.DEFAULT);
// 4.获取数据
for (SearchHit hit : resp.getHits().getHits()) {
Map<String, Object> result = hit.getSourceAsMap();
System.out.println(result);
}
}
6.3.4 fuzzy查询
模糊查询,我们输入字符的大概,ES就可以去根据输入的内容大概去匹配一下结果。
# fuzzy查询
POST /sms-logs-index/sms-logs-type/_search
{
"query": {
"fuzzy": {
"corpName": {
"value": "建设银航",
"prefix_length": 2 # 指定前面几个字符是不允许出现错误
}
}
}
}
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
ObjectMapper mapper = new ObjectMapper();
String index = "sms-logs-index";
String type = "sms-logs-type";
// 1.创建request对象
SearchRequest request = new SearchRequest(index);
request.types(type);
// 2.指定查询条件
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(QueryBuilders.fuzzyQuery("corpName","建设银航").prefixLength(2));
request.source(builder);
// 3.执行查询
SearchResponse resp = client.search(request, RequestOptions.DEFAULT);
// 4.获取数据
for (SearchHit hit : resp.getHits().getHits()) {
Map<String, Object> result = hit.getSourceAsMap();
System.out.println(result);
}
}
6.3.5 wildcard查询
通配查询,和MySQL中的like是一个套路,可以在查询时,在字符串中指定通配符*和占位符?
# wildcard查询
POST /sms-logs-index/sms-logs-type/_search
{
"query": {
"wildcard": {
"corpName": {
"value": "嘀嘀*"
}
}
}
}
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
ObjectMapper mapper = new ObjectMapper();
String index = "sms-logs-index";
String type = "sms-logs-type";
// 1.创建request对象
SearchRequest request = new SearchRequest(index);
request.types(type);
// 2.指定查询条件
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(QueryBuilders.wildcardQuery("corpName","嘀嘀*"));
request.source(builder);
// 3.执行查询
SearchResponse resp = client.search(request, RequestOptions.DEFAULT);
// 4.获取数据
for (SearchHit hit : resp.getHits().getHits()) {
Map<String, Object> result = hit.getSourceAsMap();
System.out.println(result);
}
}
6.3.6 range查询
范围查询,只针对数值类型,对某一个Field进行大于或者小于的范围
# range查询
POST /sms-logs-index/sms-logs-type/_search
{
"query": {
"range": {
"fee": {
"gte": 5,
"lte": 10
}
}
}
}
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
ObjectMapper mapper = new ObjectMapper();
String index = "sms-logs-index";
String type = "sms-logs-type";
// 1.创建request对象
SearchRequest request = new SearchRequest(index);
request.types(type);
// 2.指定查询条件
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(QueryBuilders.rangeQuery("fee").gte(5).lte(10));
request.source(builder);
// 3.执行查询
SearchResponse resp = client.search(request, RequestOptions.DEFAULT);
// 4.获取数据
for (SearchHit hit : resp.getHits().getHits()) {
Map<String, Object> result = hit.getSourceAsMap();
System.out.println(result);
}
}
6.3.7 regexp查询
正则查询,通过你编写的正则表达式去匹配内容.
# regexp查询
POST /sms-logs-index/sms-logs-type/_search
{
"query": {
"regexp": {
"mobile": "180[0-9]{8}"
}
}
}
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
ObjectMapper mapper = new ObjectMapper();
String index = "sms-logs-index";
String type = "sms-logs-type";
// 1.创建request对象
SearchRequest request = new SearchRequest(index);
request.types(type);
// 2.指定查询条件
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(QueryBuilders.regexpQuery("mobile","180[0-9]{8}"));
request.source(builder);
// 3.执行查询
SearchResponse resp = client.search(request, RequestOptions.DEFAULT);
// 4.获取数据
for (SearchHit hit : resp.getHits().getHits()) {
Map<String, Object> result = hit.getSourceAsMap();
System.out.println(result);
}
}
6.4 深分页Scroll
ES对from+size是有限制的,from和size二者之和不能超过1W。
原理:
form+size在ES查询数据的方式:
- 第一步,将用户指定的关键字进行分词。
- 第二步,将词汇去分词库中进行检索,得到多个文档的id。
- 第三步,去各个分片中拉取指定的数据,耗时较长。
- 第四步,将数据根据score进行排序,耗时较长。
- 第五步,根据from的值,将查询到的数据舍弃一部分。
- 第六步,返回结果。
Scroll+size在ES查询数据的方式:
- 第一步,将用户指定的关键字进行分词。
- 第二步,将词汇去分词库中检索,得到多个文档的id。
- 第三步,将文档的id存放在ES的上下文中。
- 第四步,根据指定的size去ES中检索指定的数据,拿完数据文档id,会从上下文中移除。
- 第五步,如果需要下一页数据,直接去ES的上下文中找后续的内容。
Scroll的弊端:不适合做实时查询。
# 执行Scroll查询,返回第一页数据,并且将文档id信息存放在ES上下文中,指定生存时间1分钟
POST /sms-logs-index/sms-logs-type/_search?scroll=1m
{
"query": {
"match_all": {}
},
"size": 2,
"sort": [
{
"fee": {
"order": "desc"
}
}
]
}
# 根据scroll查询下一页数据
POST /_search/scroll
{
"scroll_id": "根据第一步得到的scroll_id去指定",
"scroll": "1m"
}
# 删除scroll在ES上下文中的数据
DELETE /_search/scroll/scroll_id
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
ObjectMapper mapper = new ObjectMapper();
String index = "sms-logs-index";
String type = "sms-logs-type";
// 1.创建request对象
SearchRequest request = new SearchRequest(index);
request.types(type);
// 2.指定scroll信息
request.scroll(TimeValue.timeValueMinutes(1L));
// 3.指定查询的条件
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.size(4);
builder.sort("fee", SortOrder.DESC);
builder.query(QueryBuilders.matchAllQuery());
request.source(builder);
// 4.获取数据scroll_id,source
SearchResponse resp = client.search(request, RequestOptions.DEFAULT);
String scrollId = resp.getScrollId();
System.out.println("----------首页-----------");
for (SearchHit hit : resp.getHits().getHits()) {
System.out.println(hit.getSourceAsMap());
}
while (true){
// 5.循环 - 创建SearchScrollRequest
SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);
// 6.指定scrollId的生存时间
scrollRequest.scroll(TimeValue.timeValueMinutes(1L));
// 7.执行查询获取返回结果
SearchResponse scrollResp = client.scroll(scrollRequest, RequestOptions.DEFAULT);
// 8.判断是否查询到了数据
SearchHit[] hits = scrollResp.getHits().getHits();
if (hits != null && hits.length > 0) {
System.out.println("----------下一页-----------");
for (SearchHit hit : hits) {
System.out.println(hit.getSourceAsMap());
}
}else {
// 9.判断没有查询到数据 - 退出循环
System.out.println("----------结束-----------");
break;
}
}
// 10.创建ClearScrollRequest
ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
// 11.指定scrollId
clearScrollRequest.addScrollId(scrollId);
// 12.删除ScrollId
ClearScrollResponse clearScrollResponse = client.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
// 13.输出结果
System.out.println("删除scroll:"+clearScrollResponse.isSucceeded());
}
6.5 delete-by-query
根据term、match等查询方式去删除大量文档
Ps:如果需要删除的内容是index下的大部分数据,不推荐此方法,推荐创建一个全新的index,将保留的文档内容,添加到全新的索引中
# delete-by-query
POST /sms-logs-index/sms-logs-type/_delete_by_query
{
"query": {
"range": {
"fee": {
"lt": 5
}
}
}
}
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
ObjectMapper mapper = new ObjectMapper();
String index = "sms-logs-index";
String type = "sms-logs-type";
// 1.创建request对象
DeleteByQueryRequest request = new DeleteByQueryRequest(index);
request.types(type);
// 2.指定查询条件
request.setQuery(QueryBuilders.rangeQuery("fee").lt(5));
// 3.执行查询
BulkByScrollResponse resp = client.deleteByQuery(request, RequestOptions.DEFAULT);
// 4.获取数据
System.out.println(resp.toString());
}
6.6 复合查询
6.6.1 bool查询
复合过滤器,将你的多个查询条件,以一定的逻辑组合在一起。
- must:所有的条件用must组合在一起,表示And的意思。
- must_not:将must_not中的条件,全部都不匹配,表示Not的意思。
- should:所有的条件用should组合在一起,表示or的意思。
# 查询省份为武汉或北京
# 运营商不是联通
# smsContent中包含嘀嘀和打车
# bool查询
POST /sms-logs-index/sms-logs-type/_search
{
"query": {
"bool": {
"should": [
{
"term": {
"province": {
"value": "北京"
}
}
},
{
"term": {
"province": {
"value": "武汉"
}
}
}
],
"must_not": [
{
"term": {
"operatorId": {
"value": "2"
}
}
}
],
"must": [
{
"match": {
"smsContent": "嘀嘀"
}
},
{
"match": {
"smsContent": "打车"
}
}
]
}
}
}
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
ObjectMapper mapper = new ObjectMapper();
String index = "sms-logs-index";
String type = "sms-logs-type";
// 1.创建request对象
SearchRequest request = new SearchRequest(index);
request.types(type);
// 2.指定查询条件
SearchSourceBuilder builder = new SearchSourceBuilder();
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// 查询省份为武汉或者北京
boolQuery.should(QueryBuilders.termQuery("province","北京"));
boolQuery.should(QueryBuilders.termQuery("province","武汉"));
// 运营商不是联通
boolQuery.mustNot(QueryBuilders.termQuery("operatorId",2));
// smsContent中包含嘀嘀和打车
boolQuery.must(QueryBuilders.matchQuery("smsContent","嘀嘀"));
boolQuery.must(QueryBuilders.matchQuery("smsContent","打车"));
builder.query(boolQuery);
request.source(builder);
// 3.执行查询
SearchResponse resp = client.search(request, RequestOptions.DEFAULT);
// 4.获取数据
for (SearchHit hit : resp.getHits().getHits()) {
System.out.println(hit.getSourceAsMap());
}
}
6.6.2 boosting查询
boosting查询可以帮助我们去影响查询后的score。
- positive:只有匹配上positive的查询的内容,才会被放到返回的结果集中。
- negative:如果匹配上positive并且也匹配上了negative,就可以降低这样文档的score。
- negative_boost:指定系数,必须小于1.0。
关于查询时,分数是如何计算的:
- 搜索的关键字在文档中出现的频次越高,分数就越高。
- 指定的文档内容越短,分数越高。
- 在搜索时,指定的关键字也会被分词,这个 被分词的内容,在分词库中匹配的个数越多,分数越高。
# boosting查询
POST /sms-logs-index/sms-logs-type/_search
{
"query": {
"boosting": {
"positive": {
"match": {
"smsContent": "嘀嘀打车"
}
},
"negative": {
"match": {
"smsContent": "打车" # 在positive匹配的基础上再去匹配positive匹配得到的内容
}
},
"negative_boost": 0.5
}
}
}
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
ObjectMapper mapper = new ObjectMapper();
String index = "sms-logs-index";
String type = "sms-logs-type";
// 1.创建request对象
SearchRequest request = new SearchRequest(index);
request.types(type);
// 2.指定查询条件
SearchSourceBuilder builder = new SearchSourceBuilder();
BoostingQueryBuilder boostingQuery = QueryBuilders.boostingQuery(
QueryBuilders.matchQuery("smsContent", "嘀嘀打车"),
QueryBuilders.matchQuery("smsContent", "打车")
).negativeBoost(0.5f);
builder.query(boostingQuery);
request.source(builder);
// 3.执行查询
SearchResponse resp = client.search(request, RequestOptions.DEFAULT);
// 4.获取数据
for (SearchHit hit : resp.getHits().getHits()) {
Map<String, Object> result = hit.getSourceAsMap();
System.out.println(result);
}
}
6.7 filter查询
query查询,根据查询的条件,去计算文档的匹配度得到一个分数,并且根据分数进行排序,不会做缓存。
filter查询,根据查询的条件去查询文档,但不会去计算分数,而且filter会对经常被过滤的数据进行缓存。
# filter查询
POST /sms-logs-index/sms-logs-type/_search
{
"query": {
"bool": {
"filter": [
{
"term": {
"corpName": "途虎养车"
}
},
{
"range": {
"fee": {
"lte": 5
}
}
}
]
}
}
}
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
ObjectMapper mapper = new ObjectMapper();
String index = "sms-logs-index";
String type = "sms-logs-type";
// 1.创建request对象
SearchRequest request = new SearchRequest(index);
request.types(type);
// 2.指定查询条件
SearchSourceBuilder builder = new SearchSourceBuilder();
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
boolQuery.filter(QueryBuilders.termQuery("corpName","途虎养车"));
boolQuery.filter(QueryBuilders.rangeQuery("fee").lte(5));
builder.query(boolQuery);
request.source(builder);
// 3.执行查询
SearchResponse resp = client.search(request, RequestOptions.DEFAULT);
// 4.获取数据
for (SearchHit hit : resp.getHits().getHits()) {
Map<String, Object> result = hit.getSourceAsMap();
System.out.println(result);
}
}
6.8 高亮查询
高亮查询就是用户输入的关键字,以一定的特殊样式展示给用户。
ES提供了一个highlight属性,和query同级别。
- fragment_size:指定高亮数据展示多少个字符。
- pre_tags:指定前缀标签,例如:
- post_tags:指定后缀标签,例如:
- fields:指定哪几个Fields一高亮的形式返回
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
ObjectMapper mapper = new ObjectMapper();
String index = "sms-logs-index";
String type = "sms-logs-type";
// 1.创建request对象
SearchRequest request = new SearchRequest(index);
request.types(type);
// 2.指定查询条件
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(QueryBuilders.matchQuery("smsContent","嘀嘀"));
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("smsContent",6)
.preTags("<font color='red'>")
.postTags("</font>");
builder.highlighter(highlightBuilder);
request.source(builder);
// 3.执行查询
SearchResponse resp = client.search(request, RequestOptions.DEFAULT);
// 4.获取数据
for (SearchHit hit : resp.getHits().getHits()) {
System.out.println(hit.getHighlightFields().get("smsContent"));
}
}
6.9 聚合查询
ES的聚合查询和MySQL的聚合查询类似,ES的聚合查询相比MySQL强大的多,ES提供的统计数据的方式多得多。
# ES的聚合查询的RESTful语法
POST /index/type/_search
{
"aggs": {
"名字(agg)": {
"agg_type": {
"属性": "值"
}
}
}
}
6.9.1 去重计数查询
去重计数,即Cardinality,将返回的文档中的一个指定的field进行去重,统计一共有多少条。
POST /sms-logs-index/sms-logs-type/_search
{
"aggs": {
"agg": {
"cardinality": {
"field": "province"
}
}
}
}
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
ObjectMapper mapper = new ObjectMapper();
String index = "sms-logs-index";
String type = "sms-logs-type";
// 1.创建request对象
SearchRequest request = new SearchRequest(index);
request.types(type);
// 2.指定查询条件
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.aggregation(AggregationBuilders.cardinality("agg").field("province"));
request.source(builder);
// 3.执行查询
SearchResponse resp = client.search(request, RequestOptions.DEFAULT);
// 4.获取数据
Cardinality agg = resp.getAggregations().get("agg");
System.out.println(agg.getValue());
}
6.9.2 范围统计
统计一定范围内出现的文档个数。
范围统计可以针对普通的数值、时间类型、IP类型都可以统计。
数值统计
POST /sms-logs-index/sms-logs-type/_search
{
"aggs": {
"a": {
"range": {
"field": "fee",
"ranges": [
{
"to": 5
},
{
"from": 5, # from包含等于
"to": 10
},
{
"from": 10
}
]
}
}
}
}
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
ObjectMapper mapper = new ObjectMapper();
String index = "sms-logs-index";
String type = "sms-logs-type";
// 1.创建request对象
SearchRequest request = new SearchRequest(index);
request.types(type);
// 2.指定查询条件
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.aggregation(AggregationBuilders.range("agg").field("fee")
.addUnboundedTo(5)
.addRange(5,10)
.addUnboundedFrom(10)
);
request.source(builder);
// 3.执行查询
SearchResponse resp = client.search(request, RequestOptions.DEFAULT);
// 4.获取数据
Range agg = resp.getAggregations().get("agg");
for (Range.Bucket bucket : agg.getBuckets()) {
String key = bucket.getKeyAsString();
Object from = bucket.getFrom();
Object to = bucket.getTo();
long docCount = bucket.getDocCount();
System.out.println(String.format("key:%s,form:%s,to:%s,docCount:%s",key,from,to,docCount));
}
}
时间范围统计
POST /sms-logs-index/sms-logs-type/_search
{
"aggs": {
"agg": {
"date_range": {
"field": "createDate",
"format": "yyyy",
"ranges": [
{
"to": 2000
},
{
"from": 2000
}
]
}
}
}
}
IP范围统计
POST /sms-logs-index/sms-logs-type/_search
{
"aggs": {
"agg": {
"ip_range": {
"field": "ipAddr",
"ranges": [
{
"to": "10.126.2.10"
},
{
"from": "10.126.2.10"
}
]
}
}
}
}
6.9.3 统计聚合查询
它可以帮你指定Field的最大值、最小值、平均值。。。。
使用:extended_stats
POST /sms-logs-index/sms-logs-type/_search
{
"aggs": {
"agg": {
"extended_stats": {
"field": "fee"
}
}
}
}
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
ObjectMapper mapper = new ObjectMapper();
String index = "sms-logs-index";
String type = "sms-logs-type";
// 1.创建request对象
SearchRequest request = new SearchRequest(index);
request.types(type);
// 2.指定查询条件
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.aggregation(AggregationBuilders.extendedStats("agg").field("fee"));
request.source(builder);
// 3.执行查询
SearchResponse resp = client.search(request, RequestOptions.DEFAULT);
// 4.获取数据
ExtendedStats agg = resp.getAggregations().get("agg");
System.out.println(agg.getCount());
System.out.println(agg.getMin());
System.out.println(agg.getMax());
System.out.println(agg.getAvg());
System.out.println(agg.getSum());
}
6.10 地图经纬度搜索
ES中提供了一个数据类型geo_point,这个类型就是用来存储经纬度的。
先创建测试数据
PUT /map
{
"settings": {
"number_of_shards": 5,
"number_of_replicas": 1
},
"mappings": {
"map": {
"properties": {
"name": {
"type": "text"
},
"location": {
"type": "geo_point"
}
}
}
}
}
PUT /map/map/1
{
"name": "天安门",
"location": {
"lon": 116.403981,
"lat": 39.914492
}
}
PUT /map/map/2
{
"name": "海淀公园",
"location": {
"lon": 116.302509,
"lat": 39.991152
}
}
PUT /map/map/3
{
"name": "北京动物园",
"location": {
"lon": 116.343184,
"lat": 39.947468
}
}
6.10.1 ES的地图检索方式
- geo_distance:直线距离检索方式
- geo_bounding_box:以两个点确定一个矩形,获取在矩形内的全部数据
- geo_polygon:以多个点,确定一个多边形,获取多边形内的全部数据
6.10.2 基于RESTful实现地图检索
- geo_distance
POST /map/map/_search
{
"query": {
"geo_distance": {
"location": { # 确定一个点
"lon": 116.433733,
"lat": 39.908404
},
"distance": 3000, # 确定半径
"distance_type": "arc" # 指定形状为圆形
}
}
}
- geo_bounding_box
POST /map/map/_search
{
"query": {
"geo_bounding_box": {
"location": {
"top_left": { # 左上角坐标点
"lon": 116.326943,
"lat": 39.95499
},
"bottom_right": { # 右下角坐标点
"lon": 116.347783,
"lat": 39.939281
}
}
}
}
}
- geo_polygon
POST /map/map/_search
{
"query": {
"geo_polygon": {
"location": {
"points": [ # 指定多个点确定一个多边形
{
"lon": 116.298772,
"lat": 39.998227
},
{
"lon": 116.295394,
"lat": 39.975672
},
{
"lon": 116.323206,
"lat": 39.989936
}
]
}
}
}
}
6.10.3 Java实现geo_polygon
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ESClient.getClient();
ObjectMapper mapper = new ObjectMapper();
String index = "map";
String type = "map";
// 1.创建request对象
SearchRequest request = new SearchRequest(index);
request.types(type);
// 2.指定查询条件
SearchSourceBuilder builder = new SearchSourceBuilder();
List<GeoPoint> points = new ArrayList<GeoPoint>();
points.add(new GeoPoint(39.998227 ,116.298772));
points.add(new GeoPoint(39.975672,116.295394));
points.add(new GeoPoint(39.989936,116.323206));
builder.query(QueryBuilders.geoPolygonQuery("location",points));
request.source(builder);
// 3.执行查询
SearchResponse resp = client.search(request, RequestOptions.DEFAULT);
// 4.获取数据
for (SearchHit hit : resp.getHits().getHits()) {
System.out.println(hit.getSourceAsMap());
}
}