基于Elasticsearch后端的Hibernate Search:多索引查询下实现countByDocumentType聚合的疑问
解决Hibernate Search中按索引/实体类型分组统计的问题
好消息是你完全不需要添加自定义字段就能实现这个需求!Hibernate Search和Elasticsearch的原生字段确实可以用来做聚合统计,只是不能直接用你尝试的那种简单字符串字段名来引用,下面分两种场景给你具体的实现方案:
1. 按_entity_type分组统计
_entity_type是Hibernate Search自动维护的内置字段,你可以通过Hibernate Search提供的字段引用API来正确访问它,而不是直接写字符串"_entity_type"。以下是可运行的示例代码:
// 定义聚合键 AggregationKey<Map<String, Long>> countsByEntityKey = AggregationKey.of("countsByEntity"); SearchSession searchSession = Search.session(entityManager); // 执行查询和聚合 searchSession.search(targetIndexes) .extension(ElasticsearchExtension.get()) .where(f -> f.matchAll()) // 替换成你的实际查询条件 .aggregation(countsByEntityKey, f -> f.terms() // 使用Hibernate Search的内置实体类型字段引用 .field(Search.mapping(entityManager).entityTypeField(), String.class)) .fetchHits(20); // 这里的hit数量不影响聚合结果,可按需设置 // 获取聚合结果 Map<String, Long> countsByEntity = searchSession.aggregation(countsByEntityKey);
这个方法的核心是用Search.mapping(entityManager).entityTypeField()获取到Hibernate Search维护的_entity_type字段的正式引用,这样聚合就能正确识别这个字段了。
2. 按_index分组统计
_index是Elasticsearch的原生元字段,Hibernate Search没有为它创建对应的映射,所以需要借助Elasticsearch的原生聚合功能来实现。你可以通过ElasticsearchExtension直接传入Elasticsearch的聚合JSON来实现:
// 定义聚合键 AggregationKey<Map<String, Object>> countsByIndexKey = AggregationKey.of("countsByIndex"); SearchSession searchSession = Search.session(entityManager); // 执行查询和原生聚合 searchSession.search(targetIndexes) .extension(ElasticsearchExtension.get()) .where(f -> f.matchAll()) // 替换成你的实际查询条件 .aggregation(countsByIndexKey, f -> f.nativeAggregation( "{\"terms\": {\"field\": \"_index\"}}" )) .fetchHits(20); // 解析原生聚合的结果 Map<String, Object> rawAggResult = searchSession.aggregation(countsByIndexKey); Map<String, Long> countsByIndex = new HashMap<>(); if (rawAggResult != null && rawAggResult.containsKey("buckets")) { @SuppressWarnings("unchecked") List<Map<String, Object>> buckets = (List<Map<String, Object>>) rawAggResult.get("buckets"); for (Map<String, Object> bucket : buckets) { String indexName = (String) bucket.get("key"); Long count = ((Number) bucket.get("doc_count")).longValue(); countsByIndex.put(indexName, count); } }
这种方式直接利用Elasticsearch的原生能力,绕过了Hibernate Search的字段映射限制,能直接访问_index元字段完成统计。
为什么你之前的尝试失败?
- 对于
_entity_type:直接写"_entity_type"字符串时,Hibernate Search无法关联到它维护的内置字段元数据,导致聚合无法识别该字段; - 对于
_index:这个字段属于Elasticsearch的原生元字段,不在Hibernate Search的映射范围内,所以无法通过Hibernate Search的标准聚合API访问,必须用原生聚合方式。
内容的提问来源于stack exchange,提问作者François F.
相关产品推荐
相关产品推荐

