diff --git a/README.md b/README.md index b1c852b..149619b 100644 --- a/README.md +++ b/README.md @@ -302,6 +302,11 @@ This endpoint returns the latest cluster status. "part": 56, "search": 59, "were": 57 + }, + "timestamp_date_range": { + "last_year": 59, + "this_year": 0, + "year_before_last": 0 } }, "documents": [ diff --git a/examples/search_with_aggregation.json b/examples/search_with_aggregation.json index 21d976a..7bea5c5 100644 --- a/examples/search_with_aggregation.json +++ b/examples/search_with_aggregation.json @@ -24,16 +24,36 @@ "field": "id", "ranges": { "low": { - "from": 0, - "to": 500 + "low": 0, + "high": 500 }, "medium": { - "from": 500, - "to": 1000 + "low": 500, + "high": 1000 }, "high": { - "from": 1000, - "to": 1500 + "low": 1000, + "high": 1500 + } + } + } + }, + "timestamp_date_range": { + "type": "date_range", + "options": { + "field": "timestamp", + "ranges": { + "year_before_last": { + "start": "2020-01-01T00:00:00Z", + "end": "2021-01-01T00:00:00Z" + }, + "last_year": { + "start": "2021-01-01T00:00:00Z", + "end": "2022-01-01T00:00:00Z" + }, + "this_year": { + "start": "2022-01-01T00:00:00Z", + "end": "2023-01-01T00:00:00Z" } } } diff --git a/search/aggregations/aggregations.go b/search/aggregations/aggregations.go index 49a8630..bfed7e6 100644 --- a/search/aggregations/aggregations.go +++ b/search/aggregations/aggregations.go @@ -8,19 +8,22 @@ const ( AggregationTypeUnknown AggregationType = iota AggregationTypeTerms AggregationTypeRange + AggregationTypeDateRange ) // Maps for AggregationType. var ( AggregationType_name = map[AggregationType]string{ - AggregationTypeUnknown: "unknown", - AggregationTypeTerms: "terms", - AggregationTypeRange: "range", + AggregationTypeUnknown: "unknown", + AggregationTypeTerms: "terms", + AggregationTypeRange: "range", + AggregationTypeDateRange: "date_range", } AggregationType_value = map[string]AggregationType{ - "unknown": AggregationTypeUnknown, - "terms": AggregationTypeTerms, - "range": AggregationTypeRange, + "unknown": AggregationTypeUnknown, + "terms": AggregationTypeTerms, + "range": AggregationTypeRange, + "date_range": AggregationTypeDateRange, } ) diff --git a/search/aggregations/range.go b/search/aggregations/range.go index 2170fe9..340331b 100644 --- a/search/aggregations/range.go +++ b/search/aggregations/range.go @@ -8,21 +8,24 @@ import ( ) // Create new RangeAggregation with given options. +// Each bucket represents the number of documents +// that the condition between `low`` or more and less than `high`. +// low <= number < high // Options example: // { // "field": "id", // "ranges": { // "low": { -// "from": 0, -// "to": 500 +// "low": 0, +// "high": 500 // }, // "medium": { -// "from": 500, -// "to": 1000 +// "low": 500, +// "high": 1000 // }, // "high": { -// "from": 1000, -// "to": 1500 +// "low": 1000, +// "high": 1500 // } // } // } @@ -51,14 +54,14 @@ func NewRangeAggregationWithOptions(opts map[string]interface{}) (*aggregations. return nil, fmt.Errorf("range %v option is unexpected: %v", name, rangeValue) } - from, ok := rangeMap["from"].(float64) + from, ok := rangeMap["low"].(float64) if !ok { - return nil, fmt.Errorf("range %v from option is unexpected: %v", name, rangeMap["from"]) + return nil, fmt.Errorf("range %v low option is unexpected: %v", name, rangeMap["low"]) } - to, ok := rangeMap["to"].(float64) + to, ok := rangeMap["high"].(float64) if !ok { - return nil, fmt.Errorf("range %v to option is unexpected: %v", name, rangeMap["to"]) + return nil, fmt.Errorf("range %v high option is unexpected: %v", name, rangeMap["high"]) } rangesAgg.AddRange(aggregations.NamedRange(name, from, to)) diff --git a/search/aggregations/range_date.go b/search/aggregations/range_date.go new file mode 100644 index 0000000..8f7c11d --- /dev/null +++ b/search/aggregations/range_date.go @@ -0,0 +1,80 @@ +package aggregations + +import ( + "fmt" + + "github.com/blugelabs/bluge/search" + "github.com/blugelabs/bluge/search/aggregations" + "github.com/mosuka/phalanx/mapping" +) + +// Create new DateRangeAggregation with given options. +// Each bucket represents the number of documents +// that the condition between `start` or more and less than `end`. +// start <= datetime < end +// Options example: +// { +// "field": "timestamp", +// "ranges": { +// "year_before_last": { +// "start": "2020-01-01T00:00:00Z", +// "end": "2021-01-01T00:00:00Z" +// }, +// "last_year": { +// "start": "2021-01-01T00:00:00Z", +// "end": "2022-01-01T00:00:00Z" +// }, +// "this_year": { +// "start": "2022-01-01T00:00:00Z", +// "end": "2023-01-01T00:00:00Z" +// } +// } +// } +func NewDateRangeAggregationWithOptions(opts map[string]interface{}) (*aggregations.DateRangeAggregation, error) { + fieldValue, ok := opts["field"] + if !ok { + return nil, fmt.Errorf("field option does not exist") + } + field, ok := fieldValue.(string) + if !ok { + return nil, fmt.Errorf("field option is unexpected: %v", fieldValue) + } + if len(field) == 0 { + return nil, fmt.Errorf("field option is empty") + } + + dateRangesAgg := aggregations.DateRanges(search.Field(field)) + + ranges, ok := opts["ranges"].(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("ranges option does not exist") + } + for name, rangeValue := range ranges { + rangeMap, ok := rangeValue.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("range %v option is unexpected: %v", name, rangeValue) + } + + startStr, ok := rangeMap["start"].(string) + if !ok { + return nil, fmt.Errorf("range %v start option is unexpected: %v", name, rangeMap["start"]) + } + start, err := mapping.MakeDateTimeWithRfc3339(startStr) + if err != nil { + return nil, fmt.Errorf("range %v start option is unexpected: %v", name, startStr) + } + + endStr, ok := rangeMap["end"].(string) + if !ok { + return nil, fmt.Errorf("range %v end option is unexpected: %v", name, rangeMap["high"]) + } + end, err := mapping.MakeDateTimeWithRfc3339(endStr) + if err != nil { + return nil, fmt.Errorf("range %v start option is unexpected: %v", name, endStr) + } + + dateRangesAgg.AddRange(aggregations.NewNamedDateRange(name, start, end)) + } + + return dateRangesAgg, nil +} diff --git a/server/index_service.go b/server/index_service.go index 45515e1..1d2ffbf 100644 --- a/server/index_service.go +++ b/server/index_service.go @@ -1131,6 +1131,33 @@ func (s *IndexService) Search(req *proto.SearchRequest) (*proto.SearchResponse, } blugeRequest.AddAggregation(name, rangeAgg) + case phalanxaggregations.AggregationType_name[phalanxaggregations.AggregationTypeDateRange]: + opts := make(map[string]interface{}) + if err := json.Unmarshal(agg.Options, &opts); err != nil { + s.logger.Error(err.Error(), zap.String("type", agg.Type), zap.String("options", string(agg.Options))) + responsesChan <- searchResponse{ + nodeName: nodeName, + indexName: request.IndexName, + shardNames: request.ShardNames, + resp: nil, + err: err, + } + return err + } + dateRangeAgg, err := phalanxaggregations.NewDateRangeAggregationWithOptions(opts) + if err != nil { + s.logger.Error(err.Error(), zap.String("type", agg.Type), zap.String("options", string(agg.Options))) + responsesChan <- searchResponse{ + nodeName: nodeName, + indexName: request.IndexName, + shardNames: request.ShardNames, + resp: nil, + err: err, + } + return err + } + + blugeRequest.AddAggregation(name, dateRangeAgg) } }