Date range aggregation

Hello,

I try to make a range query but i can’t figure out how to put alias on my dateRanges as i can do it directly in ElasticSearch :

"date_range": {
	"field": "randomDateField",
	 "ranges": [{ 
		 "key": "range1",
		 "to": "now-29d" 
	},
	{ 
		"key": "range2",
		"from": "now-25d",
		"to": "now-29d" 
	},
	{ 
		"key": "range3",
		"from": "now-20d",
		"to": "now-25d"
	},
	{ 
		"key": "range4",
		"from": "now-19d"
	}]
}
AggregationKey<Map<Range<Instant>, Long>> agg = AggregationKey.of( "myAgg" );
... 
.aggregation(agg, f -> f.range()
		.field("randomDateField", Instant.class, ValueConvert.NO)
		.range("range1", to, from) // i would like to have something like that here.
		.range("range2", to, from)

Maybe it can be done with some extension like .extension(ElasticsearchExtension.get()) or trick that i’am not aware of ?

thank for your help !

After some digging i found this part of the code :

@Override
		public void range(Range<? extends K> range) {
			JsonObject rangeJson = new JsonObject();
			Optional<? extends K> lowerBoundValue = range.getLowerBoundValue();
			if ( lowerBoundValue.isPresent() ) {
				if ( !RangeBoundInclusion.INCLUDED.equals( range.getLowerBoundInclusion() ) ) {
					throw log.elasticsearchRangeAggregationRequiresCanonicalFormForRanges( range );
				}
				rangeJson.add( "from", convertToFieldValue( lowerBoundValue.get() ) );
			}
			Optional<? extends K> upperBoundValue = range.getUpperBoundValue();
			if ( upperBoundValue.isPresent() ) {
				if ( !RangeBoundInclusion.EXCLUDED.equals( range.getUpperBoundInclusion() ) ) {
					throw log.elasticsearchRangeAggregationRequiresCanonicalFormForRanges( range );
				}
				rangeJson.add( "to", convertToFieldValue( upperBoundValue.get() ) );
			}
			// We need to request a keyed response,
			// because ranges are not always returned in the order they are submitted
			rangeJson.addProperty( "key", String.valueOf( rangesJson.size() ) );
			rangesInOrder.add( range.map( Function.identity() ) );
			rangesJson.add( rangeJson );
		}

I assume we can rely on the rangesJson.size() to help produce the expected ordered ranges in the result Aggregation Map and then i don’t really need to associate every range to a specific key.
Anyway, i think it could be more comfortable to have a way to use a specific key, maybe with an extension specific to ElasticSearch if it’s not something usefull in lucene.

Hello,

Yes, you don’t have to worry about providing range keys yourself. The idea is that you use Range instances as keys, and Hibernate Search will do what’s necessary internally to handle the implementation details, and to return ranges in the order they were provided.

If you have “business” keys that you translate to ranges, I can see how passing these keys to Hibernate Search and having Hibernate Search return these keys directly in the resulting map would come handy.
For now you can just assume ranges are returned in the order they are defined, but that’s admittedly a bit awkward.

We could consider allowing to provide a custom key with each defined range. Implementation wise, it’s easy, be it for Lucene or Elasticsearch. Honestly, the real challenge will be how to present that in APIs… because we shouldn’t allow only some ranges to have a key: either they all have a custom key and the aggregation type is Map<CustomKey, Long>, or none of them has and the aggregation type is Map<Range<F>, Long>.

I suppose something like this would do:

AggregationKey<Map<MyCustomKey, Long>> agg = AggregationKey.of( "myAgg" );
... 
.aggregation(agg, f -> f.range()
		.field("randomDateField", Instant.class, ValueConvert.NO)
		.providedKeys(MyCustomKey.class)
		.range(myCustomKey1, to1, from1)
		.range(myCustomKey2, to2, from2)

Or:

AggregationKey<Map<MyCustomKey, Long>> agg = AggregationKey.of( "myAgg" );
Map<MyCustomKey, Range<Instant>> ranges = ...;
... 
.aggregation(agg, f -> f.range()
		.field("randomDateField", Instant.class, ValueConvert.NO)
		.providedKeys(MyCustomKey.class)
		.ranges(ranges)

If you’re interested in this feature, could you please open a ticket? Thanks.

1 Like