Java学习Day60:回家!(ElasticStatic)

1.what is ElasticStatic

The Elastic Stack, 包括 Elasticsearch、 Kibana、 Beats 和 Logstash(也称为 ELK Stack)。能够安全可靠地获取任何来源、任何格式的数据,然后实时地对数据进行搜索、分析和可视化。

Elaticsearch,简称为 ES, ES 是一个开源的高扩展的分布式全文搜索引擎, 是整个 ElasticStack 技术栈的核心。

它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理 PB 级别的数据。

一般传统数据库,全文检索都实现的很鸡肋,因为一般也没人用数据库存文本字段。进行全文检索需要扫描整个表,如果数据量大的话即使对 SQL 的语法优化,也收效甚微。建立了索引,但是维护起来也很麻烦,对于 insert 和 update 操作都会重新构建索引。

搜索的数据对象是大量的非结构化的文本数据。
文件记录量达到数十万或数百万个甚至更多。
支持大量基于交互式文本的查询。
需求非常灵活的全文搜索查询。
对高度相关的搜索结果的有特殊需求,但是没有可用的关系数据库可以满足。
对不同记录类型、非文本数据操作或安全事务处理的需求相对较少的情况。为了解决结构化数据搜索和非结构化数据搜索性能问题,我们就需要专业,健壮,强大的全文搜索引擎 。

2.ES倒排索引

2.1倒排索引

也称之为反向索引,建立词和文档id之间的对应关系。即把“文档→单词”的形式变为“单词→文档”的形式。

java编程思想 -----> 分词 -----> java、编程、思想、编程思想、java编程思想

先对数据进行分词,得到一个个的词条,然后将词条与文档的对应关系保存起来,最后在对词条本身做索引排序。

如下数据:

idname
1小米手机
2华为手机
3小米电视
4三星电视

ES主要实现数据的搜索,并且ES还不支持事务,因此要保证数据一致性,那么此时还需要使用到数据库。

2.2.ES环境搭建

ElasticSearch本质就是一个搜索引擎,用来实现海量数据的搜索。

Kibana是一个可视化的工具,配合ES进行使用,可以ES中所存储的数据进行可视化的展示(柱状图、饼状图、散点图、折线图...)。并且在Kibana中还提供了开发者工具

# 启动容器(如果不存在容器就创建、存在则修改)
docker compose -f docker-compose.yml up -d

# 删除所有容器
docker compose -f docker-compose.yml down

# 停止所有容器
docker compose -f docker-compose.yml stop

# 启动所有容器
docker compose -f docker-compose.yml start

# 重启所有容器
docker compose -f docker-compose.yml restart

2.3ik分词器

在IK分词器中提供了两种分词器算法:

1、ik_smart:粗粒度分词,分出的词比较少

2、ik_max_word:细粒度分词,分出的词比较多

2.4自定义词典

1、在plugins/ik/config目录下创建一个ext.dic文件

touch ext.dic

2.更改IKAnalyzer.cfg.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
        <comment>IK Analyzer 扩展配置</comment>
        <!--用户可以在这里配置自己的扩展字典 -->
        <entry key="ext_dict">ext.dic</entry>
         <!--用户可以在这里配置自己的扩展停止词字典-->
        <entry key="ext_stopwords">stopword.dic</entry>
        <!--用户可以在这里配置远程扩展字典 -->
        <!-- <entry key="remote_ext_dict">words_location</entry> -->
        <!--用户可以在这里配置远程扩展停止词字典-->
        <!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>

3.重启ES

docker restart es

3 ES核心概念

为了方便理解ES(8.5.0)中的相关概念,我们可以对比着MySQL进行学习,如下所示:

MySQLElasticsearch说明
TableIndex索引(index),就是文档的集合,类似数据库的表(table)
RowDocument文档(Document),就是一条条的数据,类似数据库中的行(Row),文档都是JSON格式
ColumnField字段(Field),就是JSON文档中的字段,类似数据库中的列(Column)
SchemaMappingMapping(映射)是索引中文档的约束,例如字段类型约束。类似数据库的表结构(Schema)
SQLDSLDSL是elasticsearch提供的JSON风格的请求语句,用来操作elasticsearch,实现CRUD

Elasticsearch是面向文档型数据库,一条数据在这里就是一个文档,用JSON作为文档序列化的格式,比如下面这条用户数据;

{
    "name" :     "John",
    "sex" :      "Make",
    "age" :      25,
    "birthDate": "2024/05/01",
    "about" :    "I love to go rock climbing",
    "interests": [ "sports", "music" ]
}

mapping是处理数据的方式和规则方面做一些限制,如:某个字段的数据类型、默认值、分析器、是否被索引等等。这些都是映射里面可以设置的,其它就是处理ES里面数据的一些使用规则设置也叫做映射,按着最优规则处理数据对性能提高很大,因此才需要建立映射,并且需要思考如何建立映射才能对性能更好。

4 ES基本操作(DSL)

4.2.1 创建索引库

PUT /my_index

结果:
{
  "acknowledged" : true,
  "shards_acknowledged" : true,
  "index" : "my_index"
}

4.2.2 查看所有索引库

语法: GET /_cat/indices?v

4.2.3 查看单个索引库

语法: GET /{索引名称}

GET /my_index
结果:
{
  "my_index" : {
    "aliases" : { },
    "mappings" : { },
    "settings" : {
      "index" : {
        "creation_date" : "1633499968211",
        "number_of_shards" : "1",
        "number_of_replicas" : "1",
        "uuid" : "bclHUdHrS4W80qxnj3NP0A",
        "version" : {
          "created" : "7080099"
        },
        "provided_name" : "my_index"
      }
    }
  }
}

4.2.4 删除索引库

DELETE /my_index
结果:
{
  "acknowledged" : true
}

4.3 文档操作

PUT /my_index/_doc/1
{
  "title": "小米手机",
  "category": "小米",
  "images": "/xm.jpg",
  "price": 3999
}

返回结果:
{
  "_index" : "my_index",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 0,
  "_primary_term" : 1
}

4.3.2 查看文档

语法: GET /{索引名称}/{类型}/{id}

GET /my_index/_doc/1
结果:
{
  "_index" : "my_index",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 1,
  "_seq_no" : 0,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "title" : "小米手机",
    "category" : "小米",
    "images" : "/xm.jpg",
    "price" : 3999
  }
}

4.3.3 修改文档

PUT /my_index/_doc/1
{
  "title": "华为手机",
  "category": "华为",
  "images": "/hw.jpg"
}

0注意:上述的修改是先根据id把文档删除掉,然后重新添加文档

4.3.4 指定字段修改

POST /my_index/_update/1
{
  "doc": {
    "price": 4500
  }
}

4.3.5 删除文档

DELETE /my_index/_doc/1
结果:
{
  "_index" : "my_index",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 12,
  "result" : "deleted",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 11,
  "_primary_term" : 1
}

4.4 映射mapping

语法: GET /{索引名称}/_mapping

Mapping的常见属性:

  • type:字段数据类型,常见的简单类型有:

    • 字符串:text(可分词的文本)、keyword(精确值,例如:品牌、国家、ip地址)

    • 数值:long、integer、short、byte、double、float、

    • 布尔:boolean

    • 日期:date

    • 对象:object

  • index:是否创建倒排索引,默认为true

  • analyzer:指定在创建索引的时候所使用的分词器

  • search_analyzer:指定在搜索的时候所使用的分词器(如果没有指定该分词器,那么搜索的时候使用analyzer所指定的分词器)

4.4.3 创建索引指定映射

#删除原创建的索引
DELETE /my_index

#创建索引,并同时指定映射关系和分词器等。
PUT /my_index
{
  "mappings": {
    "properties": {
      "id":{
        "type": "long",
        "index": true
      },
      "title": {
        "type": "text",
        "index": true,
        "analyzer": "ik_max_word",
        "search_analyzer": "ik_smart"
      },
      "category": {
        "type": "keyword",
        "index": true
      },
      "images": {
        "type": "keyword",
        "index": false
      },
      "price": {
        "type": "integer",
        "index": true
      }
    }
  }
}

结果:
{
  "acknowledged" : true,
  "shards_acknowledged" : true,
  "index" : "my_index"
}

创建映射的两种方式:

1、使用ES自动映射推断机制

2、在创建索引库的时候指定映射

上述两种方式如何进行选择?

1、优先选择"自动映射推断机制", 如果这种方式创建出来的映射中的某一个字段相关属性和我们所期望的有点不太一样,删除索引库

2、此时选择第二种方式创建映射(把有问题的字段属性设置一下即可)

5 ES高级查询(DSL)

5.1 查询所有(match_all)

GET /my_index/_search
{
  "query": {   # query 关键字就是设置搜索方式以及一些其他的搜索参数
    "match_all": {}
  }
}

5.2 匹配查询(match)

GET /my_index/_search
{
  "query": {
    "match": {
      "title": "华为智能手机"   # 会对搜索的关键字进行分词,然后使用各个词条从对应的倒排索引表中进行搜索
    }
  }
}

5.3 多字段匹配(multi_match)

GET /my_index/_search
{
  "query": {
    "multi_match": {
      "query": "华为智能手机",
      "fields": ["title","category"]
    }
  }
}

5.4 关键字精确查询(term)

GET /my_index/_search
{
  "query": {
   "term": {
     "title": {
       "value": "华为手机"
     }
   }
  }
}

5.5 范围查询(range)

GET /my_index/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 3000,
        "lte": 5000
      }
    }
  }
}

范围查询使用range。

  • gte: 大于等于

  • lte: 小于等于

  • gt: 大于

  • lt: 小于

5.6 组合查询(bool)

bool各条件之间有and,or或not的关系

  • must: 各个条件都必须满足,所有条件是and的关系

  • should: 各个条件有一个满足即可,即各条件是or的关系

  • must_not: 不满足所有条件,即各条件是not的关系

  • filter: 与must效果等同,但是它不计算得分,效率更高点。

ES会根据用户所输入的关键字和文档的匹配程度去计算文档的得分,后期会使用文档的得分对文档数据进行排序,返回的时候就会返回排序的结果

5.6.1 must

GET /my_index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": "华为"
          }
        },
        {
          "range": {
            "price": {
              "gte": 3000,
              "lte": 5000
            }
          }
        }
      ]
    }
  }
}

5.6.2 should

GET /my_index/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "title": "华为"
          }
        },
        {
          "range": {
            "price": {
              "gte": 3000,
              "lte": 5000
            }
          }
        }
      ]
    }
  }
}

5.6.3 must_not

GET /my_index/_search
{
  "query": {
    "bool": {
      "must_not": [
        {
          "match": {
            "title": "华为"
          }
        },
        {
          "range": {
            "price": {
              "gte": 3000,
              "lte": 5000
            }
          }
        }
      ]
    }
  }
}

5.7 排序(sort)

GET /my_index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": "华为"
          }
        }
      ]
    }
  },
  "sort": [
    {
      "price": {
        "order": "asc"
      }
    }
  ]
}

5.8 分页查询(from、size)

分页的两个关键属性:from、size

  • from: 当前页的起始索引,默认从0开始。 from = (pageNo - 1) * pageize

  • size: 每页显示多少条

POST /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "from": 0,
  "size": 2
}

5.9 高亮查询(highlight)

高亮显示的实现分为两步:

1)给文档中的所有关键字都添加一个标签,例如<em>标签

2)页面给<em>标签编写CSS样式

POST /my_index/_search
{
  "query": {
    "match": {
      "title": "华为"
    }
  },
  "highlight": {
    "fields": { // 指定要高亮的字段
      "FIELD": {
        "pre_tags": "<em>",  // 用来标记高亮字段的前置标签
        "post_tags": "</em>" // 用来标记高亮字段的后置标签
      }
    }
  }
}

6 Spring Data ES

Spring Data是一个用于简化数据库、非关系型数据库、索引库访问,并支持云服务的开源框架。其主要目标是使得对数据的访问变得方便快捷,并支持map-reduce

框架和云计算数据服务。 Spring Data可以极大的简化JPA(Elasticsearch…)的写法,可以在几乎不用写实现的情况下,实现对数据的访问和操作。除了CRUD外,还包括如分页、排序等一些常用的功能。

Spring Data Elasticsearch 基于Spring data API 简化 Elasticsearch操作(基本的增删改查),将原始操作Elasticsearch的客户端API 进行封装 。

Spring Data为Elasticsearch项目提供集成搜索引擎。Spring Data Elasticsearch也是基于ORM思想进行设计

思考问题:怎么使用呢?需要建立实体类索引库之间的对应关系,以及实体类中的属性和索引库字段之间的对应关系【使用相关的注解完成】。

6.4 环境搭建

6.4.1 创建项目

创建一个spring boot的项目(spring-data-es),在pom.xml文件中加入如下的依赖:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.0.5</version>
    <relativePath/>
</parent>

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
    </dependency>
</dependencies>
<build>
    <plugins>
        <!-- java编译插件 -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.2</version>
            <configuration>
                <source>17</source>
                <target>17</target>
                <encoding>UTF-8</encoding>
            </configuration>
        </plugin>
    </plugins>
</build>

6.4.2 yml文件

# es连接地址配置
spring:
  elasticsearch:
    uris: 192.168.136.147:9200

6.4.3 启动类

package com.es;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringDataEsApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringDataEsApplication.class , args) ;
    }
    
}

6.4.4 实体类

// 商品实体类
@Data
@Document(indexName = "product")        // 建立实体类和es索引库中的对应关系
public class Product {

    @Id
    private Long id ;                  // 主键

    @Field(name = "productName" , type = FieldType.Text , analyzer = "ik_max_word" )
    private String productName ;        // 商品的名称,普通的属性

    @Field(type = FieldType.Integer)
    private Integer store ;         // 产品库存

    @Field(type = FieldType.Double)
    private Double price ;			// 产品价格

    @Field(type = FieldType.Long)
    private Long brandId ;			// 品牌id

    @Field(type = FieldType.Keyword)
    private String brandName ;		// 品牌名称

    @Field(type = FieldType.Nested)  // 针对List集合类型的属性需要将其类型设置为Nested, TODO: 为什么需要设置为Nested
    private List<Attr> attrList ;    // 产品属性

}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Attr {			// 产品属性实体类

    @Id
    private Long attrId ;           // 属性id

    @Field(type = FieldType.Keyword)
    private String attrName ;       // 属性名称

    @Field(type = FieldType.Keyword)
    private String attrValue ;      // 属性值

}

注解含义:

@Document 作用在类,标记实体类为文档对象。indexName属性:对应索引库名称

@Id 作用在成员变量,标记一个字段作为id主键

@Field 作用在成员变量,标记为文档的字段,并指定字段映射属性:

1、type:字段类型,取值是枚举:FieldType

2、index:是否索引,布尔类型,默认是true

3、store:是否存储,布尔类型,默认是false

4、analyzer:分词器名称:ik_max_word

6.5 使用方式一

借助:ElasticsearchRepository接口

定义持久层操作接口:

@Repository
public interface ProductRepository extends ElasticsearchRepository<Product , Long> {

}

6.5.1 添加文档

@Test
public void saveProduct() {

    List<Attr> attrList = Arrays.asList(new Attr(1L, "内存", "8G"), new Attr(2L, "硬盘", "256G"));
    Product prod = Product.builder()
        .id(4L).productName("vivo手机")
        .price(2999.0).store(50)
        .brandId(3L)
        .brandName("vivo")
        .attrList(attrList).build();
    productRepository.save(prod) ;

}

6.5.2 根据id查询

@SpringBootTest(classes = SpringDataEsApplication.class)
public class ProductRepositoryTest {

    @Autowired
    private ProductRepository productRepository ;

    @Test
    public void findById() {
        Optional<Product> product = productRepository.findById(1L);
        Product prod = product.get();
        System.out.println(prod);
    }

}

6.5.4 分页查询

@Test
public void queryPage() {
    Pageable pageable = PageRequest.of(0, 1);
    Page<Product> products = productRepository.findAll(pageable);
    products.forEach( s -> System.out.println(s) );
}

6.6 使用方式二

借助:ElasticsearchClient

ElasticsearchClient是Elasticsearch中所提供的一个操作ES的新的核心类,并且已经被Spring Boot实现了自动化配置。

6.6.1 基本操作

@SpringBootTest(classes = SpringDataEsApplication.class)
//classes = SpringDataEsApplication.class 表示加载应用的主配置类
public class ElasticsearchClientTest {
    @Autowired
    private ElasticsearchClient elasticsearchClient ;
    @Test
    public void saveProduct() throws IOException {

        List<Attr> attrList = Arrays.asList(new Attr(1L, "内存", "6G"), new Attr(2L, "硬盘", "128G"));
        Product prod = Product.builder()
                .id(2L).productName("小米手机")
                .price(3999.0).store(200)
                .brandId(2L)
                .brandName("小米")
                .attrList(attrList).build();
        //IndexRequest 是 Elasticsearch 的请求对象,表示向 Elasticsearch 发送一个索引请求,将一个文档存入指定的索引库。
        IndexRequest indexRequest = new IndexRequest.Builder<Product>()
                .index("product")       // 指定索引库的名称
                .id("2")                // 指定文档id
                .document(prod)
                .build() ;

        IndexResponse indexResponse = elasticsearchClient.index(indexRequest) ;
        System.out.println(indexResponse);
    }
}
根据id查询
@Test
public void getById() throws IOException {
    GetRequest getRequest = new GetRequest.Builder()
        .index("product")
        .id("2")
        .build() ;
    GetResponse<Product> response = elasticsearchClient.get(getRequest, Product.class);
    Product product = response.source();
    System.out.println(product);
}
@Test
public void updateById() throws IOException {
    Product prod = Product.builder().id(1L).store(150).build();
    // Builder类上的两个泛型:TDocument表示完整的文档类型,而TPartialDocument表示部分更新的文档类型。
    UpdateRequest updateRequest = new UpdateRequest.Builder<Product , Product>()
        .index("product")
        .id("1")
        .doc(prod)
        .build() ;
    UpdateResponse updateResponse = elasticsearchClient.update(updateRequest, Product.class);
    System.out.println(updateResponse);
}
@Test
public void deleteById() throws IOException {
    DeleteRequest deleteRequest = new DeleteRequest.Builder().index("product").id("3").build() ;
    DeleteResponse deleteResponse = elasticsearchClient.delete(deleteRequest);
    System.out.println(deleteResponse);
}

6.6.2 高级操作

查询所有

@Test
public void search() throws IOException {

    // 创建搜索条件对象
    Query query = new Query.Builder().matchAll(QueryBuilders.matchAll().build()).build();

    // 创建搜索请求对象
    SearchRequest searchRequest = new SearchRequest.Builder()
        .index("product")           // 设置索引库的名称
        .query(query)
        .build() ;

    // 发送搜索请求
    SearchResponse<Product> searchResponse = elasticsearchClient.search(searchRequest, Product.class);

    // 解析结果
    HitsMetadata<Product> hitsMetadata = searchResponse.hits();
    long total = hitsMetadata.total().value() ;
    System.out.println("满足条件的总记录数:" + total);
    List<Hit<Product>> hits = hitsMetadata.hits();
    for(Hit<Product> hit : hits) {
        Product product = hit.source();
        System.out.println(product);
    }
}
// 范围查询
Query query = new Query.Builder().range(QueryBuilders.range().field("store").gt(JsonData.of(180)).build()).build() ;

// 匹配查询
Query query = new Query.Builder().match(QueryBuilders.match().field("productName").query("小米").build()).build();

// 多字段匹配查询
 Query query = new Query.Builder().multiMatch(QueryBuilders.multiMatch().fields("productName" , "brandName").query("手机").build()).build();

// 词条查询
Query query = new Query.Builder().term(QueryBuilders.term().field("brandName").value("小米").build()).build() ;
  @Test
    public void updataRequest() throws IOException {
        Product prod = Product.builder().id(1L).price(2000D).build();

        // Builder类上的两个泛型:TDocument表示完整的文档类型,而TPartialDocument表示部分更新的文档类型。
        UpdateRequest updateRequest = new UpdateRequest.Builder<Product , Product>()
                .index("product")
                .id("1")
                .doc(prod)
                .build() ;
        UpdateResponse updateResponse = elasticsearchClient.update(updateRequest, Product.class);
        System.out.println(updateResponse);


//        //属性数据
//        List<Attr> attrList = List.of(new Attr(1L,"内存","32GB"),
//                new Attr(2L,"内存","64GB"));
//        //商品数据
//        Product product = new
//                Product(3L,"华为手机",999,6599.99,5L,"华为",attrList);
//
//        UpdateRequest<Object, Object> build = new UpdateRequest.Builder<>()
//                .index("product")
//                .id("2")
//                .doc(product)
//                .build();
//        elasticsearchClient.update(build,Product.class);
    }
Product prod = Product.builder().id(1L).price(2000D).build();//构造器模式
    @Test
    public void jingqueSearch() throws IOException {

//            TermQuery termQuery = new TermQuery.Builder()
//                .field("brandName")
//                .value("苹果")
//                .build();
//        Query query = new Query.Builder()
//                .term(termQuery->termQuery.field("brandName").value("苹果"))
//                .build();

        SearchRequest build = new SearchRequest.Builder()
                .query(query1->query1.term(termQuery->termQuery.field("brandName").value("苹果")))
                .build();

        SearchResponse searchResponse= elasticsearchClient.search(build,Product.class);
        System.out.println(searchResponse);
    }
}
  @Test
    public void jingqueSearch() throws IOException {

//            TermQuery termQuery = new TermQuery.Builder()
//                .field("brandName")
//                .value("苹果")
//                .build();
//        Query query = new Query.Builder()
//                .term(termQuery->termQuery.field("brandName").value("苹果"))
//                .build();

        SearchRequest build = new SearchRequest.Builder()
                .query(query1->query1.term(termQuery->termQuery.field("brandName").value("苹果")))
                .build();

        SearchResponse searchResponse= elasticsearchClient.search(build,Product.class);
        System.out.println(searchResponse);
    }
 @Test
    public void highLight() throws IOException {
        MatchQuery matchQuery = new MatchQuery.Builder()
                .query("华为")
                .field("brandName")
                .build();

        HighlightField highlightField = new HighlightField.Builder()
                .preTags("<font color='red'>")
                .postTags("</font>")
                .build();

        Highlight highlight = new Highlight.Builder()
                .fields("brandName",highlightField)
                .build();

        Query query =new Query.Builder()
                .match(matchQuery)
                .build();

        SearchRequest searchRequest=new SearchRequest.Builder()
                .highlight(highlight)
                .query(query)
                .build();

        SearchResponse<Product> searchResponse=elasticsearchClient.search(searchRequest,Product.class);

        HitsMetadata<Product> hitsMetadata = searchResponse.hits();

        long total = hitsMetadata.total().value() ;

        System.out.println("满足条件的总记录数:" + total);

        List<Hit<Product>> hits = hitsMetadata.hits();

        for (Hit<Product> hit : hits){
            // 获取原始文档数据
            Product product = hit.source();
            // 解析高亮结果
            Map<String, List<String>> highlightFielMap = hit.highlight();
            if(highlightFielMap != null && highlightFielMap.size() > 0) {
                List<String> productNameHighlightList = highlightFielMap.get("productName");
                if(productNameHighlightList != null && productNameHighlightList.size() > 0) {
                    String productNameHighlightValue = productNameHighlightList.get(0);
                    product.setProductName(productNameHighlightValue);
                }
            }
            // 输出结果
            System.out.println(product);
        }
    }
}
    @Test
    public void highLight() throws IOException {

        Highlight highlight = new Highlight.Builder()
                .fields("brandName",highlightField->highlightField
                        .preTags("<font color='red'>").postTags("</font>"))
                .build();

        Query query =new Query.Builder()
                .match(matchQuery->matchQuery.field("brandName").query("华为"))
                .build();

        SearchRequest searchRequest=new SearchRequest.Builder()
                .highlight(highlight)
                .query(query)
                .build();

        SearchResponse<Product> searchResponse=elasticsearchClient.search(searchRequest,Product.class);

        HitsMetadata<Product> hitsMetadata = searchResponse.hits();

        long total = hitsMetadata.total().value() ;

        System.out.println("满足条件的总记录数:" + total);

        List<Hit<Product>> hits = hitsMetadata.hits();

        for (Hit<Product> hit : hits){
            // 获取原始文档数据
            Product product = hit.source();
            // 解析高亮结果
            Map<String, List<String>> highlightFielMap = hit.highlight();
            if(highlightFielMap != null && highlightFielMap.size() > 0) {
                List<String> productNameHighlightList = highlightFielMap.get("productName");
                if(productNameHighlightList != null && productNameHighlightList.size() > 0) {
                    String productNameHighlightValue = productNameHighlightList.get(0);
                    product.setProductName(productNameHighlightValue);
                }
            }
            // 输出结果
            System.out.println(product);
        }
    }
}

7.Nested类型

8.聚合查询

ES聚合查询是一种统计、分组和过滤数据的方式,通过对文档中的字段进行聚合操作,实现对数据的统计分析。类似于MySQL分组操作(group by)以及聚合函数(max、min、avg 、sum、 count)。

ES提供的聚合分析功能有指标聚合(metrics aggregations)桶聚合(bucket aggregations)管道聚合(pipeline aggregations)三大类。

8.1 桶型聚合

桶型聚合是将文档按照某个字段进行分组,把相同值的文档放入同一个桶中,然后对桶中的文档进行统计、计算等操作(类似于MySQL中的Group by)。ES中的桶型

8.1.1 桶型聚合简介

8.1.2 terms聚合演示

 @Test
    public void bucketAggreation() throws IOException {
        // MatchAll 查询
        MatchAllQuery matchAllQuery = new MatchAllQuery.Builder()
                .build();

        Query querybuid = new Query.Builder()
                .matchAll(matchAllQuery)
                .build();

        // Terms 聚合,按 price 字段聚合,获取前 2 个桶
        TermsAggregation termsAggregation = new TermsAggregation.Builder()
                .field("price")
                .size(2)
                .build();

        // 将聚合添加到请求中
        Aggregation aggregation = new Aggregation.Builder()
                .terms(termsAggregation)
                .build();

        // 创建搜索请求
        SearchRequest searchRequest = new SearchRequest.Builder()
                .index("product")
                .query(querybuid)
                .aggregations("price", aggregation)  // 设置聚合名称为 "price"
                .build();

        // 执行搜索请求
        SearchResponse searchResponse = elasticsearchClient.search(searchRequest,Product.class);

        // 获取聚合结果
        Map<String, Aggregate> aggregations = searchResponse.aggregations();

        // 根据聚合名称 "price" 获取聚合结果
        Aggregate aggregate = aggregations.get("price");
        System.out.println("aggregate = " + aggregate);

        // 转换为 DoubleTermsAggregate(假设 price 是 double 类型)
        DoubleTermsAggregate o = (DoubleTermsAggregate) aggregate._get();

        // 获取聚合结果中的桶
        Buckets<DoubleTermsBucket> buckets = o.buckets();

        // 打印桶的信息
        List<DoubleTermsBucket> array = buckets.array();
        for (DoubleTermsBucket bucket : array) {
            System.out.println("bucket.key() = " + bucket.key());
            System.out.println("bucket.docCount() = " + bucket.docCount());
        }
    }
 @Test
        public void statsSearch() throws IOException {
            StatsAggregation statsAggregation = new StatsAggregation.Builder()
                    .field("price")
                    .build();

            Aggregation aggregation = new Aggregation.Builder()
                    .stats(statsAggregation)
                    .build();

            SearchRequest searchRequest = new SearchRequest.Builder()
                    .index("product")
                    .aggregations("stats",aggregation)
                    .build();

            // 执行搜索请求
            SearchResponse searchResponse = elasticsearchClient.search(searchRequest,Product.class);
            Map<String, Aggregate> aggregateMap = searchResponse.aggregations();
            Aggregate aggregate = aggregateMap.get("stats");
            StatsAggregate statsAggregate = (StatsAggregate) aggregate._get();
            double sum = statsAggregate.sum();
            System.out.println("sum = " + sum);
            double min = statsAggregate.min();
            System.out.println("min = " + min);
            double max = statsAggregate.max();
            System.out.println("max = " + max);
            double avg = statsAggregate.avg();
            System.out.println("avg = " + avg);
            long count = statsAggregate.count();
            System.out.println("count = " + count);
            System.out.println();
        }

9 .自动补全功能

9.1 自动补全说明

自动补全功能就是当用户输入搜索关键字以后,自动从ES索引库中搜索出来和当前关键字相匹配的数据,如下所示:

Completion Suggester实现自动补全功能,需要自动补全的字段类型一定是completion,参与补全查询的字段必须是completion类型。字段的内容可以是用

来补全的多个词条形成的数组。

GET /test/_search
{
  "suggest": {
    "suggestValue": {
      "prefix":"fo",
       "completion":{
         "skip_duplicates":true,
         "field":"suggest",
         "fuzzy":{
           "fuzziness":"auto"
         }
       }
    }
  }
}
  • suggest: 表示这是一个建议(suggestion)查询,用于提供基于用户输入的自动补全建议。

  • suggestValue: 是自定义的建议查询名称,你可以根据需要随意命名。

  • prefix: 查询前缀,用于匹配建议的起始字符串。在这个例子中,任何以"fos"开头的建议都会被返回。

  • completion: 表示这是一个完成建议查询,它是Elasticsearch中用于实现自动补全功能的一种查询类型。

    • skip_duplicates: 当设置为true时,会跳过结果中的重复项,确保每个建议都是唯一的。

    • field: 指定在哪个字段上执行完成建议查询。在这个例子中,字段名为"suggest"。

    • fuzzy: 启用模糊匹配,允许返回与用户输入相近但不完全匹配的建议。

      • fuzziness: 指定模糊匹配的程度。"auto"表示Elasticsearch会根据术语的长度自动选择一个合适的模糊值。例如,对于短的术语,它可能会允许更多的编辑距离。

总的来说,这段代码的目的是在Elasticsearch的test索引中,查找字段suggest包含以"fos"为前缀的项,并且返回唯一的、可能模糊匹配的建议列表。这通常用于实现搜索框的自动补全功能,帮助用户在输入查询时快速找到他们可能想要搜索的完整术语。

        @Test
        public void suggestReserach() throws IOException {
            CompletionSuggester completionSuggester = new CompletionSuggester.Builder()
                    .field("suggest")
                    .skipDuplicates(true)
                    .fuzzy(fuzzy1->fuzzy1.fuzziness("auto"))
                    .build();

            FieldSuggester fieldSuggester = new FieldSuggester.Builder()
                    .prefix("fo")
                    .completion(completionSuggester)
                    .build();

            Suggester suggester = new Suggester.Builder()
                    .suggesters("Suggester",fieldSuggester)
                    .build();

            SearchRequest searchRequest = new SearchRequest.Builder()
                    .index("test")
                    .suggest(suggester)
                    .build();

            // 执行搜索请求
            SearchResponse searchResponse = elasticsearchClient.search(searchRequest,Object.class);

            Map suggest = searchResponse.suggest();
        }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/915895.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《进制转换:数字世界的奇妙变身术》

思维导图 一、什么是进制转换 在当今数字化飞速发展的时代&#xff0c;数字如同构建整个数字宇宙的基本粒子&#xff0c;无处不在且发挥着至关重要的作用。而在这个数字的魔法世界里&#xff0c;进制就像是不同的语言规则&#xff0c;每种进制都有着独特的构建方式和逻辑。 我…

Unity3D高级编程

1、标签(Tag)和图层(Layer) 他们都用于游戏物体分类&#xff0c;但是侧重点不一样。 标签便于代码中对特定物体进行操作。 图层则服务于渲染和碰撞管理&#xff0c;如控制摄像机渲染、光源影响及碰撞设置。 标签和图层的位置&#xff1a; &#xff08;1&#xff09;标签Tag…

Jmeter基础篇(22)服务器性能监测工具Nmon的使用

一、前言 我们在日常做压测的过程中&#xff0c;不仅仅需要监控TPS&#xff0c;响应时间&#xff0c;报错率等这些系统基础性能数据&#xff0c;还需要对服务器的性能&#xff08;如CPU、磁盘、内存、网络IO等&#xff09;做监控&#xff0c;以求对系统运行过程中的硬件性能有…

IDEA最新最全设置教程(包括常用的插件)

一、目的 统一安装一些必要的插件,方便大家开发。统一代码格式、注释格式、统一字符集编码。新加入的同事可以快速适应和熟悉,不需要在讲解IDEA配置问题。二、IDEA要修改的设置 新项目设置和设置 1. Java编译版本 这里请使用自己的JDK 2. 统一IDEA字符集 统一使用UTF-8 无…

日本IT工作好找吗?

在日本做IT是否好找工作&#xff0c;实际上取决于多个因素&#xff0c;包括个人的技术能力、日语水平、工作经验以及市场需求等。以下是对这一问题的详细分析&#xff1a; 技术能力与日语水平 技术能力&#xff1a;IT行业是一个技术密集型行业&#xff0c;技术能力自然是求职…

多端校园圈子论坛小程序,多个学校同时代理,校园小程序分展示后台管理源码

社团活动与组织 信息发布&#xff1a;系统支持社团发布活动信息、招募新成员等&#xff0c;方便社团进行线上线下活动的组织和管理。 增强凝聚力&#xff1a;通过系统&#xff0c;社团成员可以更好地交流和互动&#xff0c;增强社团的凝聚力和影响力。 生活服务功能 二手市场…

用 Python 从零开始创建神经网络(六):优化(Optimization)介绍

优化&#xff08;Optimization&#xff09;介绍 引言 引言 在随机初始化的模型中&#xff0c;或者即使是采用更复杂方法初始化的模型中&#xff0c;我们的目标是随着时间的推移培训或教育一个模型。为了训练一个模型&#xff0c;我们调整权重和偏差以提高模型的准确性和置信度…

架构篇(04理解架构的演进)

目录 学习前言 一、架构演进 1. 初始阶段的网站架构 2. 应用服务和数据服务分离 3. 使用缓存改善网站性能 4. 使用应用服务器集群改善网站的并发处理能力 5. 数据库读写分离 6. 使用反向代理和CDN加上网站相应 7. 使用分布式文件系统和分布式数据库系统 8. 使用NoSQL和…

Linux软件包管理与Vim编辑器使用指南

目录 一、Linux软件包管理器yum 1.什么是软件包&#xff1f; 2.什么是软件包管理器&#xff1f; 3.查看软件包 4.安装软件 ​编辑 5.卸载软件 Linux开发工具&#xff1a; 二、Linux编辑器---vim 1.vim的基本概念 (1) 正常/普通模式&#xff08;Normal mode&#xff0…

嵌入式硬件实战基础篇(一)-STM32+DAC0832 可调信号发生器-产生方波-三角波-正弦波

引言&#xff1a;本内容主要用作于学习巩固嵌入式硬件内容知识&#xff0c;用于想提升下述能力&#xff0c;针对学习STM32与DAC0832产生波形以及波形转换&#xff0c;对于硬件的降压和对于前面硬件篇的实际运用&#xff0c;针对仿真的使用&#xff0c;具体如下&#xff1a; 设…

怎么样绑定域名到AWS(亚马逊云)服务器

1&#xff0c;拿着你买的域名去亚马逊申请一个证书。申请证书分两种&#xff0c;一种是去亚马逊后台填域名手动申请 &#xff0c;另一种是通过API来申请&#xff0c;类似如下代码&#xff1a; 2、证验证书。有两种方式&#xff1a;一种是通过邮件&#xff0c;另一种去到域名提供…

【网络安全】公钥基础设施

1. PKI 定义 1.1 公钥基础设施的概念 公钥基础设施&#xff08;Public Key Infrastructure&#xff0c;简称PKI&#xff09;是一种基于公钥密码学的系统&#xff0c;它提供了一套完整的解决方案&#xff0c;用于管理和保护通过互联网传输的信息。PKI的核心功能包括密钥管理、…

【计算机网络】UDP网络程序

一、服务端 1.udpServer.hpp 此文件负责实现一个udp服务器 #pragma once#include <iostream> #include <string> #include <cstdlib> #include <cstring> #include <functional> #include <strings.h> #include <unistd.h> #incl…

定时器简介

TIM(Timer定时器)简介 在第一部分,我们主要讲的是定时器基本定时的功能&#xff0c;也就是定一个时间&#xff0c;然后让定时器每隔这个时间产生一个中断&#xff0c;来实现每隔一个固定时间执行一段程序的目的&#xff0c;比如你要做个时钟、秒表&#xff0c;或者使用一些程序…

【论文阅读】HITS: High-coverage LLM-based Unit Test Generation via Method Slicing

HITS: High-coverage LLM-based Unit Test Generation via Method Slicing 1. 来源出处 本文是发表在2024年39th IEEE/ACM International Conference on Automated Software Engineering (ASE)上的论文。作者包括Zejun Wang, Kaiibo Liu, Ge Li和Zhi Jin,他们来自北京的PKU …

多模态大模型开启AI社交新纪元,Soul App创始人张璐团队亮相2024 GITEX GLOBAL

随着AI在全球范围内的加速发展和广泛应用,各行业纷纷在此领域发力。作为全球最大的科技盛会之一,2024年的GITEX GLOBAL将目光再次聚焦于人工智能的飞速发展,吸引了超过6700家来自各个领域的企业参与。在这样的背景下,Soul App作为国内较早将AI技术应用于社交领域的平台,首次亮相…

爬虫开发工具与环境搭建——使用Postman和浏览器开发者工具

第三节&#xff1a;使用Postman和浏览器开发者工具 在网络爬虫开发过程中&#xff0c;我们经常需要对HTTP请求进行测试、分析和调试。Postman和浏览器开发者工具&#xff08;特别是Network面板和Console面板&#xff09;是两种最常用的工具&#xff0c;能够帮助开发者有效地捕…

Zabbix中文监控指标数据乱码

1&#xff09;点击主机&#xff0c;选择Zabbix server 中的 图形 一项&#xff0c;可以看到当前显示的为乱码 2&#xff09; 下载字体文件&#xff1a; https://gitcode.com/open-source-toolkit/4a3db/blob/main/SimHei.zip 解压unzip -x SimHei.zip 3&#xff09; 替换字体文…

HBase理论_HBase架构组件介绍

近来有些空闲时间&#xff0c;正好最近也在开发HBase相关内容&#xff0c;借此整理一下学习和对HBase组件的架构的记录和个人感受&#xff0c;付出了老夫不少心血啊&#xff0c;主要介绍的就是HBase的架构设计以及我的拓展内容。内容如有不当或有其他理解 matirx70163.com HB…

微信小程序自定义顶部导航栏(适配各种机型)

效果图 1.pages.js&#xff0c;需要自定义导航栏的页面设置"navigationStyle": "custom" 2.App.vue,获取设备高度及胶囊位置 onLaunch: function () {// 系统信息const systemInfo uni.getSystemInfoSync()// 胶囊按钮位置信息const menuButtonInfo uni.…