Spring Data Elasticsearch(java操作ELK)

一、环境搭建

(1)导入依赖

 

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.12.RELEASE</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>

(2)编辑配置文件

# 注意: 地址一定不要有末尾字符 '/'
# ElasticsearchRestTemplate客户端的配置
spring:
  elasticsearch:
    rest:
      uris: http://192.168.8.128:9200  # ES服务器所在位置。集群多节点地址用逗号分隔。默认http://localhost:9200
#  data: # ElasticsearchTemplate客户端配置。所有的方法API和ElasticsearchRestTemplate完全相同。
#    elasticsearch:
#      cluster-name: docker-cluster # ES集群的名称。已过期
#      cluster-nodes: 192.168.137.128:9300 # ES集群多节点地址。多个地址用逗号分隔。在7版本后已过期

(3)编辑实体类型

/**
 * Document - 描述类型和索引的映射。
 *  indexName - 索引名称
 *  shards - 主分片数量。默认值 1。
 *  replicas - 副本分片数量。默认值 1。
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(indexName = "stu_index", shards = 1, replicas = 0)
public class Student implements Serializable {
    /**
     * Field - 当前属性和索引中的字段映射规则。
     *  name - 字段名称。默认和当前类型属性名一致。
     *  type - 字段类型。默认使用FieldType.AUTO。自动识别。
     *  analyzer - 分词器。所有的Text类型字段默认使用standard分词器。
     *  index - 是否创建索引。默认值 true。
     *  format - 如果字段类型是date日期类型。此属性必须配置,用于设置日期格式化规则,使用DateFormat类型中的常量定义。
     */
    @Id
    @Field(name = "id", type = FieldType.Keyword)
    private Long id;
    @Field(name = "name", type = FieldType.Text, analyzer = "ik_max_word")
    private String name;
    @Field(name = "gender", type = FieldType.Keyword)
    private String gender;
    @Field(name = "age", type = FieldType.Integer)
    private int age;
    @Field(name = "hobbies", type = FieldType.Text, analyzer = "ik_smart")
    private List<String> hobbies;
    @Field(name = "isMarried", type = FieldType.Boolean)
    private boolean isMarried;
    @Field(name = "books", type = FieldType.Text, analyzer = "ik_smart", index = false)
    private String[] books;
    @Field(name = "birth", type = FieldType.Date, format = DateFormat.basic_date_time)
    private Date birth;
}

 二、使用

(1)创建索引

@SpringBootTest
public class TestDataES {
    /**
     * Spring Data Elasticsearch客户端类型 - ElasticsearchRestTemplate客户端对象,
     *                                     由spring-boot-starter-data-elasticsearch启动时,自动装配创建。
     *  要求:
     *   1. 当前应用中,必须有启动类型,才能自动装配创建。
     *   2. 创建客户端时,根据配置文件,实现服务器连接。默认连接的ES服务器集群是:http://localhost:9200
     */
    @Autowired
    private ElasticsearchRestTemplate restTemplate;
    
    /**
     * 创建索引
     */
    @Test
    public void testCreateIndex(){
        /*
         * 在spring data elasticsearch中,所有的索引相关操作都封装在一个操作对象 IndexOperations 中。
         * 基于一个实体类型的类对象,创建索引操作对象。实体类型中,有使用注解描述的索引和映射相关信息。可以做为索引的创建的设置。
         */
        IndexOperations indexOps = restTemplate.indexOps(Student.class);

        // 创建索引
        indexOps.create();
    }
}

(2)设置映射

/**
 * 设置映射
 * 在商业开发中,几乎不会使用框架创建索引或设置映射。因为这是架构或者管理员的工作。且不适合使用代码实现
 */
@Test
public void testCreateIndexAndPutMapping(){
    IndexOperations indexOps = restTemplate.indexOps(Student.class);

    // 设置映射
    indexOps.putMapping(indexOps.createMapping());
}

(3)删除索引

/**
 * 删除索引
 */
@Test
public void testDeleteIndex(){
    restTemplate.indexOps(Student.class).delete();
}

(4)新增单一文档

/**
 * 新增单一文档
 */
@Test
public void testAddDocument() throws Exception{
    // 创建要新增的实体对象。手工赋予主键,返回结果就是新增的数据对象。
    Student student = new Student();
    student.setId(1L);
    student.setName("张三");
    student.setGender("男");
    student.setAge(25);
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    student.setBirth(sdf.parse("1996-01-01"));
    student.setBooks(new String[]{"Java面向对象思想","Spring In Action"});
    student.setHobbies(Arrays.asList("吃饭","上班", "睡觉"));
    student.setMarried(true);

    // 保存数据到Elasticsearch
    student = restTemplate.save(student);

    System.out.println(student);

    System.out.println("====================================================");
    
    // 自动生成主键,返回的结果中包含Elasticsearch生成的主键。
    student = new Student();
    student.setName("李四");
    student.setGender("男");
    student.setAge(21);
    student.setBirth(sdf.parse("2000-01-01"));
    student.setBooks(new String[]{"火影忍者","海贼王"});
    student.setHobbies(Arrays.asList("玩游戏","睡觉", "看动漫"));
    student.setMarried(false);

    student = restTemplate.save(student);

    System.out.println(student);
}

(5)批量新增文档

/**
 * 批量新增文档
 */
@Test
public void testBatchAdd() throws Exception {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

    List<Student> list = new ArrayList<>();
    list.add(new Student(2L, "郭德纲", "男", 48, Arrays.asList("郭麒麟"), true, new String[]{"德云逗笑社"}, sdf.parse("1973-01-18")));
    list.add(new Student(3L, "于谦", "男", 52, Arrays.asList("郭麒麟","抽烟","喝酒","烫头"), true, new String[]{"德云逗笑社"}, sdf.parse("1969-01-24")));

    restTemplate.save(list);

    System.out.println("批量新增结束");
}

(6)删除文档

/**
 * 删除文档
 */
@Test
public void testDeleteDocument(){
    // 删除类型对应的索引中的指定主键数据,返回删除数据的主键。注意:Elasticsearch中的文档主键都是字符串类型的。
    String response = restTemplate.delete("1", Student.class);

    System.out.println(response);
}

(7)更新文档

/**
 * 更新文档。
 * save方法,可以覆盖同主键数据。案例省略
 * update更新,部分更新
 */
@Test
public void testUpdate(){
    UpdateQuery query =
            UpdateQuery.builder("2")
                    .withDocument(Document.parse("{\"hobbies\":[\"郭麒麟\", \"郭汾阳\"]}"))
                    .build();
    UpdateResponse response =
            restTemplate.update(query, IndexCoordinates.of("stu_index"));
    System.out.println(response.getResult());
}

三、查询

(1)主键查询文档

/**
 * 主键查询文档
 */
@Test
public void testGetById(){
    Student student = restTemplate.get("3", Student.class);
    System.out.println(student);
}

(2)全字段搜索

/**
 * querystring 搜索。所有的搜索的套路一致。具体方法如下:
 * SearchHits search(Query query, Class<T> clazz);
 *  参数query - 具体的条件。是spring data elasticsearch框架封装的类型。
 *   一般使用构建器创建搜索条件。
 *   NativeSearchQueryBuilder().build()
 *     .withQuery(QueryBuilder) - 提供具体的条件。此方法的参数类型是QueryBuilder
 *     这个QueryBuilder类型由elasticsearch提供的java客户端jar包定义。
 *       QueryBuilder对象,一般使用构建器创建,QueryBuilders
 *  参数clazz - 返回结果每个document封装的对象类型。
 *  返回return - 搜索的结果。是一个完整的搜索结果对象。包含总数据个数,当前搜索集合,是否超时等。
 *
 *  SearchHits类型实现了Iterable接口,可迭代。容器中每个对象的类型是SearchHit。
 *  SearchHit类型中包含一个完整的搜索对象,由元数据和源数据组成。
 */
@Test
public void testQuerystring(){
    QueryBuilder queryBuilder =
            QueryBuilders.queryStringQuery("郭");

    Query query =
            new NativeSearchQueryBuilder()
                    .withQuery(queryBuilder) // 提供具体的条件。
                    .build();

    SearchHits<Student> hits =
            restTemplate.search(query, Student.class);

    System.out.println("搜索结果数据个数是: " + hits.getTotalHits());

    for(SearchHit<Student> hit : hits){
        // 源数据,就是保存在Elasticsearch中的数据
        Student content = hit.getContent();
        System.out.println("源数据:" + content);
    }
}

(3)搜索全部

/**
 * 搜索全部
 */
@Test
public void testMatchAll(){
    SearchHits<Student> hits = restTemplate.search(
            new NativeSearchQueryBuilder()
                    .withQuery(QueryBuilders.matchAllQuery())
                    .build(), Student.class);
    for(SearchHit<Student> hit : hits){
        System.out.println(hit.getContent());
    }
}

(4)匹配搜索

/**
 * match搜索
 */
@Test
public void testQueryByMatch(){
    SearchHits<Student> hits = restTemplate.search(
            new NativeSearchQueryBuilder()
                    .withQuery(
                            QueryBuilders.matchQuery(
                                    "name",
                                    "于谦")
                    )
                    .build(),
            Student.class
    );

    print(hits);

}

private void print(SearchHits<Student> hits){
    for(SearchHit<Student> hit : hits){
        System.out.println(hit.getContent());
    }
}

(5)短语搜索

/**
 * match phrase搜索
 */
@Test
public void testQueryByMatchPhrase(){
    SearchHits<Student> hits = restTemplate.search(
            new NativeSearchQueryBuilder()
                    .withQuery(
                            QueryBuilders.matchPhraseQuery(
                                    "hobbies",
                                    "烫头"
                            )
                    )
                    .build(),
            Student.class
    );
    print(hits);
}

(6)范围搜索

/**
 * range搜索
 */
@Test
public void testQueryByRange(){
    SearchHits<Student> hits =
            restTemplate.search(
                    new NativeSearchQueryBuilder()
                            .withQuery(
                                    QueryBuilders.rangeQuery("age")
                                    .lte(35).gte(30)
                            )
                            .build(),
                    Student.class
            );

    print(hits);
}

(7)范围搜索

/**
 * bool搜索。多条件
 */
@Test
public void testQueryByBool(){
    /*
     * bool搜索条件中包含3个条件集合。List<QueryBuilder>,分别命名为must(必须匹配的条件),mustNot(必须不匹配的条件),should(任意匹配的条件)。
     * 每个条件集合管理方案都提供了两个方法API。以must举例如下:
     *  must() - 获取must必要条件集合,返回List<QueryBuilder>。
     *  must(QueryBuilder) - 把参数添加到must条件集合中,返回BoolQueryBuilder类型对象,即当前对象本身(this)。
     */
    BoolQueryBuilder queryBuilder =
            QueryBuilders.boolQuery();
    List<QueryBuilder> must = queryBuilder.must();
    must.add(QueryBuilders.matchQuery("name", "郭"));
    queryBuilder.should(QueryBuilders.rangeQuery("age").gt(30))
            .should(QueryBuilders.rangeQuery("age").lt(60));

    // 搜索
    SearchHits<Student> hits =
            restTemplate.search(
                    new NativeSearchQueryBuilder()
                            .withQuery(queryBuilder)
                            .build(),
                    Student.class
            );

    print(hits);
}

(8)分页和排序

/**
 * 分页+排序
 * 在spring data系列框架中,分页和排序可以混合处理。使用通用定义spring data commons中的Page接口实现。应用接口的实现类型PageRequest处理。
 * PageRequest类型中,提供静态方法of(int page, int size[, Sort sort]);
 * page - 查询第几页,页码数字从0开始计数。
 * size - 查询多少行。
 * Sort - 具体的排序规则。可选参数。
 */
@Test
public void testQueryByPageAndSort(){
    SearchHits<Student> hits =
            restTemplate.search(
                    new NativeSearchQueryBuilder()
                            .withQuery(QueryBuilders.matchAllQuery())
                            .withPageable(
                                    PageRequest.of(0, 2,
                                            Sort.by(
                                                    Sort.Order.desc("age"),
                                                    Sort.Order.asc("id")
                                            )
                                    )
                            )
                            .build(),
                    Student.class
            );
    print(hits);
}

(9)高亮处理

/**
 * 搜索结果高亮处理
 * 提供高亮字段,字段名,是否分段(每段几个字符,返回几段),前后缀。
 * 创建Query对象,做搜索处理。
 */
@Test
public void testQueryHighLight(){
    // 创建高亮字段,必须和具体的字段名绑定。
    HighlightBuilder.Field field1 = new HighlightBuilder.Field("name");
    // 高亮前缀
    field1.preTags("<span style='color: red'>");
    // 高亮后缀
    field1.postTags("</span>");
    // 分段的每段字符数
    field1.fragmentSize(Integer.MAX_VALUE);
    // 分段后,返回几段
    field1.numOfFragments(1);
    Query query =
            new NativeSearchQueryBuilder()
                    .withQuery(
                            QueryBuilders.matchQuery(
                                    "name",
                                    "于谦")
                    )
                    .withHighlightFields(field1)
                    .build();

    SearchHits<Student> hits =
            restTemplate.search(query, Student.class);

    for (SearchHit<Student> hit : hits){
        // 获取源数据
        Student content = hit.getContent();
        // 找高亮数据
        List<String> hl = hit.getHighlightField("name");
        // 判断是否有高亮数据
        if(hl != null && hl.size() > 0){
            // 有高亮数据
            (hl.get(0));
        }
        System.out.println(content);
    }

}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值