Spring Data Elasticsearch之Operations

2,748 阅读4分钟

本文源码 github.com/tzjavadmg/e…

见UserOperationsTest.java类。

Operations简介

Repository和Operations是Spring Data Elasticsearch提供的两个高级别的抽像接口,是应用程序的首选。 使用Repository来访问Elasticsearch简单易用,上手很快,但是地理位置搜索目前支持不太好(5.0版本)。此时可以选择相对更为灵活的Operations接口来操作Elasticsearch。

Operations接口主要有以下四个:

  • IndexOperations定义索引级别的操作,如创建或删除索引。
  • DocumentOperations定义基于实体 ID 存储、更新和检索实体的操作。
  • SearchOperations定义使用查询搜索多个实体的操作
  • ElasticsearchOperations结合了DocumentOperationsSearchOperations接口。

下面来简单看看这些接口如何使用。

索引操作

创建索引

@Test
@DisplayName("创建索引")
@Order(2)
void createIndex() {
    IndexOperations indexOperations = elasticsearchOperations.indexOps(User.class);
    //设置索引基本信息
    Map<String, Object> settings = new HashMap<>();
    Map<String, Object> index = new HashMap<>();
    settings.put("index", index);
    index.put("number_of_shards", 3);
    index.put("number_of_replicas", 1);

    Document mapping = indexOperations.createMapping(User.class);
    indexOperations.create(settings, mapping);
}

查看索引

@Test
@DisplayName("查看索引信息")
@Order(3)
void indexInfo(){
    //查看索引完整信息
    IndexOperations indexOperations = elasticsearchOperations.indexOps(User.class);
    List<IndexInformation> informations = indexOperations.getInformation();
    informations.forEach(indexInformation -> System.out.println(JSON.toJSONString(indexInformation, JSONWriter.Feature.PrettyFormat)));

    //查看索引映射信息
    Map<String, Object> mapping = indexOperations.getMapping();
    System.out.println("------------mapping----------------");
    System.out.println(JSON.toJSONString(mapping, JSONWriter.Feature.PrettyFormat));

    //查看索引设置信息
    Settings settings = indexOperations.getSettings(true);
    System.out.println("------------settings----------------");
    System.out.println(JSON.toJSONString(settings, JSONWriter.Feature.PrettyFormat));

}

删除索引

@Test
@DisplayName("删除索引")
@Order(1)
void deleteIndex() {
    IndexOperations indexOperations = elasticsearchOperations.indexOps(User.class);
    if (indexOperations.exists()) {
        indexOperations.delete();
    }
}

文档操作

插入或修改文档

@Test
@DisplayName("插入或更新文档")
@Order(3)
void createUser() {
    User user = User.builder()
            .id(99L)
            .name("老六")
            .age(33)
            .province("上海")
            .city("上海")
            .district("浦东新区")
            .address("上海市浦东新区花园石桥路28弄1-8号-汤臣一品")
            .location(new GeoPoint(31.238794, 121.508506))
            .about("预测世界杯四强:阿根廷,巴西,法国,葡萄牙")
            .build();
    //支持批量插入
    elasticsearchOperations.save(user);
}

获取文档数据

@Test
@DisplayName("获取单个文档")
@Order(4)
void getUser() {
    User user = elasticsearchOperations.get("99", User.class);
    assert user != null;
    Assertions.assertEquals("老六", user.getName());
}

删除文档

@Test
@DisplayName("删除单个文档")
@Order(5)
void deleteUser(){
    elasticsearchOperations.delete("99", User.class);
}

文档查询

SearchOperations和ReactiveSearchOperations接口中定义的几乎所有方法都采用一个Query参数,该参数定义要为搜索执行的查询。Query是一个接口,Spring Data Elasticsearch 提供了三种实现:CriteriaQuery,StringQuery和NativeQuery.

查询结果类型可以是如下几种:

  • SearchHits 搜索命中集合。包括总命中数,总命中关系,最高分,聚合结果和建议信息,以及命中结果集(SearchHit对象列表)。

image.png

  • SearchHit 搜索命中对象。包括ID,分数值,排序值,高亮字段等信息。

image.png

  • SearchPage 定义一个Page包含SearchHits元素,用于分页搜索
  • SearchScrollHits 用于大数据量的连续滚动分页搜索

CriteriaQuery

CriteriaQuery允许创建Criteria来搜索数据,而无需了解 Elasticsearch 查询的语法或基础知识。Criteria允许用户通过简单地链接和组合指定搜索文档必须满足的标准的对象来构建查询。

@Test
@DisplayName("CriteriaQuery:寻找附近两公里18-30岁的妹纸")
@Order(4)
void criteriaQuery() {
    //位置坐标:九号线星中路地铁站 121.375569,31.163862
    GeoPoint location = new GeoPoint(31.163862, 121.375569);
    //按地址由近到远排序,相同小区年龄从大到小排
    Sort sort = Sort
            .by(new GeoDistanceOrder("location", location))
            .ascending()
            .and(Sort.by("age").descending());

    Query query = new CriteriaQuery(
            //查询星中路地铁站2公里内的小区
            new Criteria("location").within(location, "2km")
                    .and(new Criteria("sex").is("女"))
                    .and(new Criteria("age").lessThan(30).greaterThanEqual(18))
    );
    query.addSort(sort);
    SearchHits<User> searchHits = elasticsearchOperations.search(query, User.class);
    List<SearchHit<User>> searchHitList = searchHits.getSearchHits();

    searchHitList.forEach(searchHit -> {
                User hero = searchHit.getContent();
                log.info("\n{}", searchHit.getScore(), JSON.toJSONString(hero, JSONWriter.Feature.PrettyFormat));
            }
    );
}

StringQuery

将Elasticsearch查询作为JSON字符串。此种方式更适合对Elasticsearch查询的语法比较了解的人,同时这也更方便我们使用kibana或postman等客户端工具行进调试。

@Test
@DisplayName("StringQuery:寻找上海40-60岁的老男人")
@Order(4)
void stringQuery() {
    String dsl = """
               {"bool":{"must":[{"match":{"city":"上海"}},{"match":{"sex":"男"}},{"range":{"age":{"gte":40,"lte":60}}}]}}
            """;
    Query query = new StringQuery(dsl);

    List<SearchHit<User>> searchHitList = elasticsearchOperations.search(query, User.class).getSearchHits();
    searchHitList.forEach(searchHit -> System.out.println(JSON.toJSONString(searchHit.getContent(), JSONWriter.Feature.PrettyFormat))
    );
}

NativeQuery

NativeQuery是有复杂查询或无法使用CriteriaAPI 表达的查询时使用的类,例如在构建查询和使用聚合时。它允许使用co.elastic.clients.elasticsearch._types.query_dsl.QueryElasticsearch库中的所有不同实现,因此命名为“native”。

@Test
@DisplayName("NativeQuery:统计上海各区妹纸人数,并显示前3人信息")
@Order(4)
void nativeQuery() {
    //按距离和年龄排序,选择最近的和最年轻的
    Sort sort = Sort.by(new GeoDistanceOrder("location", new GeoPoint(31.163862, 121.375569))).ascending()
            .and(Sort.by("age").ascending());

    Query query = NativeQuery.builder()
            //定义一个名为byDistrict,按district字段分组统计的聚合。
            .withAggregation("byDistrict", Aggregation.of(a -> a.terms(ta -> ta.field("district").size(100))))
            //只统计妹纸
            .withQuery(q -> q.match(m -> m.field("sex").query("女")))
            //每页3条,显示第一页数据
            .withPageable(PageRequest.of(0, 3, sort))
            .build();
    SearchHits<User> searchHits = elasticsearchOperations.search(query, User.class);
    //获取聚合数据
    ElasticsearchAggregations aggregationsContainer = (ElasticsearchAggregations) searchHits.getAggregations();
    Map<String, ElasticsearchAggregation> aggregations = Objects.requireNonNull(aggregationsContainer).aggregationsAsMap();
    //获取指名称的聚合
    ElasticsearchAggregation aggregation = aggregations.get("byDistrict");
    Buckets<StringTermsBucket> buckets = aggregation.aggregation().getAggregate().sterms().buckets();
    //打印聚合信息
    buckets.array().forEach(bucket -> log.info("\n区名:{}\n人数:{}", bucket.key().stringValue(), bucket.docCount()));
    //打印聚合命中记录信息
    List<SearchHit<User>> searchHitList = searchHits.getSearchHits();
    searchHitList.forEach(searchHit -> System.out.println(JSON.toJSONString(searchHit.getContent(), JSONWriter.Feature.PrettyFormat)));
}