mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-10-06 02:41:52 -04:00
feat(search): add nested aggregations
Sub-aggregations on both engines: bleve folds child buckets below their parent through the same collector, OpenSearch nests them natively, so a request stays one search per space. The proto gains sub_aggregations on the option and on the bucket; merge, finalize, validation and the bucket limit walk every level, results are positional at every level, a metric has no buckets to nest in. AGG-10 to AGG-15, 20, 35, 40 and 44 in the parity suite, AGG-14 as a known OpenSearch divergence.
This commit is contained in:
1 parent
1ae248bae7
commit
993951430b
17 files changed
+603
-207
No files matched your search
@@ -478,6 +478,9 @@ type AggregationOption struct {
|
||||
// rather than a bucket aggregation; the corresponding AggregationResult
|
||||
// carries `metric` instead of `buckets`.
|
||||
MetricDefinition *MetricDefinition `protobuf:"bytes,4,opt,name=metric_definition,json=metricDefinition,proto3" json:"metric_definition,omitempty"`
|
||||
// Optional. Nested aggregations computed within each bucket of this
|
||||
// aggregation.
|
||||
SubAggregations []*AggregationOption `protobuf:"bytes,5,rep,name=sub_aggregations,json=subAggregations,proto3" json:"sub_aggregations,omitempty"`
|
||||
}
|
||||
|
||||
func (x *AggregationOption) Reset() {
|
||||
@@ -540,6 +543,13 @@ func (x *AggregationOption) GetMetricDefinition() *MetricDefinition {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *AggregationOption) GetSubAggregations() []*AggregationOption {
|
||||
if x != nil {
|
||||
return x.SubAggregations
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type MetricDefinition struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -886,6 +896,9 @@ type Bucket struct {
|
||||
|
||||
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
Count int64 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"`
|
||||
// Nested aggregation results, one per sub_aggregation of the parent
|
||||
// AggregationOption, at the same position.
|
||||
SubAggregations []*AggregationResult `protobuf:"bytes,3,rep,name=sub_aggregations,json=subAggregations,proto3" json:"sub_aggregations,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Bucket) Reset() {
|
||||
@@ -934,6 +947,13 @@ func (x *Bucket) GetCount() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Bucket) GetSubAggregations() []*AggregationResult {
|
||||
if x != nil {
|
||||
return x.SubAggregations
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type IndexSpaceRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -1179,7 +1199,7 @@ var file_opencloud_services_search_v0_search_proto_rawDesc = []byte{
|
||||
0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e,
|
||||
0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c,
|
||||
0x74, 0x52, 0x0c, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22,
|
||||
0x89, 0x02, 0x0a, 0x11, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f,
|
||||
0xeb, 0x02, 0x0a, 0x11, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f,
|
||||
0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x18, 0x0a, 0x04, 0x73,
|
||||
0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x42, 0x04, 0xe2, 0x41, 0x01, 0x01, 0x52,
|
||||
@@ -1195,149 +1215,161 @@ var file_opencloud_services_search_v0_search_proto_rawDesc = []byte{
|
||||
0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e,
|
||||
0x76, 0x30, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x42, 0x04, 0xe2, 0x41, 0x01, 0x01, 0x52, 0x10, 0x6d, 0x65, 0x74, 0x72, 0x69,
|
||||
0x63, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x50, 0x0a, 0x10, 0x4d,
|
||||
0x65, 0x74, 0x72, 0x69, 0x63, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12,
|
||||
0x3c, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e,
|
||||
0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
|
||||
0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x4d, 0x65, 0x74,
|
||||
0x72, 0x69, 0x63, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x22, 0xe4, 0x01,
|
||||
0x0a, 0x10, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x12, 0x43, 0x0a, 0x07, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x79, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0e, 0x32, 0x2a, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e,
|
||||
0x63, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x60, 0x0a, 0x10, 0x73,
|
||||
0x75, 0x62, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
|
||||
0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75,
|
||||
0x64, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63,
|
||||
0x68, 0x2e, 0x76, 0x30, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x04, 0xe2, 0x41, 0x01, 0x01, 0x52, 0x0f, 0x73, 0x75,
|
||||
0x62, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x50, 0x0a,
|
||||
0x10, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x12, 0x3c, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32,
|
||||
0x28, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x73, 0x65, 0x72, 0x76,
|
||||
0x69, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x4d,
|
||||
0x65, 0x74, 0x72, 0x69, 0x63, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x22,
|
||||
0xe4, 0x01, 0x0a, 0x10, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x12, 0x43, 0x0a, 0x07, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x79, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2a, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75,
|
||||
0x64, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63,
|
||||
0x68, 0x2e, 0x76, 0x30, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x6f, 0x72, 0x74, 0x42,
|
||||
0x79, 0x52, 0x06, 0x73, 0x6f, 0x72, 0x74, 0x42, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x73, 0x5f,
|
||||
0x64, 0x65, 0x73, 0x63, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08,
|
||||
0x52, 0x0c, 0x69, 0x73, 0x44, 0x65, 0x73, 0x63, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x23,
|
||||
0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18,
|
||||
0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x43, 0x6f,
|
||||
0x75, 0x6e, 0x74, 0x12, 0x41, 0x0a, 0x06, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x04, 0x20,
|
||||
0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e,
|
||||
0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e,
|
||||
0x76, 0x30, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x6f, 0x72, 0x74, 0x42, 0x79, 0x52,
|
||||
0x06, 0x73, 0x6f, 0x72, 0x74, 0x42, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x73, 0x5f, 0x64, 0x65,
|
||||
0x73, 0x63, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c,
|
||||
0x69, 0x73, 0x44, 0x65, 0x73, 0x63, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x23, 0x0a, 0x0d,
|
||||
0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x05, 0x52, 0x0c, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x43, 0x6f, 0x75, 0x6e,
|
||||
0x74, 0x12, 0x41, 0x0a, 0x06, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28,
|
||||
0x0b, 0x32, 0x29, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x73, 0x65,
|
||||
0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30,
|
||||
0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x06, 0x72, 0x61,
|
||||
0x6e, 0x67, 0x65, 0x73, 0x22, 0x31, 0x0a, 0x0b, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x61,
|
||||
0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x02, 0x74, 0x6f, 0x22, 0xa7, 0x01, 0x0a, 0x11, 0x41, 0x67, 0x67, 0x72,
|
||||
0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x14, 0x0a,
|
||||
0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69,
|
||||
0x65, 0x6c, 0x64, 0x12, 0x3e, 0x0a, 0x07, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x02,
|
||||
0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64,
|
||||
0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68,
|
||||
0x2e, 0x76, 0x30, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x07, 0x62, 0x75, 0x63, 0x6b,
|
||||
0x65, 0x74, 0x73, 0x12, 0x3c, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e,
|
||||
0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e,
|
||||
0x76, 0x30, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69,
|
||||
0x63, 0x22, 0xb7, 0x01, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x3c, 0x0a, 0x04,
|
||||
0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, 0x6f, 0x70, 0x65,
|
||||
0x76, 0x30, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x06,
|
||||
0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x22, 0x31, 0x0a, 0x0b, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74,
|
||||
0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x74, 0x6f, 0x22, 0xa7, 0x01, 0x0a, 0x11, 0x41, 0x67,
|
||||
0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12,
|
||||
0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
|
||||
0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x3e, 0x0a, 0x07, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73,
|
||||
0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f,
|
||||
0x75, 0x64, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72,
|
||||
0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x07, 0x62, 0x75,
|
||||
0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x3c, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18,
|
||||
0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75,
|
||||
0x64, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63,
|
||||
0x68, 0x2e, 0x76, 0x30, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x06, 0x6d, 0x65, 0x74,
|
||||
0x72, 0x69, 0x63, 0x22, 0xb7, 0x01, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x3c,
|
||||
0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, 0x6f,
|
||||
0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
|
||||
0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x4d, 0x65, 0x74, 0x72,
|
||||
0x69, 0x63, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x19, 0x0a, 0x05,
|
||||
0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x05, 0x76,
|
||||
0x61, 0x6c, 0x75, 0x65, 0x88, 0x01, 0x01, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x75, 0x6d, 0x18, 0x03,
|
||||
0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x73, 0x75, 0x6d, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75,
|
||||
0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12,
|
||||
0x10, 0x0a, 0x03, 0x6d, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x6d, 0x69,
|
||||
0x6e, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03,
|
||||
0x6d, 0x61, 0x78, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x8c, 0x01,
|
||||
0x0a, 0x06, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f,
|
||||
0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74,
|
||||
0x12, 0x5a, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x6f, 0x70, 0x65,
|
||||
0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e,
|
||||
0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
|
||||
0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x19, 0x0a, 0x05, 0x76, 0x61,
|
||||
0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x05, 0x76, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x88, 0x01, 0x01, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01,
|
||||
0x28, 0x01, 0x52, 0x03, 0x73, 0x75, 0x6d, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74,
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x10, 0x0a,
|
||||
0x03, 0x6d, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x6d, 0x69, 0x6e, 0x12,
|
||||
0x10, 0x0a, 0x03, 0x6d, 0x61, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x6d, 0x61,
|
||||
0x78, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x30, 0x0a, 0x06, 0x42,
|
||||
0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x94, 0x01,
|
||||
0x0a, 0x11, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x17,
|
||||
0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x6f, 0x72, 0x63, 0x65,
|
||||
0x5f, 0x72, 0x65, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c,
|
||||
0x66, 0x6f, 0x72, 0x63, 0x65, 0x52, 0x65, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x26, 0x0a, 0x0b,
|
||||
0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28,
|
||||
0x05, 0x42, 0x04, 0xe2, 0x41, 0x01, 0x01, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72,
|
||||
0x65, 0x6e, 0x63, 0x79, 0x22, 0xd1, 0x01, 0x0a, 0x12, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x70,
|
||||
0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x73,
|
||||
0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73,
|
||||
0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x40, 0x0a, 0x0e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f,
|
||||
0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19,
|
||||
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
||||
0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x73, 0x70, 0x61, 0x63, 0x65,
|
||||
0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x6e, 0x64, 0x65,
|
||||
0x78, 0x65, 0x64, 0x5f, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03,
|
||||
0x52, 0x0d, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x53, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12,
|
||||
0x21, 0x0a, 0x0c, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18,
|
||||
0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x53, 0x70, 0x61, 0x63,
|
||||
0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x2a, 0x7d, 0x0a, 0x0a, 0x4d, 0x65, 0x74, 0x72,
|
||||
0x69, 0x63, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1b, 0x0a, 0x17, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43,
|
||||
0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45,
|
||||
0x44, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x4b, 0x49,
|
||||
0x4e, 0x44, 0x5f, 0x53, 0x55, 0x4d, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x4d, 0x45, 0x54, 0x52,
|
||||
0x49, 0x43, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4d, 0x49, 0x4e, 0x10, 0x02, 0x12, 0x13, 0x0a,
|
||||
0x0f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4d, 0x41, 0x58,
|
||||
0x10, 0x03, 0x12, 0x13, 0x0a, 0x0f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x4b, 0x49, 0x4e,
|
||||
0x44, 0x5f, 0x41, 0x56, 0x47, 0x10, 0x04, 0x2a, 0x8c, 0x01, 0x0a, 0x0c, 0x42, 0x75, 0x63, 0x6b,
|
||||
0x65, 0x74, 0x53, 0x6f, 0x72, 0x74, 0x42, 0x79, 0x12, 0x1e, 0x0a, 0x1a, 0x42, 0x55, 0x43, 0x4b,
|
||||
0x45, 0x54, 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x42, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45,
|
||||
0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x42, 0x55, 0x43, 0x4b,
|
||||
0x45, 0x54, 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x42, 0x59, 0x5f, 0x43, 0x4f, 0x55, 0x4e, 0x54,
|
||||
0x10, 0x01, 0x12, 0x20, 0x0a, 0x1c, 0x42, 0x55, 0x43, 0x4b, 0x45, 0x54, 0x5f, 0x53, 0x4f, 0x52,
|
||||
0x54, 0x5f, 0x42, 0x59, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x53, 0x5f, 0x53, 0x54, 0x52, 0x49,
|
||||
0x4e, 0x47, 0x10, 0x02, 0x12, 0x20, 0x0a, 0x1c, 0x42, 0x55, 0x43, 0x4b, 0x45, 0x54, 0x5f, 0x53,
|
||||
0x4f, 0x52, 0x54, 0x5f, 0x42, 0x59, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x53, 0x5f, 0x4e, 0x55,
|
||||
0x4d, 0x42, 0x45, 0x52, 0x10, 0x03, 0x32, 0xb3, 0x02, 0x0a, 0x0e, 0x53, 0x65, 0x61, 0x72, 0x63,
|
||||
0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x85, 0x01, 0x0a, 0x06, 0x53, 0x65,
|
||||
0x61, 0x72, 0x63, 0x68, 0x12, 0x2b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64,
|
||||
0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68,
|
||||
0x2e, 0x76, 0x30, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x2c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x73, 0x65,
|
||||
0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30,
|
||||
0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
|
||||
0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x3a, 0x01, 0x2a, 0x22, 0x15, 0x2f, 0x61, 0x70, 0x69,
|
||||
0x2f, 0x76, 0x30, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63,
|
||||
0x68, 0x12, 0x98, 0x01, 0x0a, 0x0a, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x70, 0x61, 0x63, 0x65,
|
||||
0x12, 0x2f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x73, 0x65, 0x72,
|
||||
0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x0f, 0x73, 0x75, 0x62,
|
||||
0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x94, 0x01, 0x0a,
|
||||
0x11, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x17, 0x0a,
|
||||
0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
|
||||
0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f,
|
||||
0x72, 0x65, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x66,
|
||||
0x6f, 0x72, 0x63, 0x65, 0x52, 0x65, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x26, 0x0a, 0x0b, 0x63,
|
||||
0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05,
|
||||
0x42, 0x04, 0xe2, 0x41, 0x01, 0x01, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65,
|
||||
0x6e, 0x63, 0x79, 0x22, 0xd1, 0x01, 0x0a, 0x12, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x70, 0x61,
|
||||
0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70,
|
||||
0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70,
|
||||
0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x40, 0x0a, 0x0e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x64,
|
||||
0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e,
|
||||
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
|
||||
0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x73, 0x70, 0x61, 0x63, 0x65, 0x44,
|
||||
0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x6e, 0x64, 0x65, 0x78,
|
||||
0x65, 0x64, 0x5f, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52,
|
||||
0x0d, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x53, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x21,
|
||||
0x0a, 0x0c, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x04,
|
||||
0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x53, 0x70, 0x61, 0x63, 0x65,
|
||||
0x73, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x2a, 0x7d, 0x0a, 0x0a, 0x4d, 0x65, 0x74, 0x72, 0x69,
|
||||
0x63, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1b, 0x0a, 0x17, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f,
|
||||
0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44,
|
||||
0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x4b, 0x49, 0x4e,
|
||||
0x44, 0x5f, 0x53, 0x55, 0x4d, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x4d, 0x45, 0x54, 0x52, 0x49,
|
||||
0x43, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4d, 0x49, 0x4e, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f,
|
||||
0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x4d, 0x41, 0x58, 0x10,
|
||||
0x03, 0x12, 0x13, 0x0a, 0x0f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x4b, 0x49, 0x4e, 0x44,
|
||||
0x5f, 0x41, 0x56, 0x47, 0x10, 0x04, 0x2a, 0x8c, 0x01, 0x0a, 0x0c, 0x42, 0x75, 0x63, 0x6b, 0x65,
|
||||
0x74, 0x53, 0x6f, 0x72, 0x74, 0x42, 0x79, 0x12, 0x1e, 0x0a, 0x1a, 0x42, 0x55, 0x43, 0x4b, 0x45,
|
||||
0x54, 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x42, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43,
|
||||
0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x42, 0x55, 0x43, 0x4b, 0x45,
|
||||
0x54, 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x42, 0x59, 0x5f, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x10,
|
||||
0x01, 0x12, 0x20, 0x0a, 0x1c, 0x42, 0x55, 0x43, 0x4b, 0x45, 0x54, 0x5f, 0x53, 0x4f, 0x52, 0x54,
|
||||
0x5f, 0x42, 0x59, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x53, 0x5f, 0x53, 0x54, 0x52, 0x49, 0x4e,
|
||||
0x47, 0x10, 0x02, 0x12, 0x20, 0x0a, 0x1c, 0x42, 0x55, 0x43, 0x4b, 0x45, 0x54, 0x5f, 0x53, 0x4f,
|
||||
0x52, 0x54, 0x5f, 0x42, 0x59, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x53, 0x5f, 0x4e, 0x55, 0x4d,
|
||||
0x42, 0x45, 0x52, 0x10, 0x03, 0x32, 0xb3, 0x02, 0x0a, 0x0e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68,
|
||||
0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x85, 0x01, 0x0a, 0x06, 0x53, 0x65, 0x61,
|
||||
0x72, 0x63, 0x68, 0x12, 0x2b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e,
|
||||
0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e,
|
||||
0x76, 0x30, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x1a, 0x2c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x73, 0x65, 0x72,
|
||||
0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e,
|
||||
0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x30, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x73, 0x65,
|
||||
0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30,
|
||||
0x2e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x3a, 0x01, 0x2a, 0x22, 0x1a,
|
||||
0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x30, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x69,
|
||||
0x6e, 0x64, 0x65, 0x78, 0x2d, 0x73, 0x70, 0x61, 0x63, 0x65, 0x30, 0x01, 0x32, 0xa7, 0x01, 0x0a,
|
||||
0x0d, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x95,
|
||||
0x01, 0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x30, 0x2e, 0x6f, 0x70, 0x65, 0x6e,
|
||||
0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x73,
|
||||
0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x49,
|
||||
0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x6f, 0x70,
|
||||
0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73,
|
||||
0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63,
|
||||
0x68, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26,
|
||||
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x3a, 0x01, 0x2a, 0x22, 0x1b, 0x2f, 0x61, 0x70, 0x69, 0x2f,
|
||||
0x76, 0x30, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2f,
|
||||
0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x42, 0xf2, 0x02, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75,
|
||||
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2d,
|
||||
0x65, 0x75, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c,
|
||||
0x6f, 0x75, 0x64, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x73, 0x65, 0x61, 0x72,
|
||||
0x63, 0x68, 0x2f, 0x76, 0x30, 0x92, 0x41, 0xa2, 0x02, 0x12, 0xb7, 0x01, 0x0a, 0x10, 0x4f, 0x70,
|
||||
0x65, 0x6e, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x22, 0x51,
|
||||
0x0a, 0x0e, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x20, 0x47, 0x6d, 0x62, 0x48,
|
||||
0x12, 0x29, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
|
||||
0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20,
|
||||
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x3a, 0x01, 0x2a, 0x22, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f,
|
||||
0x76, 0x30, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68,
|
||||
0x12, 0x98, 0x01, 0x0a, 0x0a, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x70, 0x61, 0x63, 0x65, 0x12,
|
||||
0x2f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x73, 0x65, 0x72, 0x76,
|
||||
0x69, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x49,
|
||||
0x6e, 0x64, 0x65, 0x78, 0x53, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x1a, 0x30, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x73, 0x65, 0x72,
|
||||
0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e,
|
||||
0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x3a, 0x01, 0x2a, 0x22, 0x1a, 0x2f,
|
||||
0x61, 0x70, 0x69, 0x2f, 0x76, 0x30, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x69, 0x6e,
|
||||
0x64, 0x65, 0x78, 0x2d, 0x73, 0x70, 0x61, 0x63, 0x65, 0x30, 0x01, 0x32, 0xa7, 0x01, 0x0a, 0x0d,
|
||||
0x49, 0x6e, 0x64, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x95, 0x01,
|
||||
0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x30, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63,
|
||||
0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x65,
|
||||
0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x49, 0x6e,
|
||||
0x64, 0x65, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x6f, 0x70, 0x65,
|
||||
0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e,
|
||||
0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68,
|
||||
0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82,
|
||||
0xd3, 0xe4, 0x93, 0x02, 0x20, 0x3a, 0x01, 0x2a, 0x22, 0x1b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76,
|
||||
0x30, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2f, 0x73,
|
||||
0x65, 0x61, 0x72, 0x63, 0x68, 0x42, 0xf2, 0x02, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
|
||||
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2d, 0x65,
|
||||
0x75, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x1a, 0x14, 0x73, 0x75, 0x70,
|
||||
0x70, 0x6f, 0x72, 0x74, 0x40, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x65,
|
||||
0x75, 0x2a, 0x49, 0x0a, 0x0a, 0x41, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2d, 0x32, 0x2e, 0x30, 0x12,
|
||||
0x3b, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
|
||||
0x75, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f,
|
||||
0x75, 0x64, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63,
|
||||
0x68, 0x2f, 0x76, 0x30, 0x92, 0x41, 0xa2, 0x02, 0x12, 0xb7, 0x01, 0x0a, 0x10, 0x4f, 0x70, 0x65,
|
||||
0x6e, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x22, 0x51, 0x0a,
|
||||
0x0e, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x20, 0x47, 0x6d, 0x62, 0x48, 0x12,
|
||||
0x29, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
|
||||
0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2d, 0x65, 0x75,
|
||||
0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x62, 0x6c, 0x6f, 0x62, 0x2f,
|
||||
0x6d, 0x61, 0x69, 0x6e, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x32, 0x05, 0x31, 0x2e,
|
||||
0x30, 0x2e, 0x30, 0x2a, 0x02, 0x01, 0x02, 0x32, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69,
|
||||
0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x72, 0x3e, 0x0a, 0x10, 0x44,
|
||||
0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x72, 0x20, 0x4d, 0x61, 0x6e, 0x75, 0x61, 0x6c, 0x12,
|
||||
0x2a, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x64, 0x6f, 0x63, 0x73, 0x2e, 0x6f, 0x70,
|
||||
0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x65, 0x75, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69,
|
||||
0x63, 0x65, 0x73, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x62, 0x06, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x33,
|
||||
0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x1a, 0x14, 0x73, 0x75, 0x70, 0x70,
|
||||
0x6f, 0x72, 0x74, 0x40, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x65, 0x75,
|
||||
0x2a, 0x49, 0x0a, 0x0a, 0x41, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2d, 0x32, 0x2e, 0x30, 0x12, 0x3b,
|
||||
0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
|
||||
0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2d, 0x65, 0x75, 0x2f,
|
||||
0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x62, 0x6c, 0x6f, 0x62, 0x2f, 0x6d,
|
||||
0x61, 0x69, 0x6e, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x32, 0x05, 0x31, 0x2e, 0x30,
|
||||
0x2e, 0x30, 0x2a, 0x02, 0x01, 0x02, 0x32, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x72, 0x3e, 0x0a, 0x10, 0x44, 0x65,
|
||||
0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x72, 0x20, 0x4d, 0x61, 0x6e, 0x75, 0x61, 0x6c, 0x12, 0x2a,
|
||||
0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x64, 0x6f, 0x63, 0x73, 0x2e, 0x6f, 0x70, 0x65,
|
||||
0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x65, 0x75, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
|
||||
0x65, 0x73, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -1385,24 +1417,26 @@ var file_opencloud_services_search_v0_search_proto_depIdxs = []int32{
|
||||
10, // 7: opencloud.services.search.v0.SearchIndexResponse.aggregations:type_name -> opencloud.services.search.v0.AggregationResult
|
||||
8, // 8: opencloud.services.search.v0.AggregationOption.bucket_definition:type_name -> opencloud.services.search.v0.BucketDefinition
|
||||
7, // 9: opencloud.services.search.v0.AggregationOption.metric_definition:type_name -> opencloud.services.search.v0.MetricDefinition
|
||||
0, // 10: opencloud.services.search.v0.MetricDefinition.kind:type_name -> opencloud.services.search.v0.MetricKind
|
||||
1, // 11: opencloud.services.search.v0.BucketDefinition.sort_by:type_name -> opencloud.services.search.v0.BucketSortBy
|
||||
9, // 12: opencloud.services.search.v0.BucketDefinition.ranges:type_name -> opencloud.services.search.v0.BucketRange
|
||||
12, // 13: opencloud.services.search.v0.AggregationResult.buckets:type_name -> opencloud.services.search.v0.Bucket
|
||||
11, // 14: opencloud.services.search.v0.AggregationResult.metric:type_name -> opencloud.services.search.v0.Metric
|
||||
0, // 15: opencloud.services.search.v0.Metric.kind:type_name -> opencloud.services.search.v0.MetricKind
|
||||
17, // 16: opencloud.services.search.v0.IndexSpaceResponse.space_duration:type_name -> google.protobuf.Duration
|
||||
2, // 17: opencloud.services.search.v0.SearchProvider.Search:input_type -> opencloud.services.search.v0.SearchRequest
|
||||
13, // 18: opencloud.services.search.v0.SearchProvider.IndexSpace:input_type -> opencloud.services.search.v0.IndexSpaceRequest
|
||||
4, // 19: opencloud.services.search.v0.IndexProvider.Search:input_type -> opencloud.services.search.v0.SearchIndexRequest
|
||||
3, // 20: opencloud.services.search.v0.SearchProvider.Search:output_type -> opencloud.services.search.v0.SearchResponse
|
||||
14, // 21: opencloud.services.search.v0.SearchProvider.IndexSpace:output_type -> opencloud.services.search.v0.IndexSpaceResponse
|
||||
5, // 22: opencloud.services.search.v0.IndexProvider.Search:output_type -> opencloud.services.search.v0.SearchIndexResponse
|
||||
20, // [20:23] is the sub-list for method output_type
|
||||
17, // [17:20] is the sub-list for method input_type
|
||||
17, // [17:17] is the sub-list for extension type_name
|
||||
17, // [17:17] is the sub-list for extension extendee
|
||||
0, // [0:17] is the sub-list for field type_name
|
||||
6, // 10: opencloud.services.search.v0.AggregationOption.sub_aggregations:type_name -> opencloud.services.search.v0.AggregationOption
|
||||
0, // 11: opencloud.services.search.v0.MetricDefinition.kind:type_name -> opencloud.services.search.v0.MetricKind
|
||||
1, // 12: opencloud.services.search.v0.BucketDefinition.sort_by:type_name -> opencloud.services.search.v0.BucketSortBy
|
||||
9, // 13: opencloud.services.search.v0.BucketDefinition.ranges:type_name -> opencloud.services.search.v0.BucketRange
|
||||
12, // 14: opencloud.services.search.v0.AggregationResult.buckets:type_name -> opencloud.services.search.v0.Bucket
|
||||
11, // 15: opencloud.services.search.v0.AggregationResult.metric:type_name -> opencloud.services.search.v0.Metric
|
||||
0, // 16: opencloud.services.search.v0.Metric.kind:type_name -> opencloud.services.search.v0.MetricKind
|
||||
10, // 17: opencloud.services.search.v0.Bucket.sub_aggregations:type_name -> opencloud.services.search.v0.AggregationResult
|
||||
17, // 18: opencloud.services.search.v0.IndexSpaceResponse.space_duration:type_name -> google.protobuf.Duration
|
||||
2, // 19: opencloud.services.search.v0.SearchProvider.Search:input_type -> opencloud.services.search.v0.SearchRequest
|
||||
13, // 20: opencloud.services.search.v0.SearchProvider.IndexSpace:input_type -> opencloud.services.search.v0.IndexSpaceRequest
|
||||
4, // 21: opencloud.services.search.v0.IndexProvider.Search:input_type -> opencloud.services.search.v0.SearchIndexRequest
|
||||
3, // 22: opencloud.services.search.v0.SearchProvider.Search:output_type -> opencloud.services.search.v0.SearchResponse
|
||||
14, // 23: opencloud.services.search.v0.SearchProvider.IndexSpace:output_type -> opencloud.services.search.v0.IndexSpaceResponse
|
||||
5, // 24: opencloud.services.search.v0.IndexProvider.Search:output_type -> opencloud.services.search.v0.SearchIndexResponse
|
||||
22, // [22:25] is the sub-list for method output_type
|
||||
19, // [19:22] is the sub-list for method input_type
|
||||
19, // [19:19] is the sub-list for extension type_name
|
||||
19, // [19:19] is the sub-list for extension extendee
|
||||
0, // [0:19] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_opencloud_services_search_v0_search_proto_init() }
|
||||
|
||||
@@ -186,6 +186,13 @@
|
||||
"metricDefinition": {
|
||||
"$ref": "#/definitions/v0MetricDefinition",
|
||||
"description": "Optional. When set, this aggregation is a scalar metric over `field`\nrather than a bucket aggregation; the corresponding AggregationResult\ncarries `metric` instead of `buckets`."
|
||||
},
|
||||
"subAggregations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v0AggregationOption"
|
||||
},
|
||||
"description": "Optional. Nested aggregations computed within each bucket of this\naggregation."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -277,6 +284,13 @@
|
||||
"count": {
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
"subAggregations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v0AggregationResult"
|
||||
},
|
||||
"description": "Nested aggregation results, one per sub_aggregation of the parent\nAggregationOption, at the same position."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -138,6 +138,9 @@ message AggregationOption {
|
||||
// rather than a bucket aggregation; the corresponding AggregationResult
|
||||
// carries `metric` instead of `buckets`.
|
||||
MetricDefinition metric_definition = 4 [(google.api.field_behavior) = OPTIONAL];
|
||||
// Optional. Nested aggregations computed within each bucket of this
|
||||
// aggregation.
|
||||
repeated AggregationOption sub_aggregations = 5 [(google.api.field_behavior) = OPTIONAL];
|
||||
}
|
||||
|
||||
message MetricDefinition {
|
||||
@@ -205,6 +208,9 @@ message Metric {
|
||||
message Bucket {
|
||||
string key = 1;
|
||||
int64 count = 2;
|
||||
// Nested aggregation results, one per sub_aggregation of the parent
|
||||
// AggregationOption, at the same position.
|
||||
repeated AggregationResult sub_aggregations = 3;
|
||||
}
|
||||
|
||||
message IndexSpaceRequest {
|
||||
|
||||
@@ -92,13 +92,17 @@ func unsupportedProperty(sr libregraph.SearchRequest) string {
|
||||
if len(sr.AggregationFilters) > 0 {
|
||||
return "aggregationFilters"
|
||||
}
|
||||
for _, a := range sr.Aggregations {
|
||||
switch {
|
||||
case a.LibreGraphGeohashDefinition != nil:
|
||||
return "@libre.graph.geohashDefinition"
|
||||
case len(a.LibreGraphSubAggregations) > 0:
|
||||
return "@libre.graph.subAggregations"
|
||||
var geohash func(aggs []libregraph.AggregationOption) bool
|
||||
geohash = func(aggs []libregraph.AggregationOption) bool {
|
||||
for _, a := range aggs {
|
||||
if a.LibreGraphGeohashDefinition != nil || geohash(a.LibreGraphSubAggregations) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
if geohash(sr.Aggregations) {
|
||||
return "@libre.graph.geohashDefinition"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -243,6 +247,9 @@ func validateAggregations(aggs []libregraph.AggregationOption) error {
|
||||
return fmt.Errorf("minimumCount of the aggregation on %q must not be negative", a.Field)
|
||||
}
|
||||
}
|
||||
if err := validateAggregations(a.LibreGraphSubAggregations); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ func indexField(field string) string {
|
||||
func resolveFields(opts []*searchsvc.AggregationOption) {
|
||||
for _, opt := range opts {
|
||||
opt.Field = indexField(opt.Field)
|
||||
resolveFields(opt.SubAggregations)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +69,9 @@ func libregraphAggregationsToSearch(in []libregraph.AggregationOption) []*search
|
||||
if a.BucketDefinition != nil {
|
||||
agg.BucketDefinition = libregraphBucketDefinitionToSearch(*a.BucketDefinition)
|
||||
}
|
||||
if len(a.LibreGraphSubAggregations) > 0 {
|
||||
agg.SubAggregations = libregraphAggregationsToSearch(a.LibreGraphSubAggregations)
|
||||
}
|
||||
if md := a.LibreGraphMetricDefinition; md != nil {
|
||||
agg.MetricDefinition = &searchsvc.MetricDefinition{Kind: metricKinds[md.Kind]}
|
||||
}
|
||||
@@ -99,8 +103,8 @@ func libregraphRangesToSearch(in []libregraph.BucketAggregationRange) []*searchs
|
||||
}
|
||||
|
||||
// searchAggregationsToLibregraph maps the results to their definitions by
|
||||
// position: the search service answers one result per aggregation, in request
|
||||
// order.
|
||||
// position, at every level: the search service answers one result per
|
||||
// aggregation, in request order.
|
||||
func searchAggregationsToLibregraph(in []*searchsvc.AggregationResult, defs []libregraph.AggregationOption) []libregraph.SearchAggregation {
|
||||
if len(in) == 0 {
|
||||
return nil
|
||||
@@ -123,8 +127,9 @@ func searchAggregationsToLibregraph(in []*searchsvc.AggregationResult, defs []li
|
||||
agg.Buckets = make([]libregraph.SearchBucket, 0, len(in[i].GetBuckets()))
|
||||
for _, b := range in[i].GetBuckets() {
|
||||
agg.Buckets = append(agg.Buckets, libregraph.SearchBucket{
|
||||
Key: libregraph.PtrString(b.GetKey()),
|
||||
Count: libregraph.PtrInt64(b.GetCount()),
|
||||
Key: libregraph.PtrString(b.GetKey()),
|
||||
Count: libregraph.PtrInt64(b.GetCount()),
|
||||
LibreGraphSubAggregations: searchAggregationsToLibregraph(b.GetSubAggregations(), def.LibreGraphSubAggregations),
|
||||
})
|
||||
}
|
||||
out = append(out, agg)
|
||||
|
||||
@@ -92,8 +92,9 @@ type searchAggregationJSON struct {
|
||||
Value *float64 `json:"value"`
|
||||
} `json:"@libre.graph.metric"`
|
||||
Buckets []struct {
|
||||
Key string `json:"key"`
|
||||
Count int64 `json:"count"`
|
||||
Key string `json:"key"`
|
||||
Count int64 `json:"count"`
|
||||
SubAggregations []searchAggregationJSON `json:"@libre.graph.subAggregations"`
|
||||
} `json:"buckets"`
|
||||
}
|
||||
|
||||
@@ -309,6 +310,8 @@ var _ = ginkgo.Describe("SearchQuery", func() {
|
||||
ginkgo.Entry("an unknown sortBy", `"aggregations": [{"field": "audio.artist", "bucketDefinition": {"sortBy": "relevance"}}]`),
|
||||
ginkgo.Entry("a size below one", `"aggregations": [{"field": "audio.artist", "size": 0}]`),
|
||||
ginkgo.Entry("a negative minimumCount", `"aggregations": [{"field": "audio.artist", "bucketDefinition": {"sortBy": "count", "minimumCount": -1}}]`),
|
||||
ginkgo.Entry("an unknown metric kind two levels down",
|
||||
`"aggregations": [{"field": "audio.artist", "@libre.graph.subAggregations": [{"field": "audio.album", "@libre.graph.subAggregations": [{"field": "audio.year", "@libre.graph.metricDefinition": {"kind": "median"}}]}]}]`),
|
||||
ginkgo.Entry("a field the index does not know", `"aggregations": [{"field": "audio.nonexistent"}]`),
|
||||
ginkgo.Entry("a range bound that is no number", `"aggregations": [{"field": "audio.year", "bucketDefinition": {"sortBy": "count", "ranges": [{"from": "1970", "to": "198o"}]}}]`),
|
||||
)
|
||||
@@ -407,7 +410,8 @@ var _ = ginkgo.Describe("SearchQuery", func() {
|
||||
ginkgo.Entry("aggregationFilters", `"aggregationFilters": ["audio.artist:\"ǂǂ5361786f6e\""]`, "aggregationFilters"),
|
||||
ginkgo.Entry("a geohash aggregation",
|
||||
`"aggregations": [{"field": "location", "@libre.graph.geohashDefinition": {"precision": 5}}]`, "geohashDefinition"),
|
||||
ginkgo.Entry("sub-aggregations", `"aggregations": [{"field": "audio.artist", "@libre.graph.subAggregations": [{"field": "audio.album"}]}]`, "subAggregations"),
|
||||
ginkgo.Entry("a nested geohash aggregation",
|
||||
`"aggregations": [{"field": "audio.artist", "@libre.graph.subAggregations": [{"field": "location", "@libre.graph.geohashDefinition": {"precision": 5}}]}]`, "geohashDefinition"),
|
||||
)
|
||||
|
||||
ginkgo.It("rejects an $expand it does not know with 400", func() {
|
||||
|
||||
@@ -32,8 +32,8 @@ func KindOf(opt *searchsvc.AggregationOption) Kind {
|
||||
}
|
||||
|
||||
// MaxBuckets is how many buckets the aggregations of one request may have in
|
||||
// one space: the search.max_buckets default of OpenSearch, held on every
|
||||
// engine.
|
||||
// one space, all levels counted: the search.max_buckets default of OpenSearch,
|
||||
// held on every engine.
|
||||
const MaxBuckets = math.MaxUint16
|
||||
|
||||
// ErrTooManyBuckets is what an engine answers beyond MaxBuckets; the service
|
||||
@@ -52,6 +52,9 @@ func countBuckets(results []*searchsvc.AggregationResult) int {
|
||||
n := 0
|
||||
for _, r := range results {
|
||||
n += len(r.GetBuckets())
|
||||
for _, b := range r.GetBuckets() {
|
||||
n += countBuckets(b.GetSubAggregations())
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -11,6 +11,17 @@ import (
|
||||
)
|
||||
|
||||
var _ = Describe("CheckBuckets", func() {
|
||||
// artists of an album each: twice as many buckets as artists
|
||||
artistsWithAlbum := func(artists int) []*searchsvc.AggregationResult {
|
||||
res := &searchsvc.AggregationResult{Field: "audio.artist"}
|
||||
for i := range artists {
|
||||
res.Buckets = append(res.Buckets, &searchsvc.Bucket{Key: strconv.Itoa(i), Count: 1, SubAggregations: []*searchsvc.AggregationResult{
|
||||
{Field: "audio.album", Buckets: []*searchsvc.Bucket{{Key: "Singles", Count: 1}}},
|
||||
}})
|
||||
}
|
||||
return []*searchsvc.AggregationResult{res}
|
||||
}
|
||||
|
||||
artists := func(count int) []*searchsvc.AggregationResult {
|
||||
res := &searchsvc.AggregationResult{Field: "audio.artist"}
|
||||
for i := range count {
|
||||
@@ -25,4 +36,9 @@ var _ = Describe("CheckBuckets", func() {
|
||||
Expect(aggregation.CheckBuckets(artists(65535))).To(Succeed())
|
||||
Expect(aggregation.CheckBuckets(artists(65536))).To(MatchError(aggregation.ErrTooManyBuckets))
|
||||
})
|
||||
|
||||
It("counts the buckets of all levels", func() {
|
||||
Expect(aggregation.CheckBuckets(artistsWithAlbum(32767))).To(Succeed(), "65534 buckets")
|
||||
Expect(aggregation.CheckBuckets(artistsWithAlbum(32768))).To(MatchError(aggregation.ErrTooManyBuckets), "65536 buckets")
|
||||
})
|
||||
})
|
||||
@@ -29,14 +29,15 @@ func Empty(opts []*searchsvc.AggregationOption) []*searchsvc.AggregationResult {
|
||||
}
|
||||
|
||||
// RangeBuckets lists every range of a range aggregation in request order: the
|
||||
// answered bucket where there is one, a count of zero otherwise.
|
||||
// answered bucket where there is one, a count of zero and empty
|
||||
// sub-aggregations otherwise.
|
||||
func RangeBuckets(opt *searchsvc.AggregationOption, answered map[string]*searchsvc.Bucket) []*searchsvc.Bucket {
|
||||
ranges := opt.GetBucketDefinition().GetRanges()
|
||||
out := make([]*searchsvc.Bucket, 0, len(ranges))
|
||||
for _, r := range ranges {
|
||||
bucket, ok := answered[RangeKey(r)]
|
||||
if !ok {
|
||||
bucket = &searchsvc.Bucket{Key: RangeKey(r)}
|
||||
bucket = &searchsvc.Bucket{Key: RangeKey(r), SubAggregations: Empty(opt.GetSubAggregations())}
|
||||
}
|
||||
out = append(out, bucket)
|
||||
}
|
||||
@@ -44,8 +45,8 @@ func RangeBuckets(opt *searchsvc.AggregationOption, answered map[string]*searchs
|
||||
}
|
||||
|
||||
// Merge folds the results of one space into acc and returns it. Results
|
||||
// belong to the option at their position, so several aggregations on one
|
||||
// field stay apart.
|
||||
// belong to the option at their position, at every level, so several
|
||||
// aggregations on one field stay apart.
|
||||
func Merge(opts []*searchsvc.AggregationOption, acc, results []*searchsvc.AggregationResult) []*searchsvc.AggregationResult {
|
||||
if acc == nil {
|
||||
acc = Empty(opts)
|
||||
@@ -70,13 +71,16 @@ func Merge(opts []*searchsvc.AggregationOption, acc, results []*searchsvc.Aggreg
|
||||
acc[i].Buckets = append(acc[i].Buckets, merged)
|
||||
}
|
||||
merged.Count += b.GetCount()
|
||||
if subs := opt.GetSubAggregations(); len(subs) > 0 {
|
||||
merged.SubAggregations = Merge(subs, merged.GetSubAggregations(), b.GetSubAggregations())
|
||||
}
|
||||
}
|
||||
}
|
||||
return acc
|
||||
}
|
||||
|
||||
// Finalize shapes merged results per their options: buckets get their minimum
|
||||
// count, order and size, metrics their value.
|
||||
// Finalize shapes merged results per their options, at every level: buckets
|
||||
// get their minimum count, order and size, metrics their value.
|
||||
func Finalize(opts []*searchsvc.AggregationOption, results []*searchsvc.AggregationResult) {
|
||||
for i, opt := range opts {
|
||||
r := results[i]
|
||||
@@ -87,6 +91,9 @@ func Finalize(opts []*searchsvc.AggregationOption, results []*searchsvc.Aggregat
|
||||
continue
|
||||
}
|
||||
r.Buckets = shapeBuckets(opt, r.GetBuckets())
|
||||
for _, b := range r.GetBuckets() {
|
||||
Finalize(opt.GetSubAggregations(), b.GetSubAggregations())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,8 +31,8 @@ func buckets(field string, bs ...*searchsvc.Bucket) *searchsvc.AggregationResult
|
||||
return &searchsvc.AggregationResult{Field: field, Buckets: bs}
|
||||
}
|
||||
|
||||
func bucket(key string, count int64) *searchsvc.Bucket {
|
||||
return &searchsvc.Bucket{Key: key, Count: count}
|
||||
func bucket(key string, count int64, subs ...*searchsvc.AggregationResult) *searchsvc.Bucket {
|
||||
return &searchsvc.Bucket{Key: key, Count: count, SubAggregations: subs}
|
||||
}
|
||||
|
||||
func rangesOf(sortBy searchsvc.BucketSortBy, rs ...*searchsvc.BucketRange) *searchsvc.BucketDefinition {
|
||||
@@ -40,23 +40,29 @@ func rangesOf(sortBy searchsvc.BucketSortBy, rs ...*searchsvc.BucketRange) *sear
|
||||
}
|
||||
|
||||
// render flattens results like the parity suite: one line per bucket or
|
||||
// metric.
|
||||
// metric, nested ones below their bucket.
|
||||
func render(results []*searchsvc.AggregationResult) []string {
|
||||
out := []string{}
|
||||
for _, r := range results {
|
||||
if m := r.GetMetric(); m != nil {
|
||||
if m.Value == nil {
|
||||
out = append(out, fmt.Sprintf("%s %s none", r.GetField(), m.GetKind()))
|
||||
} else {
|
||||
out = append(out, fmt.Sprintf("%s %s=%v", r.GetField(), m.GetKind(), m.GetValue()))
|
||||
var walk func(prefix string, results []*searchsvc.AggregationResult) []string
|
||||
walk = func(prefix string, results []*searchsvc.AggregationResult) []string {
|
||||
out := []string{}
|
||||
for _, r := range results {
|
||||
if m := r.GetMetric(); m != nil {
|
||||
if m.Value == nil {
|
||||
out = append(out, prefix+fmt.Sprintf("%s %s none", r.GetField(), m.GetKind()))
|
||||
} else {
|
||||
out = append(out, prefix+fmt.Sprintf("%s %s=%v", r.GetField(), m.GetKind(), m.GetValue()))
|
||||
}
|
||||
continue
|
||||
}
|
||||
for _, b := range r.GetBuckets() {
|
||||
line := prefix + fmt.Sprintf("%s %s=%d", r.GetField(), b.GetKey(), b.GetCount())
|
||||
out = append(out, line)
|
||||
out = append(out, walk(line+" / ", b.GetSubAggregations())...)
|
||||
}
|
||||
continue
|
||||
}
|
||||
for _, b := range r.GetBuckets() {
|
||||
out = append(out, fmt.Sprintf("%s %s=%d", r.GetField(), b.GetKey(), b.GetCount()))
|
||||
}
|
||||
return out
|
||||
}
|
||||
return out
|
||||
return walk("", results)
|
||||
}
|
||||
|
||||
// merged runs the service layer's fold over the answers of several spaces.
|
||||
@@ -81,7 +87,8 @@ var _ = Describe("Merge and Finalize", func() {
|
||||
opts := []*searchsvc.AggregationOption{
|
||||
{Field: "audio.artist"},
|
||||
metricOpt("audio.year", sum),
|
||||
{Field: "audio.year", BucketDefinition: rangesOf(searchsvc.BucketSortBy_BUCKET_SORT_BY_KEY_AS_NUMBER, &searchsvc.BucketRange{To: "1980"}, &searchsvc.BucketRange{From: "1980"})},
|
||||
{Field: "audio.year", BucketDefinition: rangesOf(searchsvc.BucketSortBy_BUCKET_SORT_BY_KEY_AS_NUMBER, &searchsvc.BucketRange{To: "1980"}, &searchsvc.BucketRange{From: "1980"}),
|
||||
SubAggregations: []*searchsvc.AggregationOption{{Field: "audio.artist"}, metricOpt("audio.year", max)}},
|
||||
}
|
||||
got := merged(opts)
|
||||
Expect(got).To(HaveLen(3))
|
||||
@@ -89,7 +96,8 @@ var _ = Describe("Merge and Finalize", func() {
|
||||
Expect(got[0].GetBuckets()).To(BeEmpty())
|
||||
Expect(render(got)).To(Equal([]string{
|
||||
"audio.year METRIC_KIND_SUM none",
|
||||
"audio.year ..1980=0", "audio.year 1980..=0",
|
||||
"audio.year ..1980=0", "audio.year ..1980=0 / audio.year METRIC_KIND_MAX none",
|
||||
"audio.year 1980..=0", "audio.year 1980..=0 / audio.year METRIC_KIND_MAX none",
|
||||
}))
|
||||
})
|
||||
|
||||
@@ -146,6 +154,44 @@ var _ = Describe("Merge and Finalize", func() {
|
||||
}))
|
||||
})
|
||||
|
||||
It("keeps sibling metrics on one field apart below a bucket that spans spaces", func() {
|
||||
opts := []*searchsvc.AggregationOption{{Field: "audio.artist", SubAggregations: []*searchsvc.AggregationOption{
|
||||
metricOpt("audio.year", sum),
|
||||
metricOpt("audio.year", avg),
|
||||
}}}
|
||||
space := func(years ...float64) []*searchsvc.AggregationResult {
|
||||
return []*searchsvc.AggregationResult{buckets("audio.artist",
|
||||
bucket("Saxon", int64(len(years)), metric("audio.year", sum, years...), metric("audio.year", avg, years...)),
|
||||
)}
|
||||
}
|
||||
got := merged(opts, space(1971, 1975), space(1980))
|
||||
Expect(render(got)).To(Equal([]string{
|
||||
"audio.artist Saxon=3",
|
||||
"audio.artist Saxon=3 / audio.year METRIC_KIND_SUM=5926",
|
||||
"audio.artist Saxon=3 / audio.year METRIC_KIND_AVG=1975.3333333333333",
|
||||
}))
|
||||
})
|
||||
|
||||
It("answers nested results in request order", func() {
|
||||
opts := []*searchsvc.AggregationOption{{Field: "audio.artist", SubAggregations: []*searchsvc.AggregationOption{
|
||||
{Field: "audio.genre"}, {Field: "audio.album"}, metricOpt("audio.year", max), {Field: "audio.composers"},
|
||||
}}}
|
||||
space := []*searchsvc.AggregationResult{buckets("audio.artist", bucket("Saxon", 1,
|
||||
buckets("audio.genre", bucket("Metal", 1)),
|
||||
buckets("audio.album", bucket("Wheels of Steel", 1)),
|
||||
metric("audio.year", max, 1980),
|
||||
buckets("audio.composers", bucket("Byford", 1)),
|
||||
))}
|
||||
for range 20 {
|
||||
got := merged(opts, space, space)
|
||||
fields := []string{}
|
||||
for _, sub := range got[0].GetBuckets()[0].GetSubAggregations() {
|
||||
fields = append(fields, sub.GetField())
|
||||
}
|
||||
Expect(fields).To(Equal([]string{"audio.genre", "audio.album", "audio.year", "audio.composers"}))
|
||||
}
|
||||
})
|
||||
|
||||
DescribeTable("reduces a metric over the values of all spaces",
|
||||
func(kind searchsvc.MetricKind, want string) {
|
||||
opts := []*searchsvc.AggregationOption{metricOpt("audio.year", kind)}
|
||||
@@ -237,5 +283,21 @@ var _ = Describe("Merge and Finalize", func() {
|
||||
)
|
||||
Expect(render(got)).To(Equal([]string{"audio.artist Saxon=2"}))
|
||||
})
|
||||
|
||||
It("shapes the buckets of a sub-aggregation by its own definition", func() {
|
||||
opts := []*searchsvc.AggregationOption{{Field: "audio.artist", SubAggregations: []*searchsvc.AggregationOption{{
|
||||
Field: "audio.album", Size: 2,
|
||||
BucketDefinition: &searchsvc.BucketDefinition{SortBy: searchsvc.BucketSortBy_BUCKET_SORT_BY_KEY_AS_STRING, MinimumCount: 2},
|
||||
}}}}
|
||||
space := []*searchsvc.AggregationResult{buckets("audio.artist", bucket("Saxon", 4, buckets("audio.album",
|
||||
bucket("Wheels of Steel", 1), bucket("Strong Arm of the Law", 1), bucket("Denim and Leather", 1), bucket("Crusader", 1),
|
||||
)))}
|
||||
Expect(render(merged(opts, space, space))).To(Equal([]string{
|
||||
"audio.artist Saxon=8",
|
||||
"audio.artist Saxon=8 / audio.album Crusader=2",
|
||||
"audio.artist Saxon=8 / audio.album Denim and Leather=2",
|
||||
}))
|
||||
Expect(render(merged(opts, space))).To(Equal([]string{"audio.artist Saxon=4"}), "one space alone stays below the minimum count")
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -21,6 +21,9 @@ func ValidateOptions(opts []*searchsvc.AggregationOption, fieldType func(string)
|
||||
if t != mapping.TypeNumeric {
|
||||
return fmt.Errorf("metric aggregation needs a numeric field, %q is none", field)
|
||||
}
|
||||
if len(opt.GetSubAggregations()) > 0 {
|
||||
return fmt.Errorf("metric aggregation on %q has no buckets to nest sub-aggregations in", field)
|
||||
}
|
||||
case KindRange:
|
||||
parsed, err := ParseRanges(field, opt.GetBucketDefinition().GetRanges())
|
||||
if err != nil {
|
||||
@@ -34,6 +37,9 @@ func ValidateOptions(opts []*searchsvc.AggregationOption, fieldType func(string)
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := ValidateOptions(opt.GetSubAggregations(), fieldType); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -39,6 +39,11 @@ var _ = Describe("ValidateOptions", func() {
|
||||
Entry("numeric ranges on a number", &searchsvc.AggregationOption{Field: "audio.year", BucketDefinition: numeric}),
|
||||
Entry("date ranges on a date", &searchsvc.AggregationOption{Field: "photo.takenDateTime", BucketDefinition: dates}),
|
||||
Entry("a metric on a number", &searchsvc.AggregationOption{Field: "audio.year", MetricDefinition: sum}),
|
||||
Entry("a tree of them", &searchsvc.AggregationOption{Field: "audio.artist", SubAggregations: []*searchsvc.AggregationOption{
|
||||
{Field: "audio.year", BucketDefinition: numeric, SubAggregations: []*searchsvc.AggregationOption{
|
||||
{Field: "audio.album"}, {Field: "audio.year", MetricDefinition: sum},
|
||||
}},
|
||||
}}),
|
||||
)
|
||||
|
||||
DescribeTable("rejects an aggregation the index cannot answer",
|
||||
@@ -53,8 +58,13 @@ var _ = Describe("ValidateOptions", func() {
|
||||
Entry("terms on a geopoint", &searchsvc.AggregationOption{Field: "location"}),
|
||||
Entry("a metric on a keyword", &searchsvc.AggregationOption{Field: "audio.artist", MetricDefinition: sum}),
|
||||
Entry("a metric on a date", &searchsvc.AggregationOption{Field: "photo.takenDateTime", MetricDefinition: sum}),
|
||||
Entry("a metric with sub-aggregations", &searchsvc.AggregationOption{Field: "audio.year", MetricDefinition: sum,
|
||||
SubAggregations: []*searchsvc.AggregationOption{{Field: "audio.artist"}}}),
|
||||
Entry("numeric ranges on a keyword", &searchsvc.AggregationOption{Field: "audio.artist", BucketDefinition: numeric}),
|
||||
Entry("numeric ranges on a date", &searchsvc.AggregationOption{Field: "photo.takenDateTime", BucketDefinition: numeric}),
|
||||
Entry("date ranges on a number", &searchsvc.AggregationOption{Field: "audio.year", BucketDefinition: dates}),
|
||||
Entry("an invalid aggregation two levels down", &searchsvc.AggregationOption{Field: "audio.artist", SubAggregations: []*searchsvc.AggregationOption{
|
||||
{Field: "audio.album", SubAggregations: []*searchsvc.AggregationOption{{Field: "photo.takenDateTime"}}},
|
||||
}}),
|
||||
)
|
||||
})
|
||||
@@ -17,8 +17,8 @@ import (
|
||||
)
|
||||
|
||||
// Bleve facets count the indexed terms of one field (numbers among them as
|
||||
// prefix-coded terms), so every aggregation is folded from doc values by
|
||||
// aggCollector, hooked into the collector walk through bleve's
|
||||
// prefix-coded terms) and cannot nest, so every aggregation is folded from doc
|
||||
// values by aggCollector, hooked into the collector walk through bleve's
|
||||
// document-match-handler context key. Loading hits for them instead costs a
|
||||
// stored-document decode per match.
|
||||
|
||||
@@ -39,6 +39,7 @@ type aggLevel struct {
|
||||
kind aggregation.Kind
|
||||
fieldType string
|
||||
ranges aggregation.Ranges
|
||||
children []*aggLevel
|
||||
}
|
||||
|
||||
// numeric values are prefix-coded in the index, their terms come from the
|
||||
@@ -56,6 +57,13 @@ func newAggLevel(opt *searchService.AggregationOption) (*aggLevel, error) {
|
||||
}
|
||||
l.ranges = ranges
|
||||
}
|
||||
for _, sub := range opt.GetSubAggregations() {
|
||||
child, err := newAggLevel(sub)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l.children = append(l.children, child)
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
@@ -69,6 +77,7 @@ type fieldValues struct {
|
||||
|
||||
type bucketAcc struct {
|
||||
counts map[string]int64
|
||||
subs map[string][]*bucketAcc
|
||||
metric *searchService.Metric
|
||||
}
|
||||
|
||||
@@ -76,7 +85,11 @@ func newBucketAcc(l *aggLevel) *bucketAcc {
|
||||
if l.kind == aggregation.KindMetric {
|
||||
return &bucketAcc{metric: &searchService.Metric{Kind: l.opt.GetMetricDefinition().GetKind()}}
|
||||
}
|
||||
return &bucketAcc{counts: map[string]int64{}}
|
||||
a := &bucketAcc{counts: map[string]int64{}}
|
||||
if len(l.children) > 0 {
|
||||
a.subs = map[string][]*bucketAcc{}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// aggCollector serves one search; bleve's collector is single-threaded.
|
||||
@@ -119,6 +132,9 @@ func (c *aggCollector) register(l *aggLevel) {
|
||||
} else {
|
||||
fv.asNumbers = true
|
||||
}
|
||||
for _, child := range l.children {
|
||||
c.register(child)
|
||||
}
|
||||
}
|
||||
|
||||
// The handler runs for every match, before the top-n cut.
|
||||
@@ -216,8 +232,22 @@ func (c *aggCollector) fold(a *bucketAcc, l *aggLevel) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *aggCollector) foldBucket(a *bucketAcc, _ *aggLevel, key string) {
|
||||
func (c *aggCollector) foldBucket(a *bucketAcc, l *aggLevel, key string) {
|
||||
a.counts[key]++
|
||||
if len(l.children) == 0 {
|
||||
return
|
||||
}
|
||||
subs, ok := a.subs[key]
|
||||
if !ok {
|
||||
subs = make([]*bucketAcc, len(l.children))
|
||||
for i, child := range l.children {
|
||||
subs[i] = newBucketAcc(child)
|
||||
}
|
||||
a.subs[key] = subs
|
||||
}
|
||||
for i, child := range l.children {
|
||||
c.fold(subs[i], child)
|
||||
}
|
||||
}
|
||||
|
||||
// results returns one result per aggregation, in request order.
|
||||
@@ -238,7 +268,11 @@ func (a *bucketAcc) result(l *aggLevel) *searchService.AggregationResult {
|
||||
|
||||
counted := make(map[string]*searchService.Bucket, len(a.counts))
|
||||
for key, count := range a.counts {
|
||||
counted[key] = &searchService.Bucket{Key: key, Count: count}
|
||||
b := &searchService.Bucket{Key: key, Count: count}
|
||||
for i, child := range l.children {
|
||||
b.SubAggregations = append(b.SubAggregations, a.subs[key][i].result(child))
|
||||
}
|
||||
counted[key] = b
|
||||
}
|
||||
if l.kind == aggregation.KindRange {
|
||||
r.Buckets = aggregation.RangeBuckets(l.opt, counted)
|
||||
|
||||
@@ -12,10 +12,10 @@ import (
|
||||
"github.com/opencloud-eu/opencloud/services/search/pkg/aggregation"
|
||||
)
|
||||
|
||||
// Build renders the aggregations as native OpenSearch aggregations, each
|
||||
// named by its position. A terms aggregation asks for every bucket up to the
|
||||
// limit: the service layer cuts to the requested size after the cross-space
|
||||
// merge, a cut here would make that merge inexact.
|
||||
// Build renders the aggregation tree as native OpenSearch aggregations, each
|
||||
// named by its position among its siblings. A terms aggregation asks for
|
||||
// every bucket up to the limit: the service layer cuts to the requested size
|
||||
// after the cross-space merge, a cut here would make that merge inexact.
|
||||
func Build(opts []*searchsvc.AggregationOption) (map[string]any, error) {
|
||||
if len(opts) == 0 {
|
||||
return nil, nil
|
||||
@@ -51,6 +51,13 @@ func build(opt *searchsvc.AggregationOption) (map[string]any, error) {
|
||||
default:
|
||||
entry = map[string]any{"terms": map[string]any{"field": opt.GetField(), "size": aggregation.MaxBuckets}}
|
||||
}
|
||||
children, err := Build(opt.GetSubAggregations())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if children != nil {
|
||||
entry["aggs"] = children
|
||||
}
|
||||
return entry, nil
|
||||
}
|
||||
|
||||
@@ -159,10 +166,18 @@ func parseBucket(opt *searchsvc.AggregationOption, raw json.RawMessage) (*search
|
||||
KeyAsString *string `json:"key_as_string"`
|
||||
DocCount int64 `json:"doc_count"`
|
||||
}
|
||||
children := aggNode{}
|
||||
if err := json.Unmarshal(raw, &head); err != nil {
|
||||
return nil, fmt.Errorf("decode bucket of %s: %w", opt.GetField(), err)
|
||||
}
|
||||
return &searchsvc.Bucket{Key: bucketKey(head.Key, head.KeyAsString), Count: head.DocCount}, nil
|
||||
if err := json.Unmarshal(raw, &children); err != nil {
|
||||
return nil, fmt.Errorf("decode bucket of %s: %w", opt.GetField(), err)
|
||||
}
|
||||
subs, err := parse(opt.GetSubAggregations(), children)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &searchsvc.Bucket{Key: bucketKey(head.Key, head.KeyAsString), Count: head.DocCount, SubAggregations: subs}, nil
|
||||
}
|
||||
|
||||
// bucketKey spells a response key like the bleve backend does: a number
|
||||
|
||||
@@ -110,6 +110,11 @@ var _ = Describe("Aggregations", func() {
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
|
||||
It("rejects a malformed range below another aggregation", func() {
|
||||
_, err := aggs.Build([]*searchsvc.AggregationOption{{Field: "audio.artist", SubAggregations: []*searchsvc.AggregationOption{malformed}}})
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
|
||||
It("lists every requested range in request order, empty ones included", func() {
|
||||
out := parse(`{"a_0": {"buckets": [
|
||||
{"key": "1970..1980", "doc_count": 4},
|
||||
@@ -177,4 +182,58 @@ var _ = Describe("Aggregations", func() {
|
||||
&searchsvc.AggregationOption{Field: "audio.year", MetricDefinition: &searchsvc.MetricDefinition{Kind: searchsvc.MetricKind_METRIC_KIND_SUM}},
|
||||
`{"a_0": {"count": "many"}}`),
|
||||
)
|
||||
|
||||
It("nests sub-aggregations in their parent, named by position", func() {
|
||||
artist := build(&searchsvc.AggregationOption{
|
||||
Field: "audio.artist",
|
||||
SubAggregations: []*searchsvc.AggregationOption{
|
||||
{Field: "audio.year", BucketDefinition: &searchsvc.BucketDefinition{Ranges: []*searchsvc.BucketRange{{To: "1980"}}},
|
||||
SubAggregations: []*searchsvc.AggregationOption{{Field: "audio.album"}}},
|
||||
{Field: "audio.duration", MetricDefinition: &searchsvc.MetricDefinition{Kind: searchsvc.MetricKind_METRIC_KIND_SUM}},
|
||||
},
|
||||
})["a_0"].(map[string]any)
|
||||
children := artist["aggs"].(map[string]any)
|
||||
Expect(children).To(HaveKey("a_1"), "the metric")
|
||||
Expect(children["a_1"].(map[string]any)).To(HaveKey("stats"))
|
||||
|
||||
years := children["a_0"].(map[string]any)
|
||||
Expect(years).To(HaveKey("range"))
|
||||
albums := years["aggs"].(map[string]any)["a_0"].(map[string]any)["terms"].(map[string]any)
|
||||
Expect(albums["field"]).To(Equal("audio.album"))
|
||||
})
|
||||
|
||||
It("reads nested results below their bucket, terms below a range below terms too", func() {
|
||||
out := parse(`{"a_0": {"buckets": [
|
||||
{"key": "Saxon", "doc_count": 2,
|
||||
"a_0": {"buckets": [
|
||||
{"key": "..1980", "doc_count": 2, "a_0": {"buckets": [{"key": "Wheels of Steel", "doc_count": 2}]}},
|
||||
{"key": "1980..", "doc_count": 0, "a_0": {"buckets": []}}
|
||||
]},
|
||||
"a_1": {"count": 2, "min": 1971.0, "max": 1975.0, "sum": 3946.0}},
|
||||
{"key": "Accept", "doc_count": 1,
|
||||
"a_0": {"buckets": [
|
||||
{"key": "..1980", "doc_count": 0, "a_0": {"buckets": []}},
|
||||
{"key": "1980..", "doc_count": 1, "a_0": {"buckets": [{"key": "Breaker", "doc_count": 1}]}}
|
||||
]},
|
||||
"a_1": {"count": 1, "min": 1981.0, "max": 1981.0, "sum": 1981.0}}
|
||||
]}}`, &searchsvc.AggregationOption{
|
||||
Field: "audio.artist",
|
||||
SubAggregations: []*searchsvc.AggregationOption{
|
||||
{Field: "audio.year", BucketDefinition: &searchsvc.BucketDefinition{Ranges: []*searchsvc.BucketRange{{To: "1980"}, {From: "1980"}}},
|
||||
SubAggregations: []*searchsvc.AggregationOption{{Field: "audio.album"}}},
|
||||
{Field: "audio.year", MetricDefinition: &searchsvc.MetricDefinition{Kind: searchsvc.MetricKind_METRIC_KIND_SUM}},
|
||||
},
|
||||
})
|
||||
Expect(keys(out[0])).To(Equal([]string{"Saxon=2", "Accept=1"}))
|
||||
|
||||
saxon := out[0].GetBuckets()[0].GetSubAggregations()
|
||||
Expect(saxon).To(HaveLen(2))
|
||||
Expect(keys(saxon[0])).To(Equal([]string{"..1980=2", "1980..=0"}))
|
||||
Expect(keys(saxon[0].GetBuckets()[0].GetSubAggregations()[0])).To(Equal([]string{"Wheels of Steel=2"}), "only the albums of this artist")
|
||||
Expect(saxon[0].GetBuckets()[1].GetSubAggregations()[0].GetBuckets()).To(BeEmpty())
|
||||
Expect(saxon[1].GetMetric().GetSum()).To(Equal(3946.0))
|
||||
|
||||
accept := out[0].GetBuckets()[1].GetSubAggregations()
|
||||
Expect(keys(accept[0].GetBuckets()[1].GetSubAggregations()[0])).To(Equal([]string{"Breaker=1"}))
|
||||
})
|
||||
})
|
||||
@@ -745,11 +745,20 @@ Fixtures:
|
||||
| AGG-07 | `mediatype:image` reads `photo.takenDateTime buckets per date range, an empty range counts zero` | photo.take...00:00Z=0, photo.take...00:00Z=1, photo.take...00:00Z=2, photo.take...00:00Z=2 | photo.take...00:00Z=0, photo.take...00:00Z=1, photo.take...00:00Z=2, photo.take...00:00Z=2 | photo.take...00:00Z=0, photo.take...00:00Z=1, photo.take...00:00Z=2, photo.take...00:00Z=2 | ✅ |
|
||||
| AGG-08 | `mediatype:image` reads `open-ended date ranges` | photo.take...00:00Z=3, photo.take...:00Z..=1 | photo.take...00:00Z=3, photo.take...:00Z..=1 | photo.take...00:00Z=3, photo.take...:00Z..=1 | ✅ |
|
||||
| AGG-09 | `mediatype:image` reads `malformed date range bound` | bad request | bad request | bad request | ✅ |
|
||||
| AGG-10 | `mediatype:audio` reads `album buckets nested in artist buckets` | audio.arti... Steel=2, audio.arti...Bomber=2, audio.arti...Spades=1, audio.artist Motörhead=3, audio.artist Saxon=2 | audio.arti... Steel=2, audio.arti...Bomber=2, audio.arti...Spades=1, audio.artist Motörhead=3, audio.artist Saxon=2 | audio.arti... Steel=2, audio.arti...Bomber=2, audio.arti...Spades=1, audio.artist Motörhead=3, audio.artist Saxon=2 | ✅ |
|
||||
| AGG-11 | `mediatype:audio` reads `sum and avg of audio.year per artist` | audio.arti... count=2, audio.arti... count=3, audio.arti...sum=3946, audio.arti...sum=5982, audio.artist Motörhead=3, audio.artist Saxon=2 | audio.arti... count=2, audio.arti... count=3, audio.arti...sum=3946, audio.arti...sum=5982, audio.artist Motörhead=3, audio.artist Saxon=2 | audio.arti... count=2, audio.arti... count=3, audio.arti...sum=3946, audio.arti...sum=5982, audio.artist Motörhead=3, audio.artist Saxon=2 | ✅ |
|
||||
| AGG-12 | `mediatype:audio` reads `artist buckets nested in audio.year decades` | audio.year 1970..1980=2, audio.year 1980..1990=1, audio.year 1990..2000=1, audio.year 2000..2010=3, audio.year... Saxon=2, audio.year...örhead=1, audio.year...örhead=1, audio.year...örhead=1 | audio.year 1970..1980=2, audio.year 1980..1990=1, audio.year 1990..2000=1, audio.year 2000..2010=3, audio.year... Saxon=2, audio.year...örhead=1, audio.year...örhead=1, audio.year...örhead=1 | audio.year 1970..1980=2, audio.year 1980..1990=1, audio.year 1990..2000=1, audio.year 2000..2010=3, audio.year... Saxon=2, audio.year...örhead=1, audio.year...örhead=1, audio.year...örhead=1 | ✅ |
|
||||
| AGG-13 | `mediatype:audio` reads `max audio.year per album per artist, three levels` | audio.arti... Steel=2, audio.arti...Bomber=2, audio.arti...Spades=1, audio.arti...max=1975, audio.arti...max=1999, audio.arti...max=2001, audio.artist Motörhead=3, audio.artist Saxon=2 | audio.arti... Steel=2, audio.arti...Bomber=2, audio.arti...Spades=1, audio.arti...max=1975, audio.arti...max=1999, audio.arti...max=2001, audio.artist Motörhead=3, audio.artist Saxon=2 | audio.arti... Steel=2, audio.arti...Bomber=2, audio.arti...Spades=1, audio.arti...max=1975, audio.arti...max=1999, audio.arti...max=2001, audio.artist Motörhead=3, audio.artist Saxon=2 | ✅ |
|
||||
| AGG-14 | `mediatype:image` reads `MimeType buckets nested in open-ended date ranges` | photo.take...00:00Z=3, photo.take...:00Z..=1, photo.take...e/jpeg=1, photo.take...e/jpeg=3 | photo.take...00:00Z=3, photo.take...:00Z..=1, photo.take...e/jpeg=1, photo.take...e/jpeg=3 | error | ❌ known |
|
||||
| AGG-15 | `mediatype:audio` reads `nested aggregations cover every match on a page of one` | 1 of 7 matches, audio.arti... Steel=2, audio.arti...Bomber=2, audio.arti...Spades=1, audio.artist Motörhead=3, audio.artist Saxon=2, audio.year sum=13942 | 1 of 7 matches, audio.arti... Steel=2, audio.arti...Bomber=2, audio.arti...Spades=1, audio.artist Motörhead=3, audio.artist Saxon=2, audio.year sum=13942 | 1 of 7 matches, audio.arti... Steel=2, audio.arti...Bomber=2, audio.arti...Spades=1, audio.artist Motörhead=3, audio.artist Saxon=2, audio.year sum=13942 | ✅ |
|
||||
| AGG-18 | `mediatype:audio` reads `two range aggregations and a metric on audio.year stay apart` | audio.year ..1980=2, audio.year ..2000=4, audio.year 1980..=5, audio.year 2000..=3, audio.year max=2009 | audio.year ..1980=2, audio.year ..2000=4, audio.year 1980..=5, audio.year 2000..=3, audio.year max=2009 | audio.year ..1980=2, audio.year ..2000=4, audio.year 1980..=5, audio.year 2000..=3, audio.year max=2009 | ✅ |
|
||||
| AGG-20 | `mediatype:audio` reads `album buckets in audio.year ranges in artist buckets` | audio.arti... Steel=2, audio.arti.....1980=0, audio.arti.....1980=2, audio.arti...1980..=0, audio.arti...1980..=3, audio.arti...Bomber=2, audio.arti...Spades=1, audio.artist Motörhead=3, … +1 more | audio.arti... Steel=2, audio.arti.....1980=0, audio.arti.....1980=2, audio.arti...1980..=0, audio.arti...1980..=3, audio.arti...Bomber=2, audio.arti...Spades=1, audio.artist Motörhead=3, … +1 more | audio.arti... Steel=2, audio.arti.....1980=0, audio.arti.....1980=2, audio.arti...1980..=0, audio.arti...1980..=3, audio.arti...Bomber=2, audio.arti...Spades=1, audio.artist Motörhead=3, … +1 more | ✅ |
|
||||
| AGG-21 | `mediatype:image` reads `a metric without a single value has none` | audio.year avg none, audio.year min none, audio.year sum none | audio.year avg none, audio.year min none, audio.year sum none | audio.year avg none, audio.year min none, audio.year sum none | ✅ |
|
||||
| AGG-34 | `mediatype:audio` reads `term buckets on the numeric audio.year` | audio.year 1971=1, audio.year 1975=1, audio.year 1982=1, audio.year 1999=1, audio.year 2001=1, audio.year 2005=1, audio.year 2009=1 | audio.year 1971=1, audio.year 1975=1, audio.year 1982=1, audio.year 1999=1, audio.year 2001=1, audio.year 2005=1, audio.year 2009=1 | audio.year 1971=1, audio.year 1975=1, audio.year 1982=1, audio.year 1999=1, audio.year 2001=1, audio.year 2005=1, audio.year 2009=1 | ✅ |
|
||||
| AGG-35 | `mediatype:audio` reads `audio.year buckets nested in artist buckets` | audio.arti...r 1971=1, audio.arti...r 1975=1, audio.arti...r 1982=1, audio.arti...r 1999=1, audio.arti...r 2001=1, audio.artist Motörhead=3, audio.artist Saxon=2 | audio.arti...r 1971=1, audio.arti...r 1975=1, audio.arti...r 1982=1, audio.arti...r 1999=1, audio.arti...r 2001=1, audio.artist Motörhead=3, audio.artist Saxon=2 | audio.arti...r 1971=1, audio.arti...r 1975=1, audio.arti...r 1982=1, audio.arti...r 1999=1, audio.arti...r 2001=1, audio.artist Motörhead=3, audio.artist Saxon=2 | ✅ |
|
||||
| AGG-37 | `mediatype:audio` reads `term buckets on the bool audio.hasDrm, spelled true and false` | audio.hasDrm false=1, audio.hasDrm true=1 | audio.hasDrm false=1, audio.hasDrm true=1 | audio.hasDrm false=1, audio.hasDrm true=1 | ✅ |
|
||||
| AGG-39 | `mediatype:audio` reads `no bucket for the empty Title of every match` | no match | no match | no match | ✅ |
|
||||
| AGG-40 | `mediatype:audio` reads `no bucket for the empty Title, with sub-aggregations neither` | no match | no match | no match | ✅ |
|
||||
| AGG-41 | `mediatype:audio` reads `matches of the same score in the order of their ids, page after page` | 3 of 7 matches, a.mp3, b.mp3, c.mp3 | 3 of 7 matches, a.mp3, b.mp3, c.mp3 | 3 of 7 matches, a.mp3, b.mp3, c.mp3 | ✅ |
|
||||
|
||||
### cardinality
|
||||
@@ -773,3 +782,4 @@ Fixtures:
|
||||
| AGG-17 | `mediatype:audio` reads `one bucket per artist, cardinality above the page size` | 10050 items | audio.arti...-00000=1, audio.arti...-00001=1, audio.arti...-00002=1, audio.arti...-00003=1, audio.arti...-00004=1, audio.arti...-00005=1, audio.arti...-00006=1, audio.arti...-00007=1, … +10042 more | audio.arti...-00000=1, audio.arti...-00001=1, audio.arti...-00002=1, audio.arti...-00003=1, audio.arti...-00004=1, audio.arti...-00005=1, audio.arti...-00006=1, audio.arti...-00007=1, … +10042 more | ✅ |
|
||||
| AGG-42 | `mediatype:audio` reads `the total counts every match` | 1 of 10050 matches | 1 of 10050 matches | 1 of 10050 matches | ✅ |
|
||||
| AGG-43 | `mediatype:audio` reads `a page reaching beyond the first 10000 matches is refused` | bad request | bad request | bad request | ✅ |
|
||||
| AGG-44 | `mediatype:audio` reads `more than 65535 buckets in one request are refused` | bad request | bad request | bad request | ✅ |
|
||||
@@ -159,6 +159,70 @@ func aggregationCases() []aggCase {
|
||||
&searchService.BucketRange{From: "2018-08-11T00:00:00Z", To: "not-a-date"},
|
||||
)}},
|
||||
wantBadRequest: true},
|
||||
{id: 10, query: "mediatype:audio", reads: "album buckets nested in artist buckets",
|
||||
aggs: []*searchService.AggregationOption{{Field: "audio.artist", SubAggregations: []*searchService.AggregationOption{{Field: "audio.album"}}}},
|
||||
want: []string{
|
||||
"audio.artist Saxon=2", "audio.artist Saxon=2 / audio.album Wheels of Steel=2",
|
||||
"audio.artist Motörhead=3", "audio.artist Motörhead=3 / audio.album Bomber=2", "audio.artist Motörhead=3 / audio.album Ace of Spades=1",
|
||||
}},
|
||||
{id: 11, query: "mediatype:audio", reads: "sum and avg of audio.year per artist",
|
||||
aggs: []*searchService.AggregationOption{{Field: "audio.artist", SubAggregations: []*searchService.AggregationOption{
|
||||
metric("audio.year", searchService.MetricKind_METRIC_KIND_SUM),
|
||||
metric("audio.year", searchService.MetricKind_METRIC_KIND_AVG),
|
||||
}}},
|
||||
want: []string{
|
||||
"audio.artist Saxon=2", "audio.artist Saxon=2 / audio.year sum=3946", "audio.artist Saxon=2 / audio.year avg sum=3946 count=2",
|
||||
"audio.artist Motörhead=3", "audio.artist Motörhead=3 / audio.year sum=5982", "audio.artist Motörhead=3 / audio.year avg sum=5982 count=3",
|
||||
}},
|
||||
{id: 12, query: "mediatype:audio", reads: "artist buckets nested in audio.year decades",
|
||||
aggs: []*searchService.AggregationOption{{Field: "audio.year", BucketDefinition: ranges(
|
||||
&searchService.BucketRange{From: "1970", To: "1980"},
|
||||
&searchService.BucketRange{From: "1980", To: "1990"},
|
||||
&searchService.BucketRange{From: "1990", To: "2000"},
|
||||
&searchService.BucketRange{From: "2000", To: "2010"},
|
||||
), SubAggregations: []*searchService.AggregationOption{{Field: "audio.artist"}}}},
|
||||
want: []string{
|
||||
"audio.year 1970..1980=2", "audio.year 1970..1980=2 / audio.artist Saxon=2",
|
||||
"audio.year 1980..1990=1", "audio.year 1980..1990=1 / audio.artist Motörhead=1",
|
||||
"audio.year 1990..2000=1", "audio.year 1990..2000=1 / audio.artist Motörhead=1",
|
||||
"audio.year 2000..2010=3", "audio.year 2000..2010=3 / audio.artist Motörhead=1",
|
||||
}},
|
||||
{id: 13, query: "mediatype:audio", reads: "max audio.year per album per artist, three levels",
|
||||
aggs: []*searchService.AggregationOption{{Field: "audio.artist", SubAggregations: []*searchService.AggregationOption{
|
||||
{Field: "audio.album", SubAggregations: []*searchService.AggregationOption{
|
||||
metric("audio.year", searchService.MetricKind_METRIC_KIND_MAX),
|
||||
}},
|
||||
}}},
|
||||
want: []string{
|
||||
"audio.artist Saxon=2", "audio.artist Saxon=2 / audio.album Wheels of Steel=2", "audio.artist Saxon=2 / audio.album Wheels of Steel=2 / audio.year max=1975",
|
||||
"audio.artist Motörhead=3", "audio.artist Motörhead=3 / audio.album Bomber=2", "audio.artist Motörhead=3 / audio.album Bomber=2 / audio.year max=1999",
|
||||
"audio.artist Motörhead=3 / audio.album Ace of Spades=1", "audio.artist Motörhead=3 / audio.album Ace of Spades=1 / audio.year max=2001",
|
||||
}},
|
||||
{id: 14, query: "mediatype:image", reads: "MimeType buckets nested in open-ended date ranges",
|
||||
aggs: []*searchService.AggregationOption{{Field: "photo.takenDateTime", BucketDefinition: ranges(
|
||||
&searchService.BucketRange{To: "2019-01-01T00:00:00Z"},
|
||||
&searchService.BucketRange{From: "2019-01-01T00:00:00Z"},
|
||||
), SubAggregations: []*searchService.AggregationOption{{Field: "MimeType"}}}},
|
||||
want: []string{
|
||||
"photo.takenDateTime ..2019-01-01T00:00:00Z=3", "photo.takenDateTime ..2019-01-01T00:00:00Z=3 / MimeType image/jpeg=3",
|
||||
"photo.takenDateTime 2019-01-01T00:00:00Z..=1", "photo.takenDateTime 2019-01-01T00:00:00Z..=1 / MimeType image/jpeg=1",
|
||||
},
|
||||
// OpenSearch maps MimeType as wildcard, which serves no doc values to
|
||||
// aggregate on; the follow-up is an aggregatable keyword sibling
|
||||
engineOverrides: map[string]override{"opensearch": {want: []string{"error"}}}},
|
||||
{id: 15, query: "mediatype:audio", reads: "nested aggregations cover every match on a page of one",
|
||||
pageSize: 1,
|
||||
aggs: []*searchService.AggregationOption{
|
||||
{Field: "audio.artist", SubAggregations: []*searchService.AggregationOption{{Field: "audio.album"}}},
|
||||
metric("audio.year", searchService.MetricKind_METRIC_KIND_SUM),
|
||||
},
|
||||
want: []string{
|
||||
"1 of 7 matches",
|
||||
"audio.artist Saxon=2", "audio.artist Saxon=2 / audio.album Wheels of Steel=2",
|
||||
"audio.artist Motörhead=3", "audio.artist Motörhead=3 / audio.album Bomber=2", "audio.artist Motörhead=3 / audio.album Ace of Spades=1",
|
||||
"audio.year sum=13942",
|
||||
}},
|
||||
// 16 and 19 pinned malformed bounds twice over, 17 is the cardinality case below
|
||||
{id: 18, query: "mediatype:audio", reads: "two range aggregations and a metric on audio.year stay apart",
|
||||
aggs: []*searchService.AggregationOption{
|
||||
{Field: "audio.year", BucketDefinition: ranges(&searchService.BucketRange{To: "1980"}, &searchService.BucketRange{From: "1980"})},
|
||||
@@ -170,6 +234,21 @@ func aggregationCases() []aggCase {
|
||||
"audio.year ..2000=4", "audio.year 2000..=3",
|
||||
"audio.year max=2009",
|
||||
}},
|
||||
{id: 20, query: "mediatype:audio", reads: "album buckets in audio.year ranges in artist buckets",
|
||||
aggs: []*searchService.AggregationOption{{Field: "audio.artist", SubAggregations: []*searchService.AggregationOption{{
|
||||
Field: "audio.year",
|
||||
BucketDefinition: ranges(&searchService.BucketRange{To: "1980"}, &searchService.BucketRange{From: "1980"}),
|
||||
SubAggregations: []*searchService.AggregationOption{{Field: "audio.album"}},
|
||||
}}}},
|
||||
want: []string{
|
||||
"audio.artist Saxon=2",
|
||||
"audio.artist Saxon=2 / audio.year ..1980=2", "audio.artist Saxon=2 / audio.year ..1980=2 / audio.album Wheels of Steel=2",
|
||||
"audio.artist Saxon=2 / audio.year 1980..=0",
|
||||
"audio.artist Motörhead=3",
|
||||
"audio.artist Motörhead=3 / audio.year ..1980=0",
|
||||
"audio.artist Motörhead=3 / audio.year 1980..=3",
|
||||
"audio.artist Motörhead=3 / audio.year 1980..=3 / audio.album Bomber=2", "audio.artist Motörhead=3 / audio.year 1980..=3 / audio.album Ace of Spades=1",
|
||||
}},
|
||||
{id: 21, query: "mediatype:image", reads: "a metric without a single value has none",
|
||||
aggs: []*searchService.AggregationOption{
|
||||
metric("audio.year", searchService.MetricKind_METRIC_KIND_SUM),
|
||||
@@ -183,11 +262,20 @@ func aggregationCases() []aggCase {
|
||||
"audio.year 1971=1", "audio.year 1975=1", "audio.year 1982=1", "audio.year 1999=1",
|
||||
"audio.year 2001=1", "audio.year 2005=1", "audio.year 2009=1",
|
||||
}},
|
||||
{id: 35, query: "mediatype:audio", reads: "audio.year buckets nested in artist buckets",
|
||||
aggs: []*searchService.AggregationOption{{Field: "audio.artist", SubAggregations: []*searchService.AggregationOption{{Field: "audio.year"}}}},
|
||||
want: []string{
|
||||
"audio.artist Saxon=2", "audio.artist Saxon=2 / audio.year 1971=1", "audio.artist Saxon=2 / audio.year 1975=1",
|
||||
"audio.artist Motörhead=3", "audio.artist Motörhead=3 / audio.year 1982=1",
|
||||
"audio.artist Motörhead=3 / audio.year 1999=1", "audio.artist Motörhead=3 / audio.year 2001=1",
|
||||
}},
|
||||
{id: 37, query: "mediatype:audio", reads: "term buckets on the bool audio.hasDrm, spelled true and false",
|
||||
aggs: []*searchService.AggregationOption{{Field: "audio.hasDrm"}},
|
||||
want: []string{"audio.hasDrm true=1", "audio.hasDrm false=1"}},
|
||||
{id: 39, query: "mediatype:audio", reads: "no bucket for the empty Title of every match",
|
||||
aggs: []*searchService.AggregationOption{{Field: "Title"}}},
|
||||
{id: 40, query: "mediatype:audio", reads: "no bucket for the empty Title, with sub-aggregations neither",
|
||||
aggs: []*searchService.AggregationOption{{Field: "Title", SubAggregations: []*searchService.AggregationOption{{Field: "audio.artist"}}}}},
|
||||
{id: 41, query: "mediatype:audio", reads: "matches of the same score in the order of their ids, page after page",
|
||||
pageSize: 3, listMatches: true,
|
||||
want: []string{"3 of 7 matches", "a.mp3", "b.mp3", "c.mp3"}},
|
||||
@@ -195,7 +283,7 @@ func aggregationCases() []aggCase {
|
||||
}
|
||||
|
||||
// renderAggregations flattens an answer into comparable strings, one per
|
||||
// bucket or metric.
|
||||
// bucket or metric. A nested result renders under its bucket, joined by " / ".
|
||||
func renderAggregations(resp *searchService.SearchIndexResponse, err error) []string {
|
||||
if err != nil {
|
||||
if _, ok := err.(errtypes.BadRequest); ok {
|
||||
@@ -227,7 +315,11 @@ func renderAggregation(prefix string, a *searchService.AggregationResult) []stri
|
||||
|
||||
out := []string{}
|
||||
for _, b := range a.Buckets {
|
||||
out = append(out, prefix+fmt.Sprintf("%s %s=%d", a.Field, b.Key, b.Count))
|
||||
line := prefix + fmt.Sprintf("%s %s=%d", a.Field, b.Key, b.Count)
|
||||
out = append(out, line)
|
||||
for _, sub := range b.SubAggregations {
|
||||
out = append(out, renderAggregation(line+" / ", sub)...)
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
@@ -243,6 +335,15 @@ func cardinalityFixtures() []search.Resource {
|
||||
return docs
|
||||
}
|
||||
|
||||
// nested puts a terms aggregation on each field into the one before it.
|
||||
func nested(fields ...string) *searchService.AggregationOption {
|
||||
opt := &searchService.AggregationOption{Field: fields[0]}
|
||||
if len(fields) > 1 {
|
||||
opt.SubAggregations = []*searchService.AggregationOption{nested(fields[1:]...)}
|
||||
}
|
||||
return opt
|
||||
}
|
||||
|
||||
func cardinalityCases() []aggCase {
|
||||
count := 10050
|
||||
return []aggCase{
|
||||
@@ -255,6 +356,9 @@ func cardinalityCases() []aggCase {
|
||||
{id: 43, query: "mediatype:audio", reads: "a page reaching beyond the first 10000 matches is refused",
|
||||
pageSize: 10001,
|
||||
wantBadRequest: true},
|
||||
{id: 44, query: "mediatype:audio", reads: "more than 65535 buckets in one request are refused",
|
||||
aggs: []*searchService.AggregationOption{nested("audio.artist", "Name", "ID", "RootID", "ParentID", "audio.album", "audio.year")},
|
||||
wantBadRequest: true},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user