ElasticSearch基础入门(七)使用Spring Data ElasticSearch查询文档

一、基本查询

ElasticsearchTemplate提供了一些基本的查询方法。
在这里插入图片描述
我们可以根据Id来查询或者直接查询所有,即match_all

	@Test
    public void findDoc() {
        Iterable<Item> items = itemRepository.findAll();
        items.forEach(System.out::println);
    }

在这里插入图片描述

二、自定义方法

Spring Data的一个很强大的功能,就是根据方法名称就能自动实现功能。
比如:你的方法名叫做:findByTitle,那么它就知道你是根据title查询,然后自动帮你完成,无需写实现类。

当然,方法名称要符合一定的约定:

KeywordSampleElasticsearch Query String
AndfindByNameAndPrice{"bool" : {"must" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}}
OrfindByNameOrPrice{"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}}
IsfindByName{"bool" : {"must" : {"field" : {"name" : "?"}}}}
NotfindByNameNot{"bool" : {"must_not" : {"field" : {"name" : "?"}}}}
BetweenfindByPriceBetween{"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
LessThanEqualfindByPriceLessThan{"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
GreaterThanEqualfindByPriceGreaterThan{"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}}
BeforefindByPriceBefore{"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
AfterfindByPriceAfter{"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}}
LikefindByNameLike{"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}}
StartingWithfindByNameStartingWith{"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}}
EndingWithfindByNameEndingWith{"bool" : {"must" : {"field" : {"name" : {"query" : "*?","analyze_wildcard" : true}}}}}
Contains/ContainingfindByNameContaining{"bool" : {"must" : {"field" : {"name" : {"query" : "**?**","analyze_wildcard" : true}}}}}
InfindByNameIn(Collection<String>names){"bool" : {"must" : {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}}
NotInfindByNameNotIn(Collection<String>names){"bool" : {"must_not" : {"bool" : {"should" : {"field" : {"name" : "?"}}}}}}
NearfindByStoreNearNot Supported Yet !
TruefindByAvailableTrue{"bool" : {"must" : {"field" : {"available" : true}}}}
FalsefindByAvailableFalse{"bool" : {"must" : {"field" : {"available" : false}}}}
OrderByfindByAvailableTrueOrderByNameDesc{"sort" : [{ "name" : {"order" : "desc"} }],"bool" : {"must" : {"field" : {"available" : true}}}}

举个例子,我们可以根据价格是否满足一定范围来查询,在接口中定义一个方法。
在这里插入图片描述
在测试方法中,我们直接调用这个方法,并没有写这个接口的实现类,直接运行。

	@Test
    public void findDoc() {
        // 测试接口默认实现
        itemRepository.findByPriceBetween(3000d, 4000d);
        byPriceBetween.forEach(System.out::println);
    }

在这里插入图片描述可以看到价格在3000~4000元的两部小米手机被查询到了,而价格为 5499的华为并未找到。

虽然基本查询和自定义方法查询已经很强大了,但在很多场景下还是不够的。比如模糊查询通配符词条查询等。因此,我们还需要更多高级查询。

三、高级查询

1. 匹配查询

	@Test
    public void testQuery() {
        MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("title", "小米电视");
        Iterable<Item> items = itemRepository.search(queryBuilder);
        items.forEach(System.out::println);
    }

在这里插入图片描述
我们title匹配的是小米电视,但在指定映射的时候我们指定了该字段使用ik分词器,所以会被分成小米电视连个词条。这个方法相当于使用了match做匹配。所以只要包含了小米或者电视,就都可以被查询到。

Repository的search方法需要QueryBuilder参数,elasticSearch为我们提供了一个QueryBuilders工具类。

QueryBuilders提供了大量的静态方法,用于生成各种不同类型的查询对象,例如:词条、模糊、通配符等QueryBuilder对象。

elasticsearch提供很多可用的查询方式,但是不够灵活。如果想玩过滤或者聚合查询等就很难了。

2. 自定义查询

NativeSearchQueryBuilder:Spring提供的一个查询条件构建器,帮助构建json格式的请求体

	@Test
    public void testNativeQuery() {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        // 添加分词查询
        NativeSearchQueryBuilder queryBuilder = nativeSearchQueryBuilder.withQuery(QueryBuilders.matchQuery("title", "小米电视"));
        // 添加分页参数
        queryBuilder.withPageable(PageRequest.of(0, 1));
        // 构建
        NativeSearchQuery searchQuery = queryBuilder.build();
        // 执行搜索,返回分页结果
        Page<Item> itemPage = itemRepository.search(searchQuery);
        // 获取总条数
        long totalElements = itemPage.getTotalElements();
        // 获取总页数
        int totalPages = itemPage.getTotalPages();
        // 获取内容
        List<Item> items = itemPage.getContent();
        items.forEach(System.out::println);
    }

3. 排序

排序也通用通过NativeSearchQueryBuilder完成:

我们直接在2.自定义查询的基础上,直接添加一个按价格进行升序排序。


	// 添加排序信息
     queryBuilder.withSort(SortBuilders.fieldSort("price").order(SortOrder.ASC));

在这里插入图片描述
这时,结果集就会按照价格升序排序了。

四、聚合

1. 聚合为桶,再嵌套子聚合计算平均值。

聚合为桶就是分组,比我我们按照品牌将文档分组。

@Test
    public void testAggregations() {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        // 不查询任何结果,过滤结果集。
        FetchSourceFilterBuilder fetchSourceFilterBuilder = new FetchSourceFilterBuilder();
        SourceFilter sourceFilter = fetchSourceFilterBuilder.withIncludes(new String[]{}).build();
        nativeSearchQueryBuilder.withSourceFilter(sourceFilter);
        // 添加聚合
        TermsAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms("group_by_brand").field("brand");
        // 分组后添加子聚合, 即计算组内的价格平均值
        TermsAggregationBuilder subAggregationBuilder = termsAggregationBuilder.
                subAggregation(AggregationBuilders.avg("price_avg").field("price"));
        nativeSearchQueryBuilder.addAggregation(subAggregationBuilder);
        NativeSearchQuery nativeSearchQuery = nativeSearchQueryBuilder.build();
        // 执行查询
        Page<Item> itemsPage = itemRepository.search(nativeSearchQuery);
        // 先将结果转型为带聚合信息的分页
        AggregatedPage aggregatedPage = (AggregatedPage) itemsPage;
        // 因为使用的是terms, 根据词条聚合, 所以将Aggregation强转为StringTerms类型.
        StringTerms group_by_brand = (StringTerms)aggregatedPage.getAggregation("group_by_brand");
        // 取出所有buckets
        List<StringTerms.Bucket> buckets = group_by_brand.getBuckets();
        buckets.forEach(bucket -> {
            InternalAvg price_avg = (InternalAvg) bucket.getAggregations().get("price_avg");
            System.out.println(bucket.getKeyAsString());
            System.out.println(bucket.getDocCount());
            System.out.println("平均售价: " + price_avg.getValue());
        });
    }

聚合操作比较繁琐,尤其是在获取聚合结果的时候,需要将返回值转型才能获取到结果。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值