Elasticsearch学习

1、官网下载,安装方式多样,这里选择docker

docker pull docker.elastic.co/elasticsearch/elasticsearch:7.14.0

2、创建普通用户以便于使用Elasticsearch

# root用户可以使用docker,普通用户无法使用,是因为没有权限。
# 查看当前用户
whoami
# 创建新用户
adduser username
# 设置用户密码
passwd username
# 创建一个新的docker组
groupadd docker
# 添加新用户到docker组
usermod -a -G docker username
# 更新docker组
newgrp docker
# 修改docker.sock权限为root用户docker组
chown root:docker /var/run/docker.sock
# 切换到新用户
su username

3、创建Elasticsearch容器

docker run
--name elasticsearch                                # 容器名称
-p 9200:9200                                           # 映射端口 接受http协议
-p 9300:9300                                           # 接受TCP协议
-e "discovery.type=single-node"                        # 单节点式
-e ES_JAVA_OPTS="-Xms84m -Xmx512m"                       # 内存大小
-v /home/docker/elasticsearch/data:/usr/share/elasticsearch/data      # 挂载目录
-v /home/docker/elasticsearch/logs:/usr/share/elasticsearch/logs
-v /home/docker/elasticsearch/plugins:/usr/share/elasticsearch/plugins
-d
elasticsearch:8.7.0

4、Kibana安装,安装方式多样,这里也选择docker

# 拉取镜像
docker pull kibana:7.14.0
# 挂载数据卷,创建容器
docker run -d 
--name kibana 
-p 5601:5601 
-v /home/es/kibana/kibana.yml:/usr/share/kibana/config/kibana.yml 
58dffcbc8caa

5、拷贝容器内的文件

docker cp <containerId>:/path/to/file /path/on/host

6、索引操作

# 查询全部索引
GET /_cat/indices?v
# 创建索引
PUT /indexName
# 创建一个索引并使用json格式传参,设置主分片为1副本分片数量为零
PUT /indexName
{
  "settings": {
    "number_of_shards": 1
    , "number_of_replicas": 0
  }
}
# 删除一个索引
DELETE /indexName
# 删除所有索引
DELETE /*

7、映射操作

字符串类型:keyword、text

整数类型:integer、long

小数类型:float、double

布尔类型:boolean

日期类型:date

# 创建索引时创建映射
PUT /indexName
{
  "settings": {
    "number_of_shards": 1
    , "number_of_replicas": 0
  }
  , "mappings": {
    "properties": {
      "id":{
        "type": "integer" 
      },
      "title":{
        "type": "keyword"
      },
      "price":{
        "type": "double"
      },
      "update_time":{
        "type": "date"
      },
      "describe":{
        "type": "text"
      }
    }
    }
}
# 查看索引的映射
GET /indexName/_mapping

8、文档操作

# 根据映射向索引中创建一条文档,自动指定id
POST /indexName/_doc
{
  "title":"电冰箱",
  "price":2.99,
  "update_time":"2023-03-25",
  "describe":"很好用"
}
# 手动指定id
POST /indexName/_doc/id
{
  "title":"电冰箱",
  "price":2.99,
  "update_time":"2023-03-25",
  "describe":"很好用"
}
# 根据文档id查询文档
GET /indexName/_doc/id
# 根据id删除文档
DELETE /indexName/_doc/id
# 根据id更新文档,先删除再更新,覆盖式更新
PUT /indexName/_doc/id
{
  "title":"电视机"
}
# 根据id更新文档,只覆盖更新部分
POST /indexName/_doc/id/_update
{
  "doc" : {
    "title":"电视据"
  }
}
# 批量操作文档,指定id同时插入两条数据
POST /indexName/_doc/_bulk
  {"index":{"_id":7}}
  {"title":"瓜子","price":3.99,"update_time":"2022-04-23","describe":"好吃"}
  {"index":{"_id":8}}
  {"title":"饮料","price":4.99,"update_time":"2020-04-22","describe":"好喝"}
# 批量操作文档,指定id插入更新删除
POST /indexName/_doc/_bulk
  {"index":{"_id":9}}
  {"title":"矿泉水","price":8.99,"update_time":"1921-09-23","describe":"矿泉水也好喝"}
  {"update":{"_id":9}}
  {"doc":{"describe":"矿泉水好喝是好喝,就是太贵啦"}}
  {"delete":{"_id":1}}

9、高级文档查询

# 查询索引中的所有文档
GET /indexName/_doc/_search    # 或使用GET /indexName/_search
{
  "query":{
    "match_all":{}
  }
}
# 根据索引中的关键词查询
# keyword类型、Integer类型、double类型、date类型、boolean类型、float类型、long类型不分词
# text类型,es会使用分词器进行分词,中文单字查询英文单个单词(空格)查询
GET /indexName/_doc/_search
{
  "query":{
    "term":{    # term代表关键词查询
      "price":3.99    
    }
  }
}
# 范围查询
GET /indexName/_doc/_search
{
  "query":{
    "range":{    # range代表范围查询
      "price":{
        "gte":2,    # gte大于等于、gt大于
        "lte":3    # lte小于等于、lt小于
      }
    }
  }
}
# 前缀查询,查询指定前缀的内容
GET /indexName/_doc/_search
{
  "query":{
    "prefix":{    # prefix代表前缀查询
      "title":"瓜"
    }
  }
}
# 通配符查询,"?"代表任意一个字符,"*"代表任意多个字符
GET /indexName/_doc/_search
{
  "query":{
    "wildcard":{    # wildcard代表通配符查询
      "title":"?泉*"
    }
  }
}
# 多id查询
GET /indexName/_doc/_search
{
  "query":{
    "ids":{
      "values":["7","8","9"]
    }
  }
}
# 模糊查询。搜索关键词长度为2不允许出现模糊、关键词长度为3-5允许一次模糊、关键词长度大于五允许两次模糊
GET /indexName/_doc/_search
{
  "query":{
    "fuzzy":{
      "title":"矿石水"    # 文档中为矿泉水
    }
  }
}
# 布尔查询。关键字must等价于&&、should等价于||、must_not相当于!
GET /indexName/_doc/_search
{
  "query":{
    "bool":{
      "must":[{
       "ids":{
         "values":[7,8,9]
       }
      }]
    }
  }
}
# 多字段查找某个值,只能在同一类型(字符串或数字)的字段中查找,如果给出的字段类型不同会报错
# 如果查找的类型为字符串,会进行分词分别查找
GET /indexName/_doc/_search
{
  "query": {
    "multi_match": {
      "query": "饮料",
      "fields": ["title","describe"]
    }
  }
}
# 默认字段查询,默认字段分词就分词查询,默认字段不分词就不分词查询
GET /indexName/_doc/_search
{
  "query":{
    "query_string":{
      "default_field":"describe",
      "query":"好喝"
    }
  }
}

10、文档查询其他操作

# 高亮查询结果
GET /indexName/_doc/_search
{
  "query":{
    "query_string":{
      "default_field":"describe",
      "query":"好喝"
    }
  },
  "highlight":{    # 高亮关键词
    "fields":{
      "*":{}
    }
  }
}
# 控制显示文档条数
GET /indexName/_doc/_search
{
  "query":{
    "match_all":{}
  },
  "size":3    # 文档条数关键词
}
# 选择查询的起始位置,从零开始
GET /indexName/_doc/_search
{
  "query":{
    "match_all":{}
  },
  "from":0
}
# 对查询结果进行排序
GET /indexName/_doc/_search
{
  "query":{
    "match_all":{}
  },
  "sort":{    # 排序关键词
    "price":{    
      "order":"desc"    # 选择降序
    }
  }
}
# 查询指定字段
GET /indexName/_doc/_search
{
  "query":{
    "match_all":{}
  },
  "_source":["title","describe"]    # 指定需要查询的字段
}

11、ES索引规则:倒排索引

ES存储区:索引区、元数据区

倒排索引:把文档存在元数据区,将文档中不同类别和分词的数据分别与文档id绑定存在索引区,查询时,根据索引区数据找到对应元数据区的文档。

12、ES默认标准分词器

结构:字符过滤器(进行预处理例如过滤html标签)、分词器、Token过滤器(加工分好的词)

内置分词器:

①标准分词器Standard:英文按单词切分,中文按字切分,过滤符号,并小写处理

②简单分词器Simple:英文按单词切分中文按空格切分,过滤符号,小写处理

③停用词过滤分词器Stop:小写处理,停用this、a、an等词的过滤

④Whitespace:按空格切分,不过滤符号,不小写处理

⑤Keyword:不分词

# standard
POST /_analyze
{
  "analyzer": "standard"
  , "text": ["i am a good boy"]
}
# simple
POST /_analyze
{
  "analyzer": "simple"
  , "text": ["i am a good boy,这里是中文"]
}
# whitespace
POST /_analyze
{
  "analyzer": "whitespace"
  , "text": ["I am a good boy,这里是中文"]
}
# 创建映射时手动指定字段的分词器,只能指定可以分词的类型
PUT /user
{
  "settings": {
    "number_of_shards": 1
    , "number_of_replicas": 0
  },
  "mappings": {
    "properties": {
      "title":{
        "type": "text"
        , "analyzer": "standard"
      }
    }
  }
}

13、中文分词器IK

# 分词器版本与ES版本一致
# 下载分词器https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.14.0/elasticsearch-analysis-ik-7.14.0.zip
# 解压至es容器中plugins目录
# 粗粒度分词器
POST /_analyze
{
  "analyzer": "ik_smart"
  ,"text": ["这里是中文"]
}
# 细粒度分词器
POST /_analyze
{
  "analyzer": "ik_max_word"
  ,"text": ["这里是中文"]
}

14、扩展词:将非关键词的词加入词典

停用词:将关键词移除词典

# IK设置扩展词典和停用词典
# 在ik/config/IKAnalyzer.cfg.xml文件中设置停用词典文件和扩展词典文件
# 两个文件与IKAnalyzer.cfg.xml放在同一目录中,文件中每一行设置一个关键词

15、过滤查询

# 必须包裹在布尔查询内,与布尔查询连用,有filter关键字省略query关键字
# 先执行filter再执行布尔
GET /product/_doc/_search
{
  "query":{
    "bool":{
      "must":[{
        "range":{
          "price":{
          "lte":10
          ,"gte":0
        }
        }
      }],
      "filter":{
          "wildcard":{
            "describe":"*喝*"
          }
        }
    }
  }
}

16、ES整合SpringBoot

# 依赖
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>
# 配置
@Configuration
public class ESConfig extends AbstractElasticsearchConfiguration {

    @Value("${url}")
    String url;
    @Override
    @Bean
    public RestHighLevelClient elasticsearchClient() {
        final ClientConfiguration clientConfiguration = ClientConfiguration.builder().connectedTo(url).build();
        return RestClients.create(clientConfiguration).rest();
    }
}

17、两种操作ES方式:

ElasticserachOperations 面向对象

RestHighLevelClient restful风格、封装了ES细节

18、ElasticserachOperations方式

//实体类创建索引
@Document(indexName = "product",createIndex = true) //索引名,是否创建索引
public class Product {

    @Id         //将id与文档_id进行映射
    private Integer id;

    @Field(type = FieldType.Keyword)        //主要是指定字段类型
    private String title;

    @Field(type = FieldType.Double)
    private Double price;

    @Field(type = FieldType.Text)
    private String describe;

}
//创建索引并插入文档对象
@SpringBootTest     //测试类注解该加还得加,不然无法注入
public class Test {


    @Autowired
    ElasticsearchOperations elasticsearchOperations;

    @org.junit.jupiter.api.Test
    public void create(){
        Product product = new Product();
        product.setId(1);
        product.setTitle("上周青花瓷");
        product.setPrice(3.99);
        product.setDescribe("的确是上周的青花瓷,还带着烟火香");
        elasticsearchOperations.save(product);      //文档对象存入es,id不存在添加,id存在更新


    }
}
//根据id查询文档对象
    @org.junit.jupiter.api.Test
    public void get(){

        Product product = elasticsearchOperations.get("1",Product.class);
        System.out.println(product.toString());
    }
//删除全部文档
    @org.junit.jupiter.api.Test
    public void deleteAll(){
        elasticsearchOperations.delete(Query.findAll(), Product.class);
    }
//查询全部文档
    @org.junit.jupiter.api.Test
    public void find() throws JsonProcessingException {
        SearchHits<Product> searchHits =  elasticsearchOperations.search(Query.findAll(), Product.class);
        for (SearchHit<Product> s :searchHits){
            System.out.println(new ObjectMapper().writeValueAsString(s.getContent()));
        }
    }

19、RestHighLevelClient方式

创建索引

//使用Query DSL语法,创建索引
    @Test
    public void creat() throws IOException {
        CreateIndexRequest createIndexRequest = new CreateIndexRequest("product");    //创建索引名
        createIndexRequest.settings("{\n" +        //设置分片
                "    \"number_of_shards\": 1\n" +
                "    , \"number_of_replicas\": 0\n" +
                "  }",XContentType.JSON);
        createIndexRequest.mapping("{\n" +        //设置映射
                "    \"properties\": {\n" +
                "      \"id\":{\n" +
                "        \"type\": \"integer\" \n" +
                "      },\n" +
                "      \"title\":{\n" +
                "        \"type\": \"keyword\"\n" +
                "      },\n" +
                "      \"price\":{\n" +
                "        \"type\": \"double\"\n" +
                "      },\n" +
                "      \"update_time\":{\n" +
                "        \"type\": \"date\"\n" +
                "      },\n" +
                "      \"describe\":{\n" +
                "        \"type\": \"text\"\n" +
                "        , \"analyzer\": \"ik_max_word\"\n" +
                "      }\n" +
                "    }}", XContentType.JSON);
        CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(createIndexRequest, RequestOptions.DEFAULT);
        restHighLevelClient.close();
    }

删除索引

    @Test
    public void delet() throws IOException {

        DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("product");
        restHighLevelClient.indices().delete(deleteIndexRequest,RequestOptions.DEFAULT);
        restHighLevelClient.close();

    }

向指定索引中增加数据

    @Test
    public void add() throws IOException {

        IndexRequest indexRequest = new IndexRequest("product");    //指定索引
        indexRequest.id("1");       //指定id、可以不指定自动分配
        indexRequest.source("{\n" +         //指定数据
                "  \"title\":\"电冰箱\",\n" +
                "  \"price\":2.99,\n" +
                "  \"update_time\":\"2023-03-25\",\n" +
                "  \"describe\":\"很好用\"\n" +
                "}",XContentType.JSON);
        restHighLevelClient.index(indexRequest,RequestOptions.DEFAULT);
        restHighLevelClient.close();
    }

更新文档

    @Test
    public void update() throws IOException {

        UpdateRequest updateRequest = new UpdateRequest("product","1");
        updateRequest.doc("{\n" +
                "    \"title\":\"电视据\"\n" +
                "  }",XContentType.JSON);
        restHighLevelClient.update(updateRequest,RequestOptions.DEFAULT);
        restHighLevelClient.close();
    }

删除文档

    @Test
    public void dele() throws IOException {

        DeleteRequest deleteRequest = new DeleteRequest("product");
        deleteRequest.id("1");
        restHighLevelClient.delete(deleteRequest,RequestOptions.DEFAULT);
        restHighLevelClient.close();
    }

基于id查询文档

    @Test
    public void gett() throws IOException {

        GetRequest getRequest = new GetRequest("product");
        getRequest.id("1");
        GetResponse getResponse = restHighLevelClient.get(getRequest,RequestOptions.DEFAULT);
        String s = getResponse.toString();
        System.out.println(s);
        restHighLevelClient.close();
    }

查询所有

    @Test
    public void getAll() throws IOException {
        SearchRequest searchRequest = new SearchRequest("product");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.matchAllQuery());
        searchRequest.source(searchSourceBuilder);
//searchResponse结果就是kibana中查询返回结果
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);
        String s = searchResponse.toString();
        System.out.println(s);
    }

按条件查询

    @Test
    public void quer() throws IOException {
        SearchRequest searchRequest = new SearchRequest("product");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.termQuery("describe","好用"));
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);
        String s = searchResponse.toString();
        System.out.println(s);

    }

分页查询,指定从第二条开始查询,总计查询一条

    @Test
    public void sort() throws IOException {
        SearchRequest searchRequest = new SearchRequest("product");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.matchAllQuery()).from(1).size(1);
        searchRequest.source(searchSourceBuilder);
        SearchResponse s = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);
        System.out.println(s.toString());
    }

降序查询

    @Test
    public void sort() throws IOException {
        SearchRequest searchRequest = new SearchRequest("product");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.matchAllQuery()).from(0).size(10).sort("price",SortOrder.DESC);
        searchRequest.source(searchSourceBuilder);
        SearchResponse s = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);
        System.out.println(s.toString());
    }

返回指定的字段,排除describe,接受title、price

    @Test
    public void sort() throws IOException {
        SearchRequest searchRequest = new SearchRequest("product");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.matchAllQuery()).from(0).size(10).sort("price",SortOrder.DESC).fetchSource(new String[]{"price","title"},new String[]{"describe"});
        searchRequest.source(searchSourceBuilder);
        SearchResponse s = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);
        System.out.println(s.toString());
    }

使用HighLightBuilder对查询结果进行高亮显示

    @Test
    public void quer() throws IOException {
        SearchRequest searchRequest = new SearchRequest("product");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.requireFieldMatch(false).field("describe").field("title");
        searchSourceBuilder.query(QueryBuilders.termQuery("describe","好喝")).highlighter(highlightBuilder);
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);
        String s = searchResponse.toString();
        System.out.println(s);

    }

过滤查询

    @Test
    public void fget() throws IOException {
        SearchRequest searchRequest = new SearchRequest("product");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        searchSourceBuilder.query(QueryBuilders.matchAllQuery()).postFilter(QueryBuilders.termQuery("describe","好喝"));
        searchRequest.source(searchSourceBuilder);
        SearchResponse s =restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);
        System.out.println(s);


    }

ES对象存取

//作为对象存入
    @Test
    public void las() throws IOException {
    Product product = new Product();
    product.setId(4);
    product.setTitle("黑鱼片");
    product.setPrice(28.0);
    product.setDescribe("很好吃");
    product.setUpdate_time(new Date());
    IndexRequest indexRequest = new IndexRequest("product");
    indexRequest.id(String.valueOf(product.getId())).source(new ObjectMapper().writeValueAsString(product),XContentType.JSON);
    IndexResponse s = restHighLevelClient.index(indexRequest,RequestOptions.DEFAULT);
    System.out.println(s.isFragment());


    }
//取出存为对象
    @Test
    public void lget() throws IOException, JSONException {

        SearchRequest searchRequest = new SearchRequest("product");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.matchAllQuery());
        searchRequest.source(searchSourceBuilder);
        SearchResponse s = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);
        org.elasticsearch.search.SearchHit[] searchHits = s.getHits().getHits();
        List<Product> list = new ArrayList<>();
        for (org.elasticsearch.search.SearchHit a :searchHits){
            Product product = new ObjectMapper().readValue(a.getSourceAsString(),Product.class);
            list.add(product);
        }
        for (int i =0;i<list.size();i++){
        System.out.println(list.get(i));}
    }

20、聚合查询

Query DSL

//查某一个字段,
GET /product/_search
{
  "query":{
    "match_all": {}
  },
  "aggs":{
    "dd_all":{        //这里是聚合查询名,可以随便定义
      "terms": {
        "field": "price",        //查询价格
        "size": 10
      }
    }
  }
}
//查询最大值
GET /product/_search
{
  "query": {
    "match_all": {}
  },
  "aggs": {
    "dd_max":{
      "max": {
        "field": "price"        //查询价格最大值
      }
    }
  }
}
//查询最小值
GET /product/_search
{
  "query": {
    "match_all": {}
  },
  "aggs": {
    "dd_min":{
      "min": {
        "field": "price"        //这里查询价格最小值
      }
    }
  }
}
//查询平均值
GET /product/_search
{
  "query": {
    "match_all": {}
  },
  "aggs": {
    "dd_average":{
      "avg": {
        "field": "price"        //这里查询价格平均值
      }
    }
  }
}

RestHighLevelClient

//查询字段所有
    @Test
    public void agg() throws IOException {
        SearchRequest searchRequest = new SearchRequest("product");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.matchAllQuery()).aggregation(AggregationBuilders.terms("price_all").field("price"));
        searchRequest.source(searchSourceBuilder);
        SearchResponse s = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);
        System.out.println(s);
    }
//查询字段最大值
    @Test
    public void aggm() throws IOException {
        SearchRequest searchRequest = new SearchRequest("product");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.matchAllQuery()).aggregation(AggregationBuilders.max("max_all").field("price"));
        searchRequest.source(searchSourceBuilder);
        SearchResponse s = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);
        System.out.println(s);
    }
//

21、集群搭建

配置文件

# 集群名称
cluster.name: es_cluster
# 节点名称
node.name: node_1
# 开启远程访问
network.host: 0.0.0.0
# 使用发布地址进行集群间通信
network.publish_host: IP
# http通信端口
http.port: 9201
# tcp通信端口
transport.tcp.port: 9301
# 指定节点的tcp通信
discovery.seed_hosts: ["IP:9301","IP:9302","IP:9303"]
# 集群初始化节点
cluster.initial_master_nodes: ["node_1","node_2","node_3"]
# 集群最少可用节点
gateway.recover_after_nodes: 2
# 开启跨域
http.cors.enabled: true
# 跨域请求前缀
http.cors.allow-origin: "*"

kibana配置到es集群

server.host: "0"
server.shutdownTimeout: "5s"
elasticsearch.hosts: [ "http://IP:9201","http://IP:9202","http://IP:9203" ]
monitoring.ui.container.elasticsearch.enabled: true
相关推荐
喝醉酒的小白9 分钟前
Elasticsearch相关知识@1
大数据·elasticsearch·搜索引擎
边缘计算社区10 分钟前
首个!艾灵参编的工业边缘计算国家标准正式发布
大数据·人工智能·边缘计算
MZWeiei10 分钟前
Zookeeper的选举机制
大数据·分布式·zookeeper
MZWeiei11 分钟前
Zookeeper基本命令解析
大数据·linux·运维·服务器·zookeeper
学计算机的睿智大学生12 分钟前
Hadoop集群搭建
大数据·hadoop·分布式
biter008843 分钟前
opencv(15) OpenCV背景减除器(Background Subtractors)学习
人工智能·opencv·学习
Code哈哈笑2 小时前
【Java 学习】深度剖析Java多态:从向上转型到向下转型,解锁动态绑定的奥秘,让代码更优雅灵活
java·开发语言·学习
小小工匠2 小时前
ElasticSearch - 深入解析 Elasticsearch Composite Aggregation 的分页与去重机制
elasticsearch·composite·after_key·桶聚合分页
风_流沙2 小时前
java 对ElasticSearch数据库操作封装工具类(对你是否适用嘞)
java·数据库·elasticsearch