ElasticSearch


title: ElasticSearch
date: 2020/07/21
tags:

  • 开发
    categories:
  • Java

ElasticSearch

一、ElasticSearch是什么

分布式搜索和分析引擎,主要实现近乎实时的搜索和分析。可以实现海量数据的存储(⽀持PB级别的数
据),可以实现快速搜索(倒排索引)还可以实现数据分析处理.

实现海量数据下快速搜索

绝对不使⽤模糊查询,因为性能太低。
解决海量数据的快速搜索,就需要使⽤全⽂检索技术,选择ElasticSearch
市⾯上有全⽂检索技术:Lucene、Solr(底层:Lucene)、ElasticSearch(底层:Lucene)
⽬前市⾯上:ElasticSearch
ELK:ElasticSearch(存储、搜索)+Logstash(采集)+Kibana(可视化) ⽇志平台
主要存储的查询的数据,⽀持海量数据存储和查询,⼀定要带条件
主流:ElasticSearch

1.1Elasticsearch的特性

ElasticSearch是基于Lucene开发的分布式搜索框架,包含如下特性:

  1. 分布式索引、搜索。
  2. 索引自动分片、负载均衡。
  3. 自动发现机器、组建集群。
  4. 支持Restful 风格接口。
  5. 配置简单等。
  6. 海量数据的存储。
  7. 高亮显示

1.2 基本概念

1.2.1 Index (索引)

动词 ,相当于Mysql 中的insert

名词, 相当于Mysql 中的 Database

1.2.2 Type (类型)

在Index(索引)中 ,可以定义一个或多个类型。
类似Mysql 中的table ,每一种类型的数据放在一起
_doc: ElasticSearch 新版本默认的类型

1.2.3 Document (文档)

保存在某个索引(index)下,某种类型(type)的一个数据(Document) ,文档是json 格式的,Document 就像是Mysql 中某个table里面的内容。

MySQL ElasticSearch
DataBase 数据库 Index 索引
Table 表 Type 类型
Row 一行数据 Document 文档

1.3 倒排索引

ElasticSearch_第1张图片

1.4 ES结构

  • ES的服务中,可以创建多个索引。
  • 每一个索引默认被分成5片存储。
  • 每一个分片都会存在至少一个备份分片。
  • 备份分片默认不会帮助检索数据,当ES检索压力特别大的时候,备份分片才会帮助检索数据。
  • 备份的分片必须放在不同的服务器中。理解: 索引index是es 中最大的数据存储单位 ,和mysql 的区别是 一个索引(index)中可以存海量(几亿条)数据 ,如果我们要在几亿条数据中检索出几条想要的数据 效率会很低 所以 es 提供了 一种对索引进行分片的机制 ,ES 天然支持集群,在集群服务器中 ES 把一个索引进行分片 放在不同的服务器上 如下图 例如 有一亿条数据 分成两个分片 每个分片上有5000万条数据 这样做的好处 一是 提高查询速度 二是 提高数据的存储量,另外 为了保证数据的安全 每个主分片会有备份分片 主分片和备份分片在不同的服务器上 , 比如 主分片2 挂掉了 在 ES服务1 上面 还有 主分片2的备份分片 ,这样在一定程度上保证了数据的安全性 避免数据的丢失。但是 如果 当前集群中 只有一台es服务器 那么 这台服务器上 放的都是主分片,没有备份分片,什么时候扩展了集群中的 另一台服务器 才会存放备份分片。
    | 索引分片备份 |
    | — |
    | ElasticSearch_第2张图片
    ElasticSearch_第3张图片 |

二、搭建ES Kibana

2.1 安装docker-compose

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 # 查看是否安装成功

2.2 拉起镜像

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

2.2.1 查看是否安装成功

暂未启动完毕
ElasticSearch_第4张图片
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
ElasticSearch_第5张图片

2.3 IK分词器

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

2.3.2 测试分词器

# 默认分词器
POST _analyze 
{
  "text": ["中国伟大"]
}

# ik分词器
# 粗粒度分词
POST _analyze 
{
  "text": ["中国伟大"]
  , "analyzer": "ik_smart"
}

# ik分词器
# 细粒度分词
POST _analyze 
{
  "text": ["寇哲邱莹莹快快乐乐身体健康"]
  , "analyzer": "ik_max_word"
}

2.4 设置密码

2.4.1 es pwd

第一步:进入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

ElasticSearch_第6张图片
修改密码

POST /_security/user/elastic/_password
{
  "password": "123456"
}

修改密码之后,需要重新设置kibana的配置文件,才可以重新使用kibana

2.4.2 kibana pwd

第一步:进入kibana容器。命令:docker exec -it kibana /bin/bash
第二步:配置账号密码,在kibana.yml配置文件中添加以下内容。 命令:vi config/kibana.yml

#添加以下内容
elasticsearch.username: "elastic"
elasticsearch.password: "你在es中设置的密码"

退出容器 重启即可

三、ES操作

3.1 Es Field可以指定类型

字符串类型: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)

3.2 Kibana快捷键操作

ctrl i 自动补全
ctrl / 打开帮助文档
ctrl Enter 执行当前选中的请求

3.3 索引操作


# 查看所有节点
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


ElasticSearch_第7张图片

3.3.1 创建指定数据结构索引

# 创建索引,指定数据结构
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"
      }
    }
  }
}

3.4 文档操作

# 添加文档,手动指定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

3.5 kibana 可视化界面中可以看到 创建的索引信息

ElasticSearch_第8张图片ElasticSearch_第9张图片
ElasticSearch_第10张图片ElasticSearch_第11张图片

四、Java操作ES Demo *

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;

五、ES查询 *

5.1 term查询

term的查询是代表完全匹配,搜索之前不会对你搜索的关键字进行分词,对你的关键字去文档分词库中去匹配内容。

# 查询 
# term完整匹配 不进行分词

POST sms-logs-index/_search
{
  "from": 0,   limit
  "size": 20, 
  "query": {
    "term": {
      "province": {
        "value": "上海"
      }
    }
  }
}

说明: term 不对查询条件进行分词,field是text或者keyword 类型
分别是 对文档内容分词(text)和不分词(keyword)
如果查询是keyword类型就使用term

5.1.2 查询结果说明

# 查询结果说明
{
  "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数据
        }
      }
    ]
  }
}

5.1.3 java操作Demo

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());
        }
}

5.2 terms查询

terms和term的查询机制是一样,都不会将指定的查询关键字进行分词,直接去分词库中匹配,找到相应文档内容。
terms是在针对一个字段包含多个值的时候使用。
term:where province = 北京;
terms:where province = 北京 or province = ?or province = ?
一个字段可以等于多个值 有点类似 in
builder.query(QueryBuilders.termsQuery(“province”,“上海”,“北京”));

5.3 match查询

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()));
        }
    }

5.4 multi_match查询

match针对一个field做检索,multi_match针对多个field进行检索,多个field对应一个text。
针对多个索引同时查询

builder.query(QueryBuilders.multiMatchQuery("province","上海","北京").operator(Operator.OR));

5.5 ids查询

builder.query(QueryBuilders.idsQuery("province").addIds("郑州"));

5.6 prefix查询

针对keyword类型,可以进行前缀查询

    builder.query(QueryBuilders.prefixQuery("province","台湾"));

5.7 fuzzy查询

模糊查询,我们输入字符的大概(比如 出现错别字),ES就可以去根据输入的内容大概去匹配一下结果。
prefixLength指定前面几个字符是不允许出现错误的

    builder.query(QueryBuilders.fuzzyQuery("userName","张三").prefixLength(2));

5.8 wildcard查询

通配查询,和MySQL中的like是一个套路,可以在查询时,在字符串中指定通配符*和占位符?
*号匹配多个字符 ?匹配一个字符

    builder.query(QueryBuilders.wildcardQuery("userName","王五*"));

5.9 range查询

范围查询,只针对数值类型,对某一个Field进行大于或者小于的范围指定
可以使用 gt:> gte:>= lt:< lte:<=

    builder.query(QueryBuilders.rangeQuery("fee").gt(5).lte(10));

5.10 regexp查询

正则查询,通过你编写的正则表达式去匹配内容。使用较多 模糊查询性能较低
https://www.sojson.com/regex/generate

builder.query(QueryBuilders.regexpQuery("mobile","0?(13|14|15|18|17)[0-9]{9}"));

5.11 深分页Scroll

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()));
        }
    }

5.12 delete-by-query

查询并删除

    //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());

5.13 bool查询

复合过滤器,将你的多个查询条件,以一定的逻辑组合在一起。

  • 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);
        }
    }

5.14 filter查询

过滤

        //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());
        }

5.15 高亮查询

            //    @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"));
            }

5.16 聚合查询

ES的聚合查询和MySQL的聚合查询类似,ES的聚合查询相比MySQL要强大的多,ES提供的统计数据的方式多种多样
https://www.elastic.co/guide/en/elasticsearch/reference/6.5/search-aggregations-bucket-datehistogram-aggregation.html

5.16.1 去重计数查询

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);
}

5.16.2 范围统计

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"
          }
        ]
      }
    }
  }
}

5.16.3 统计聚合查询

# 统计聚合查询 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);
    }

5.17 地图经纬度搜索

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 以多个点,确定一个多边形,获取多边形内的全部数据

5.17.1 geo_distance 直线距离检索方式

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());
        }
    }

5.17.2 geo_bounding_box 以两个点确定一个矩形,获取在矩形内的全部数据

# 两个点[左上角/右下角]确定一个矩形,查找内部的数据
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());
            }
        }

5.17.3 geo_polygon 以多个点,确定一个多边形,获取多边形内的全部数据

	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
          }
        ]
      }
    }
  }
}

你可能感兴趣的:(elasticsearch,大数据,搜索引擎)