title: ElasticSearch
date: 2020/07/21
tags:
分布式搜索和分析引擎,主要实现近乎实时的搜索和分析。可以实现海量数据的存储(⽀持PB级别的数
据),可以实现快速搜索(倒排索引)还可以实现数据分析处理.
实现海量数据下快速搜索
绝对不使⽤模糊查询,因为性能太低。
解决海量数据的快速搜索,就需要使⽤全⽂检索技术,选择ElasticSearch
市⾯上有全⽂检索技术:Lucene、Solr(底层:Lucene)、ElasticSearch(底层:Lucene)
⽬前市⾯上:ElasticSearch
ELK:ElasticSearch(存储、搜索)+Logstash(采集)+Kibana(可视化) ⽇志平台
主要存储的查询的数据,⽀持海量数据存储和查询,⼀定要带条件
主流:ElasticSearch
ElasticSearch是基于Lucene开发的分布式搜索框架,包含如下特性:
动词 ,相当于Mysql 中的insert
名词, 相当于Mysql 中的 Database
在Index(索引)中 ,可以定义一个或多个类型。
类似Mysql 中的table ,每一种类型的数据放在一起
_doc: ElasticSearch 新版本默认的类型
保存在某个索引(index)下,某种类型(type)的一个数据(Document) ,文档是json 格式的,Document 就像是Mysql 中某个table里面的内容。
MySQL | ElasticSearch |
---|---|
DataBase 数据库 | Index 索引 |
Table 表 | Type 类型 |
Row 一行数据 | Document 文档 |
1. cd /usr/local/bin #切换到安装路径
2. wget https://github.com/docker/compose/releases/download/1.14.0-rc2/docker-compose-Linux-x86_64 # 下载安装源
3. rename docker-compose-Linux-x86_64 docker-compose docker-compose-Linux-x86_64 # 重命名安装下载的安装包
4. chmod +x /usr/local/bin/docker-compose
5. docker-compose version # 查看是否安装成功
mkdir -p /opt/docker_elasticsearch/config
mkdir -p /opt/docker_elasticsearch/data
mkdir -p /opt/docker_elasticsearch/plugins
echo "http.host: 0.0.0.0" >> /opt/docker_elasticsearch/config/elasticsearch.yml
chmod 777 config
chmod 777 data
vi docker-compose.yml
version: "3.1"
services:
elasticsearch:
image: daocloud.io/library/elasticsearch:7.4.2
restart: always
container_name: elasticsearch
environment:
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
volumes:
- /opt/docker_elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
- /opt/docker_elasticsearch/data:/usr/share/elasticsearch/data
- /opt/docker_elasticsearch/plugins:/usr/share/elasticsearch/plugins
ports:
- 9200:9200
- 9300:9300
kibana:
image: daocloud.io/library/kibana:7.4.2
restart: always
container_name: kibana
ports:
- 5601:5601
environment:
- elasticsearch_url=http://120.26.105.43:9200
depends_on:
- elasticsearch
执行操作yml文件没脸 拉取镜像 **docker-compose up -d **
启动日志:docker-compose logs -f
暂未启动完毕
es: http://120.26.105.43:9200/
出现json数据则启动成功
{
"name" : "2fd261b080fb",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "k9AKmU-1QdS-3GecdD0MOg",
"version" : {
"number" : "7.4.2",
"build_flavor" : "default",
"build_type" : "docker",
"build_hash" : "2f90bbf7b93631e52bafb59b3b049cb44ec25e96",
"build_date" : "2019-10-28T20:40:44.881551Z",
"build_snapshot" : false,
"lucene_version" : "8.2.0",
"minimum_wire_compatibility_version" : "6.8.0",
"minimum_index_compatibility_version" : "6.0.0-beta1"
},
"tagline" : "You Know, for Search"
}
kibana: http://120.26.105.43:5601/
图形化界面操作es
docker exec -it elasticsearch bash
cd bin/
./elasticsearch-plugin install https://ghproxy.com/https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.4.2/elasticsearch-analysis-ik-7.4.2.zip
# 查看是否安装完毕
./elasticsearch-plugin list
analysis-ik # ik分词器
安装完毕后退出容器重启es 执行操作yml文件:docker-compose restart
若安装失败需删除插件重新安装 bin目录下
./elasticsearch-plugin list
./elasticsearch-plugin remove analysis-ik
# 默认分词器
POST _analyze
{
"text": ["中国伟大"]
}
# ik分词器
# 粗粒度分词
POST _analyze
{
"text": ["中国伟大"]
, "analyzer": "ik_smart"
}
# ik分词器
# 细粒度分词
POST _analyze
{
"text": ["寇哲邱莹莹快快乐乐身体健康"]
, "analyzer": "ik_max_word"
}
第一步:进入ES容器。命令:docker exec -it es /bin/bash
第二步:在ES的配置文件中添加以下配置 命令:vi config/elasticsearch.yml
#添加如下内容
http.cors.enabled: true
http.cors.allow-origin: "*"
http.cors.allow-headers: Authorization
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true
第三步:重启es docker restart es 之后再次进入容器输入后 设置密码 大概10几种
./bin/elasticsearch-setup-passwords interactive
POST /_security/user/elastic/_password
{
"password": "123456"
}
修改密码之后,需要重新设置kibana的配置文件,才可以重新使用kibana
第一步:进入kibana容器。命令:docker exec -it kibana /bin/bash
第二步:配置账号密码,在kibana.yml配置文件中添加以下内容。 命令:vi config/kibana.yml
#添加以下内容
elasticsearch.username: "elastic"
elasticsearch.password: "你在es中设置的密码"
退出容器 重启即可
字符串类型:text:一把被用于全文检索。 将当前Field进行分词。
keyword:当前Field不会被分词。
数值类型:
long:取值范围为-9223372036854774808~922337203685477480(-2的63次方到2的63次方-1),占用8个字节
integer:取值范围为-2147483648~2147483647(-2的31次方到2的31次方-1),占用4个字节
short:取值范围为-32768~32767(-2的15次方到2的15次方-1),占用2个字节
byte:取值范围为-128~127(-2的7次方到2的7次方-1),占用1个字节
double:1.797693e+308~ 4.9000000e-324 (e+308表示是乘以10的308次方,e-324表示乘以10的负324次方)占用8个字节
float:3.402823e+38 ~ 1.401298e-45(e+38表示是乘以10的38次方,e-45表示乘以10的负45次方),占用4个字节
half_float:精度比float小一半。
scaled_float:根据一个long和scaled来表达一个浮点型,long-345,scaled-100 -> 3.45
布尔类型:
boolean类型,表达true和false
二进制类型:
binary类型暂时支持Base64 encode string
时间类型:
date类型,针对时间类型指定具体的格式
format 指定时间格式 yyyy-MM-dd
范围类型:
long_range:赋值时,无需指定具体的内容,只需要存储一个范围即可,指定gt,lt,gte,lte
integer_range:同上
double_range:同上
float_range:同上
date_range:同上
ip_range:同上
经纬度类型:
geo_point:用来存储经纬度的:经度/纬度
ip类型:
ip:可以存储IPV4或者IPV6
其他的数据类型参考官网(opens new window)
ctrl i 自动补全
ctrl / 打开帮助文档
ctrl Enter 执行当前选中的请求
# 查看所有节点
GET /_cat/nodes
# 查看es 健康状况
GET /_cat/health
#查看主节点
GET /_cat/master
#查看所有索引;
GET /_cat/indices
#查询索引信息,相当于查看 数据库表结构
GET /sms_log_test
#删除索引
DELETE book
# 创建索引,重复执行会报错
PUT test1
# 创建索引同时手动指定配置信息
PUT test2
{
"settings": {
"number_of_shards": 3, 分片
"number_of_replicas": 1 备份
}
}
# 查询索引详情
GET test2
# 查询指定的文档信息
GET sms_log_test/_doc/doc_id
# 创建索引,指定数据结构
PUT /book
{
"settings": {
# 分片数
"number_of_shards": 5,
# 备份数
"number_of_replicas": 1
},
# 指定数据结构
"mappings": {
# 文档存储的Field
"properties": {
# Field属性名
"name": {
# 类型
"type": "text",
# 指定当前Field可以被作为查询的条件
"index": true ,
# 是否需要额外存储
"store": false
},
"author": {
# keyword 也算是字符串类型
"type": "keyword"
},
"count": {
"type": "long"
},
"on-sale": {
"type": "date",
# 时间类型的格式化方式
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
},
"descr": {
"type": "text"
}
}
}
}
# 添加文档,手动指定id 推荐使用
(可不指定Id:PUT /book/_doc/)
PUT /book/_doc/1
{
"name": "红楼梦",
"author": "曹雪芹",
"count": 10000000,
"on-sale": "1985-01-01",
"descr": "一个是阆苑仙葩,一个是美玉无瑕"
}
# 修改文档 覆盖式修改 如果没有指定某个属性 这个属性会被覆盖掉 覆盖没了
PUT /book/_doc/1
{
"name": "红楼梦",
"author": "曹雪芹",
"count": 4353453,
"on-sale": "1985-01-01",
"descr": "一个是阆苑仙葩,一个是美玉无瑕"
}
# 删除文档
DELETE sms_log_test/_doc/doc_id
Java代码均为原始操作Es,**非 import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; **
新增文档 id一致相当于update
<elasticsearch.version>7.4.2elasticsearch.version>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-elasticsearchartifactId>
dependency>
yml配置
spring:
elasticsearch:
# 指定ElasticSearch连接信息
uris: http://Ip:9200
方式二:密码连接
elasticsearch:
schema: http
address: 120.26.105.43:9200
connectTimeout: 5000
socketTimeout: 5000
connectionRequestTimeout: 5000
maxConnectNum: 100
maxConnectPerRoute: 100
userName: elastic
password: Kz898989
Configuration
@Configuration
public class ElasticSearchConfiguration {
/** 协议 */
@Value("${elasticsearch.schema:http}")
private String schema;
/** 集群地址,如果有多个用“,”隔开 */
@Value("${elasticsearch.address}")
private String address;
/** 连接超时时间 */
@Value("${elasticsearch.connectTimeout}")
private int connectTimeout;
/** Socket 连接超时时间 */
@Value("${elasticsearch.socketTimeout}")
private int socketTimeout;
/** 获取连接的超时时间 */
@Value("${elasticsearch.connectionRequestTimeout}")
private int connectionRequestTimeout;
/** 最大连接数 */
@Value("${elasticsearch.maxConnectNum}")
private int maxConnectNum;
/** 最大路由连接数 */
@Value("${elasticsearch.maxConnectPerRoute}")
private int maxConnectPerRoute;
@Value("${elasticsearch.userName}")
private String userName;
@Value("${elasticsearch.password}")
private String password;
@Bean(name = "restHighLevelClient")
public RestHighLevelClient restHighLevelClient() {
// 拆分地址
List<HttpHost> hostLists = new ArrayList<>();
String[] hostList = address.split(",");
for (String addr : hostList) {
String host = addr.split(":")[0];
String port = addr.split(":")[1];
hostLists.add(new HttpHost(host, Integer.parseInt(port), schema));
}
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, password));
// 转换成 HttpHost 数组
HttpHost[] httpHost = hostLists.toArray(new HttpHost[]{});
// 构建连接对象
RestClientBuilder builder = RestClient.builder(httpHost);
// 异步连接延时配置
builder.setRequestConfigCallback(requestConfigBuilder -> {
requestConfigBuilder.setConnectTimeout(connectTimeout);
requestConfigBuilder.setSocketTimeout(socketTimeout);
requestConfigBuilder.setConnectionRequestTimeout(connectionRequestTimeout);
return requestConfigBuilder;
});
// 异步连接数配置
builder.setHttpClientConfigCallback(httpClientBuilder -> {
httpClientBuilder.setMaxConnTotal(maxConnectNum);
httpClientBuilder.setMaxConnPerRoute(maxConnectPerRoute);
return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
});
return new RestHighLevelClient(builder);
}
}
@Resource
private RestHighLevelClient esClient;
term的查询是代表完全匹配,搜索之前不会对你搜索的关键字进行分词,对你的关键字去文档分词库中去匹配内容。
# 查询
# term完整匹配 不进行分词
POST sms-logs-index/_search
{
"from": 0, limit
"size": 20,
"query": {
"term": {
"province": {
"value": "上海"
}
}
}
}
说明: term 不对查询条件进行分词,field是text或者keyword 类型
分别是 对文档内容分词(text)和不分词(keyword)
如果查询是keyword类型就使用term
# 查询结果说明
{
"took" : 2, # 查询用了2毫秒
"timed_out" : false, # 是否超时 没有超时
"_shards" : { # 分片信息
"total" : 3, # 一共使用三个分片
"successful" : 3, # 成功了三个分片
"skipped" : 0, # 跳过
"failed" : 0 # 失败
},
"hits" : { # 查询命中
"total" : { # 总命中
"value" : 2, # 命中数
"relation" : "eq" # 查询关系
},
"max_score" : 0.6931472, # 匹配分数 匹配度越高 分数越高
"hits" : [
{
"_index" : "sms-logs-index", 索引
"_type" : "_doc", 类型
"_id" : "21",
"_score" : 0.6931472,
"_source" : {
存入的JSON数据
}
}
]
}
}
public void termSearch(){
SearchRequest request = new SearchRequest("sms-logs-index");
//1.组装查询条件
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(QueryBuilders.termQuery("province","上海"));
request.source(builder);
SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);
//2.response
SearchHit[] hits = response.getHits().getHits();
for (SearchHit hit : hits) {
System.err.println(hit.getScore());
}
}
terms和term的查询机制是一样,都不会将指定的查询关键字进行分词,直接去分词库中匹配,找到相应文档内容。
terms是在针对一个字段包含多个值的时候使用。
term:where province = 北京;
terms:where province = 北京 or province = ?or province = ?
一个字段可以等于多个值 有点类似 in
builder.query(QueryBuilders.termsQuery(“province”,“上海”,“北京”));
match查询属于高层查询,他会根据你查询的字段类型不一样,采用不同的查询方式。
match查询,实际底层就是多个term查询,将多个term查询的结果给你封装到了一起。
public void match(){
try {
SearchRequest request = new SearchRequest("sms-logs-index");
//1.组装查询条件
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(QueryBuilders.matchQuery("province","上海").operator(Operator.OR));
request.source(builder);
SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);
//2.response
SearchHit[] hits = response.getHits().getHits();
for (SearchHit hit : hits) {
System.err.println(hit.getSourceAsString());
}
} catch (Exception e) {
throw new AppException(String.format("批量删除文档失败%s",e.getMessage()));
}
}
match针对一个field做检索,multi_match针对多个field进行检索,多个field对应一个text。
针对多个索引同时查询
builder.query(QueryBuilders.multiMatchQuery("province","上海","北京").operator(Operator.OR));
builder.query(QueryBuilders.idsQuery("province").addIds("郑州"));
针对keyword类型,可以进行前缀查询
builder.query(QueryBuilders.prefixQuery("province","台湾"));
模糊查询,我们输入字符的大概(比如 出现错别字),ES就可以去根据输入的内容大概去匹配一下结果。
prefixLength指定前面几个字符是不允许出现错误的
builder.query(QueryBuilders.fuzzyQuery("userName","张三").prefixLength(2));
通配查询,和MySQL中的like是一个套路,可以在查询时,在字符串中指定通配符*和占位符?
*号匹配多个字符 ?匹配一个字符
builder.query(QueryBuilders.wildcardQuery("userName","王五*"));
范围查询,只针对数值类型,对某一个Field进行大于或者小于的范围指定
可以使用 gt:> gte:>= lt:< lte:<=
builder.query(QueryBuilders.rangeQuery("fee").gt(5).lte(10));
正则查询,通过你编写的正则表达式去匹配内容。使用较多 模糊查询性能较低
https://www.sojson.com/regex/generate
builder.query(QueryBuilders.regexpQuery("mobile","0?(13|14|15|18|17)[0-9]{9}"));
ES对from + size是有限制的,from和size二者之和不能超过1W
原理:
- from+size在ES查询数据的方式:
- 第一步先将用户指定的关键进行分词。
- 第二步将词汇去分词库中进行检索,得到多个文档的id。
- 第三步去各个分片中去拉取指定的全部数据。耗时较长。
- 第四步将数据根据score进行排序。耗时较长。
- 第五步根据from的值,将查询到的数据舍弃一部分。
- 第六步返回结果。
- scroll+size在ES查询数据的方式:
- 第一步先将用户指定的关键进行分词。
- 第二步将词汇去分词库中进行检索,得到多个文档的id。
- 第三步将文档的id存放在一个ES的上下文中。
- 第四步根据你指定的size的个数去ES中检索指定个数的数据,拿完数据的文档id,会从上下文中移除。
- 第五步如果需要下一页数据,直接去ES的上下文中,找后续内容。
- 第六步循环第四步和第五步
Scroll查询方式,不适合做实时的查询
public void scroll(){
try {
SearchRequest request = new SearchRequest("sms-logs-index");
//设置ES上下文生产时间
request.scroll(TimeValue.timeValueMillis(1L));
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(QueryBuilders.matchAllQuery());
//设置每页展示几条数据
builder.size(3);
request.source(builder);
SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);
//分页Id
String scrollId = response.getScrollId();
System.err.println("首页数据"+scrollId+"--------------");
//2.response
SearchHit[] hits = response.getHits().getHits();
//第一页数据
for (SearchHit hit : hits) {
System.err.println(hit.getSourceAsString());
}
//下页数据
while (true){
SearchScrollRequest scrollReq = new SearchScrollRequest(scrollId);
//设置ES上下文生产时间
request.scroll(TimeValue.timeValueMillis(1L));
//发起下一页请求
SearchResponse scrollRes = esClient.scroll(scrollReq, RequestOptions.DEFAULT);
SearchHit[] hits1 = scrollRes.getHits().getHits();
//判断查询是否存在数据
if (!(hits1.length>0)){
System.err.println("最后一页-------------");
break;
}else {
for (SearchHit hit1 : hits1) {
System.err.println(hit1.getSourceAsString());
}
}
}
//清空ES上下文
ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
clearScrollRequest.addScrollId(scrollId);
ClearScrollResponse clearScrollResponse = esClient.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
System.err.println(clearScrollResponse.isSucceeded());
} catch (Exception e) {
throw new AppException(String.format(e.getMessage()));
}
}
查询并删除
//1. 创建DeleteByQueryRequest
DeleteByQueryRequest request = new DeleteByQueryRequest(index);
//2. 指定检索的条件 和SearchRequest指定Query的方式不一样
request.setQuery(QueryBuilders.rangeQuery("type").lt(4));
//3. 执行删除
BulkByScrollResponse resp = client.deleteByQuery(request, RequestOptions.DEFAULT);
//4. 输出返回结果
System.out.println(resp.toString());
复合过滤器,将你的多个查询条件,以一定的逻辑组合在一起。
- must: 所有的条件,用must组合在一起,表示And的意思
- must_not:将must_not中的条件,全部都不能匹配,标识Not的意思
- should:所有的条件,用should组合在一起,表示Or的意思
must 必须同时满足 and
should 任意条件符合即可 or
must_not 取反 not
public void Bool() throws IOException {
// 1、创建查询请求的对象
SearchRequest searchRequest = new SearchRequest("sms-logs-index");
// 2、查询条件构造器
SearchSourceBuilder builder = new SearchSourceBuilder();
// 3、设置查询条件
// ------------
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
// 查询省份为武汉或者北京
boolQueryBuilder.should(QueryBuilders.termQuery("province","武汉"));
boolQueryBuilder.should(QueryBuilders.termQuery("province","北京"));
// 运营商不是联通
boolQueryBuilder.mustNot(QueryBuilders.termQuery("operatorId",2));
// smsContent中包含中国和平安
boolQueryBuilder.must(QueryBuilders.matchQuery("smsContent","中国"));
boolQueryBuilder.must(QueryBuilders.matchQuery("smsContent","平安"));
builder.query(boolQueryBuilder);
// ------------
searchRequest.source(builder);
// 4、执行查询
SearchResponse response = esClient.search(searchRequest, RequestOptions.DEFAULT);
// 5、获取查询结果
SearchHit[] hits = response.getHits().getHits();
for (SearchHit hit : hits) {
Map<String, Object> map = hit.getSourceAsMap();
System.out.println(map);
}
}
过滤
//1. SearchRequest
SearchRequest request = new SearchRequest("sms-logs-index");
//2. 查询条件
SearchSourceBuilder builder = new SearchSourceBuilder();
BoolQueryBuilder boolQuery = new BoolQueryBuilder();
boolQuery.filter(QueryBuilders.termQuery("userName","张三"));
boolQuery.filter(QueryBuilders.rangeQuery("fee").lte(5));
builder.query(boolQuery);
request.source(builder);
//3. 执行查询
SearchResponse resp = esClient.search(request, RequestOptions.DEFAULT);
//4. 输出结果
for (SearchHit hit : resp.getHits().getHits()) {
System.out.println(hit.getSourceAsMap());
}
// @ResultType(PcDocument.class)
SearchRequest request = new SearchRequest("sms-logs-index");
//1.组装查询条件
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(QueryBuilders.matchQuery("test","测试"));
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("test",10)
.preTags("")
.postTags("");
builder.highlighter(highlightBuilder);
request.source(builder);
SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);
//2.response
SearchHit[] hits = response.getHits().getHits();
for (SearchHit hit : hits) {
System.err.println(hit);
System.err.println(hit.getHighlightFields());
}
//4. 获取高亮数据,输出
for (SearchHit hit : response.getHits().getHits()) {
System.out.println(hit.getHighlightFields().get("test"));
}
ES的聚合查询和MySQL的聚合查询类似,ES的聚合查询相比MySQL要强大的多,ES提供的统计数据的方式多种多样
https://www.elastic.co/guide/en/elasticsearch/reference/6.5/search-aggregations-bucket-datehistogram-aggregation.html
POST /sms-logs-index/_search
{
"aggs": {
"distant": { # 自定义名称
"cardinality": {
"field": "province" # 按照 field 进行去重
}
}
}
}
public void aggregation() throws IOException {
//1. SearchRequest
SearchRequest request = new SearchRequest("sms-logs-index");
SearchSourceBuilder builder = new SearchSourceBuilder();
//2.构造查询条件
builder.aggregation(AggregationBuilders.cardinality("agg").field("province"));
request.source(builder);
//3. 执行查询
SearchResponse resp = esClient.search(request, RequestOptions.DEFAULT);
Cardinality province = resp.getAggregations().get("agg");
long value = province.getValue();
System.err.println(value);
}
POST /sms-logs-index/_search
{
"aggs": {
"test": { # 指定name
"range": {
"field": "fee", # 字段
"ranges": [
{
"to": 5 //小于
},
{
"from": 5, // 大于
"to": 10 //小于
},
{
"from": 10
}
]
}
}
}
}
返回结果:
"aggregations" : {
"test" : {
"buckets" : [
{
"key" : "*-5.0",
"to" : 5.0,
"doc_count" : 4
},
{
"key" : "5.0-10.0",
"from" : 5.0,
"to" : 10.0,
"doc_count" : 8
},
{
"key" : "10.0-*",
"from" : 10.0,
"doc_count" : 0
}
]
}
}
public void aggregationTwo() throws IOException {
//1. SearchRequest
SearchRequest request = new SearchRequest("sms-logs-index");
SearchSourceBuilder builder = new SearchSourceBuilder();
//2.构造查询条件 相当于名称
builder.aggregation(AggregationBuilders
.range("test").field("fee")
.addUnboundedTo(5)
.addRange(5,10)
.addUnboundedFrom(10)
);
request.source(builder);
//3. 执行查询
SearchResponse resp = esClient.search(request, RequestOptions.DEFAULT);
// 根据名称获取
Range test = resp.getAggregations().get("test");
//获取存储桶
test.getBuckets().stream().forEach(bucket -> {
System.err.println(bucket.getKeyAsString());
System.err.println(bucket.getFromAsString());
System.err.println(bucket.getToAsString());
System.err.println(bucket.getDocCount());
});
}
日期范围:
# 时间方式范围统计 date_range format指定日期格式
POST /sms-logs-index/_search
{
"aggs": {
"agg": {
"date_range": {
"field": "createDate",
" ": "yyyy",
"ranges": [
{
"to": 2000
},
{
"from": 2000
}
]
}
}
}
}
ip范围:
# ip方式 范围统计 ip_range
POST /sms-logs-index/_search
{
"aggs": {
"agg": {
"ip_range": {
"field": "ipAddr",
"ranges": [
{
"to": "127.0.0.1"
},
{
"from": "10.157.2"
}
]
}
}
}
}
# 统计聚合查询 extended_stats
POST /sms-logs-index/_search
{
"aggs": {
"agg": {
"extended_stats": {
"field": "fee"
}
}
}
}
"aggregations" : {
"agg" : {
"count" : 12,
"min" : 3.0,
"max" : 8.0,
"avg" : 5.333333333333333,
"sum" : 64.0,
"sum_of_squares" : 376.0,
"variance" : 2.8888888888888906,
"std_deviation" : 1.6996731711975954,
"std_deviation_bounds" : {
"upper" : 8.732679675728523,
"lower" : 1.9339869909381422
}
}
}
public void extendedStats() throws IOException {
//1. SearchRequest
SearchRequest request = new SearchRequest("sms-logs-index");
SearchSourceBuilder builder = new SearchSourceBuilder();
//2.构造查询条件 相当于名称
builder.aggregation(AggregationBuilders
.extendedStats("test").field("fee"));
request.source(builder);
//3. 执行查询
SearchResponse resp = esClient.search(request, RequestOptions.DEFAULT);
ExtendedStats stats = resp.getAggregations().get("test");
double max = stats.getMax();
double min = stats.getMin();
System.err.println("最大值为:" + max + ",最小值为:" + min);
}
ES中提供了一个数据类型 geo_point,这个类型就是用来存储经纬度的。
创建一个带geo_point类型的索引,并添加测试数据 坐标拾取
# 创建一个索引,指定一个name,locaiton
# 创建一个map索引包含经纬度数据
PUT /map
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
},
"mappings": {
"properties": {
"name":{
"type": "text"
},
"location":{
"type": "geo_point" //类型为经纬度
}
}
}
}
PUT map/_doc/1
{
"name": "月季公园",
"location": {
"lon": 113.6368,
"lat": 34.773985
}
}
PUT map/_doc/2
{
"name": "郑州人民公园",
"location": {
"lon": 113.669581,
"lat": 34.768197
}
}
PUT map/_doc/3
{
"name": "火车站",
"location": {
"lon": 113.665988,
"lat": 34.752776
}
}
# 查看所有数据
GET map/_search
ES的地图检索方式
语法 | 说明 |
---|---|
geo_distance | 直线距离检索方式 |
geo_bounding_box | 以两个点确定一个矩形,获取在矩形内的全部数据 |
geo_polygon | 以多个点,确定一个多边形,获取多边形内的全部数据 |
POST map/_search
{
"query": {
"geo_distance": {
"location": {
"lon": 113.672455, # 经度
"lat": 34.758619 # 纬度
},
"distance": 2000, # 距离 单位米
"distance_type": "arc" # 距离类型 弧行
}
}
}
public void geoDistanceQuery() throws IOException {
//1. SearchRequest
SearchRequest request = new SearchRequest("map");
SearchSourceBuilder builder = new SearchSourceBuilder();
//地理边界框查询生成器
GeoDistanceQueryBuilder location = QueryBuilders.geoDistanceQuery("location");
location.point(34.758619,113.672455).distance(2000, DistanceUnit.METERS);
//2.构造查询条件 相当于名称
builder.query(location);
//3. 执行查询
SearchResponse resp = esClient.search(request, RequestOptions.DEFAULT);
for (SearchHit hit : resp.getHits().getHits()) {
System.err.println(hit.getSourceAsString());
}
}
# 两个点[左上角/右下角]确定一个矩形,查找内部的数据
POST map/_search
{
"query": {
"geo_bounding_box": {
"location": {
"top_left": {
"lon": 113.616689,
"lat": 34.768405
},
"bottom_right": {
"lon": 113.695452,
"lat": 34.768167
}
}
}
}
}
public void geoBoundingBoxQuery() throws IOException {
//1. SearchRequest
SearchRequest request = new SearchRequest("map");
SearchSourceBuilder builder = new SearchSourceBuilder();
//地理边界框查询生成器
GeoBoundingBoxQueryBuilder localhost = QueryBuilders.geoBoundingBoxQuery("location");
localhost.setCorners(34.778486,113.616976,34.747645,113.675186);
//2.构造查询条件 相当于名称
builder.query(localhost);
//3. 执行查询
SearchResponse resp = esClient.search(request, RequestOptions.DEFAULT);
for (SearchHit hit : resp.getHits().getHits()) {
System.err.println(hit.getSourceAsString());
}
}
POST map/_search
{
"query": {
"geo_polygon": {
"location": {
"points": [
{
"lon": 113.624306,
"lat": 34.778486
},
{
"lon": 113.616976,
"lat": 34.768286
},
{
"lon": 113.675186,
"lat": 34.747645
},
{
"lon": 113.687834,
"lat": 34.78406
}
]
}
}
}
}