mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-10-06 10:52:08 -04:00
feat(search): add aggregation filter tokens
Server-issued aggregationFilterToken on buckets, consumed verbatim via searchRequest.aggregationFilters. The graph layer decodes a filter into its buckets (a term, a range, or an or(...) of them), the proto carries them as AggregationFilter, and the engines match them natively: a key as the exact value of the field, a range from-inclusive and to-exclusive, so a drilldown returns exactly the matches the bucket counted. Filters are validated like aggregations, in both layers. Range tokens use the MS Graph spelling (range(min, 1980), range(2010, max, to="le")). AGG-22 to AGG-33, 36, 38 and 45 in the parity suite.
This commit is contained in:
1 parent
993951430b
commit
f14a92d67a
20 files changed
+1120
-237
No files matched your search
@@ -146,6 +146,9 @@ type SearchRequest struct {
|
||||
Ref *v0.Reference `protobuf:"bytes,4,opt,name=ref,proto3" json:"ref,omitempty"`
|
||||
// Optional. Aggregations (facets) to compute alongside the matches.
|
||||
Aggregations []*AggregationOption `protobuf:"bytes,5,rep,name=aggregations,proto3" json:"aggregations,omitempty"`
|
||||
// Optional. Filters narrowing the matches to buckets of a prior
|
||||
// aggregation. Combined with `query` and with each other via AND.
|
||||
AggregationFilters []*AggregationFilter `protobuf:"bytes,6,rep,name=aggregation_filters,json=aggregationFilters,proto3" json:"aggregation_filters,omitempty"`
|
||||
// Optional. Number of leading matches to skip in the globally sorted,
|
||||
// cross-space merged result list.
|
||||
From int32 `protobuf:"varint,7,opt,name=from,proto3" json:"from,omitempty"`
|
||||
@@ -218,6 +221,13 @@ func (x *SearchRequest) GetAggregations() []*AggregationOption {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SearchRequest) GetAggregationFilters() []*AggregationFilter {
|
||||
if x != nil {
|
||||
return x.AggregationFilters
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SearchRequest) GetFrom() int32 {
|
||||
if x != nil {
|
||||
return x.From
|
||||
@@ -314,6 +324,9 @@ type SearchIndexRequest struct {
|
||||
Ref *v0.Reference `protobuf:"bytes,4,opt,name=ref,proto3" json:"ref,omitempty"`
|
||||
// Optional. Per-space aggregations (facets) to compute alongside the matches.
|
||||
Aggregations []*AggregationOption `protobuf:"bytes,5,rep,name=aggregations,proto3" json:"aggregations,omitempty"`
|
||||
// Optional. Filters narrowing the matches to buckets of a prior
|
||||
// aggregation; the engine ANDs them with `query`.
|
||||
AggregationFilters []*AggregationFilter `protobuf:"bytes,6,rep,name=aggregation_filters,json=aggregationFilters,proto3" json:"aggregation_filters,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SearchIndexRequest) Reset() {
|
||||
@@ -383,6 +396,13 @@ func (x *SearchIndexRequest) GetAggregations() []*AggregationOption {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SearchIndexRequest) GetAggregationFilters() []*AggregationFilter {
|
||||
if x != nil {
|
||||
return x.AggregationFilters
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SearchIndexResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -730,6 +750,73 @@ func (x *BucketRange) GetTo() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// AggregationFilter narrows the matches to buckets of one field. A match has
|
||||
// to be in any of the listed buckets.
|
||||
type AggregationFilter struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Field string `protobuf:"bytes,1,opt,name=field,proto3" json:"field,omitempty"`
|
||||
// Bucket keys of a terms aggregation, matched exactly and case-sensitively.
|
||||
Terms []string `protobuf:"bytes,2,rep,name=terms,proto3" json:"terms,omitempty"`
|
||||
// Buckets of a range aggregation.
|
||||
Ranges []*BucketRange `protobuf:"bytes,3,rep,name=ranges,proto3" json:"ranges,omitempty"`
|
||||
}
|
||||
|
||||
func (x *AggregationFilter) Reset() {
|
||||
*x = AggregationFilter{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_opencloud_services_search_v0_search_proto_msgTypes[8]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *AggregationFilter) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*AggregationFilter) ProtoMessage() {}
|
||||
|
||||
func (x *AggregationFilter) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_opencloud_services_search_v0_search_proto_msgTypes[8]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use AggregationFilter.ProtoReflect.Descriptor instead.
|
||||
func (*AggregationFilter) Descriptor() ([]byte, []int) {
|
||||
return file_opencloud_services_search_v0_search_proto_rawDescGZIP(), []int{8}
|
||||
}
|
||||
|
||||
func (x *AggregationFilter) GetField() string {
|
||||
if x != nil {
|
||||
return x.Field
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AggregationFilter) GetTerms() []string {
|
||||
if x != nil {
|
||||
return x.Terms
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *AggregationFilter) GetRanges() []*BucketRange {
|
||||
if x != nil {
|
||||
return x.Ranges
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// One result per AggregationOption, at the same position.
|
||||
type AggregationResult struct {
|
||||
state protoimpl.MessageState
|
||||
@@ -746,7 +833,7 @@ type AggregationResult struct {
|
||||
func (x *AggregationResult) Reset() {
|
||||
*x = AggregationResult{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_opencloud_services_search_v0_search_proto_msgTypes[8]
|
||||
mi := &file_opencloud_services_search_v0_search_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -759,7 +846,7 @@ func (x *AggregationResult) String() string {
|
||||
func (*AggregationResult) ProtoMessage() {}
|
||||
|
||||
func (x *AggregationResult) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_opencloud_services_search_v0_search_proto_msgTypes[8]
|
||||
mi := &file_opencloud_services_search_v0_search_proto_msgTypes[9]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -772,7 +859,7 @@ func (x *AggregationResult) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use AggregationResult.ProtoReflect.Descriptor instead.
|
||||
func (*AggregationResult) Descriptor() ([]byte, []int) {
|
||||
return file_opencloud_services_search_v0_search_proto_rawDescGZIP(), []int{8}
|
||||
return file_opencloud_services_search_v0_search_proto_rawDescGZIP(), []int{9}
|
||||
}
|
||||
|
||||
func (x *AggregationResult) GetField() string {
|
||||
@@ -818,7 +905,7 @@ type Metric struct {
|
||||
func (x *Metric) Reset() {
|
||||
*x = Metric{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_opencloud_services_search_v0_search_proto_msgTypes[9]
|
||||
mi := &file_opencloud_services_search_v0_search_proto_msgTypes[10]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -831,7 +918,7 @@ func (x *Metric) String() string {
|
||||
func (*Metric) ProtoMessage() {}
|
||||
|
||||
func (x *Metric) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_opencloud_services_search_v0_search_proto_msgTypes[9]
|
||||
mi := &file_opencloud_services_search_v0_search_proto_msgTypes[10]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -844,7 +931,7 @@ func (x *Metric) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use Metric.ProtoReflect.Descriptor instead.
|
||||
func (*Metric) Descriptor() ([]byte, []int) {
|
||||
return file_opencloud_services_search_v0_search_proto_rawDescGZIP(), []int{9}
|
||||
return file_opencloud_services_search_v0_search_proto_rawDescGZIP(), []int{10}
|
||||
}
|
||||
|
||||
func (x *Metric) GetKind() MetricKind {
|
||||
@@ -904,7 +991,7 @@ type Bucket struct {
|
||||
func (x *Bucket) Reset() {
|
||||
*x = Bucket{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_opencloud_services_search_v0_search_proto_msgTypes[10]
|
||||
mi := &file_opencloud_services_search_v0_search_proto_msgTypes[11]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -917,7 +1004,7 @@ func (x *Bucket) String() string {
|
||||
func (*Bucket) ProtoMessage() {}
|
||||
|
||||
func (x *Bucket) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_opencloud_services_search_v0_search_proto_msgTypes[10]
|
||||
mi := &file_opencloud_services_search_v0_search_proto_msgTypes[11]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -930,7 +1017,7 @@ func (x *Bucket) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use Bucket.ProtoReflect.Descriptor instead.
|
||||
func (*Bucket) Descriptor() ([]byte, []int) {
|
||||
return file_opencloud_services_search_v0_search_proto_rawDescGZIP(), []int{10}
|
||||
return file_opencloud_services_search_v0_search_proto_rawDescGZIP(), []int{11}
|
||||
}
|
||||
|
||||
func (x *Bucket) GetKey() string {
|
||||
@@ -968,7 +1055,7 @@ type IndexSpaceRequest struct {
|
||||
func (x *IndexSpaceRequest) Reset() {
|
||||
*x = IndexSpaceRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_opencloud_services_search_v0_search_proto_msgTypes[11]
|
||||
mi := &file_opencloud_services_search_v0_search_proto_msgTypes[12]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -981,7 +1068,7 @@ func (x *IndexSpaceRequest) String() string {
|
||||
func (*IndexSpaceRequest) ProtoMessage() {}
|
||||
|
||||
func (x *IndexSpaceRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_opencloud_services_search_v0_search_proto_msgTypes[11]
|
||||
mi := &file_opencloud_services_search_v0_search_proto_msgTypes[12]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -994,7 +1081,7 @@ func (x *IndexSpaceRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use IndexSpaceRequest.ProtoReflect.Descriptor instead.
|
||||
func (*IndexSpaceRequest) Descriptor() ([]byte, []int) {
|
||||
return file_opencloud_services_search_v0_search_proto_rawDescGZIP(), []int{11}
|
||||
return file_opencloud_services_search_v0_search_proto_rawDescGZIP(), []int{12}
|
||||
}
|
||||
|
||||
func (x *IndexSpaceRequest) GetSpaceId() string {
|
||||
@@ -1045,7 +1132,7 @@ type IndexSpaceResponse struct {
|
||||
func (x *IndexSpaceResponse) Reset() {
|
||||
*x = IndexSpaceResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_opencloud_services_search_v0_search_proto_msgTypes[12]
|
||||
mi := &file_opencloud_services_search_v0_search_proto_msgTypes[13]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1058,7 +1145,7 @@ func (x *IndexSpaceResponse) String() string {
|
||||
func (*IndexSpaceResponse) ProtoMessage() {}
|
||||
|
||||
func (x *IndexSpaceResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_opencloud_services_search_v0_search_proto_msgTypes[12]
|
||||
mi := &file_opencloud_services_search_v0_search_proto_msgTypes[13]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1071,7 +1158,7 @@ func (x *IndexSpaceResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use IndexSpaceResponse.ProtoReflect.Descriptor instead.
|
||||
func (*IndexSpaceResponse) Descriptor() ([]byte, []int) {
|
||||
return file_opencloud_services_search_v0_search_proto_rawDescGZIP(), []int{12}
|
||||
return file_opencloud_services_search_v0_search_proto_rawDescGZIP(), []int{13}
|
||||
}
|
||||
|
||||
func (x *IndexSpaceResponse) GetSpaceId() string {
|
||||
@@ -1130,7 +1217,7 @@ var file_opencloud_services_search_v0_search_proto_rawDesc = []byte{
|
||||
0x6f, 0x62, 0x75, 0x66, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb6, 0x02, 0x0a, 0x0d, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9e, 0x03, 0x0a, 0x0d, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f,
|
||||
0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x42, 0x04, 0xe2, 0x41, 0x01, 0x01,
|
||||
0x48, 0x00, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x88, 0x01, 0x01, 0x12,
|
||||
@@ -1147,42 +1234,55 @@ var file_opencloud_services_search_v0_search_proto_rawDesc = []byte{
|
||||
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, 0x0c, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x18, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x07,
|
||||
0x20, 0x01, 0x28, 0x05, 0x42, 0x04, 0xe2, 0x41, 0x01, 0x01, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d,
|
||||
0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x22, 0xf1,
|
||||
0x01, 0x0a, 0x0e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x12, 0x3d, 0x0a, 0x07, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03,
|
||||
0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6d,
|
||||
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76,
|
||||
0x30, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x52, 0x07, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73,
|
||||
0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f,
|
||||
0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50,
|
||||
0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x74, 0x61,
|
||||
0x6c, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52,
|
||||
0x0c, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x53, 0x0a,
|
||||
0x0c, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x66, 0x0a, 0x13, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x06, 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, 0x52, 0x65,
|
||||
0x73, 0x75, 0x6c, 0x74, 0x52, 0x0c, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x73, 0x22, 0xa1, 0x02, 0x0a, 0x12, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x49, 0x6e, 0x64,
|
||||
0x65, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x09, 0x70, 0x61, 0x67,
|
||||
0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x42, 0x04, 0xe2, 0x41,
|
||||
0x01, 0x01, 0x48, 0x00, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x88, 0x01,
|
||||
0x01, 0x12, 0x23, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x04, 0xe2, 0x41, 0x01, 0x01, 0x52, 0x09, 0x70, 0x61, 0x67,
|
||||
0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18,
|
||||
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x3f, 0x0a, 0x03,
|
||||
0x72, 0x65, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6f, 0x70, 0x65, 0x6e,
|
||||
0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73,
|
||||
0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e,
|
||||
0x63, 0x65, 0x42, 0x04, 0xe2, 0x41, 0x01, 0x01, 0x52, 0x03, 0x72, 0x65, 0x66, 0x12, 0x59, 0x0a,
|
||||
0x0c, 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, 0x0c, 0x61, 0x67, 0x67, 0x72,
|
||||
0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x70, 0x61, 0x67,
|
||||
0x76, 0x30, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69,
|
||||
0x6c, 0x74, 0x65, 0x72, 0x42, 0x04, 0xe2, 0x41, 0x01, 0x01, 0x52, 0x12, 0x61, 0x67, 0x67, 0x72,
|
||||
0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x12, 0x18,
|
||||
0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x42, 0x04, 0xe2, 0x41,
|
||||
0x01, 0x01, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x70, 0x61, 0x67,
|
||||
0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x22, 0xf1, 0x01, 0x0a, 0x0e, 0x53, 0x65, 0x61, 0x72, 0x63,
|
||||
0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x07, 0x6d, 0x61, 0x74,
|
||||
0x63, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6f, 0x70, 0x65,
|
||||
0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e,
|
||||
0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x52,
|
||||
0x07, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74,
|
||||
0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e,
|
||||
0x12, 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65,
|
||||
0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x61,
|
||||
0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x53, 0x0a, 0x0c, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 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, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x0c, 0x61, 0x67,
|
||||
0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x89, 0x03, 0x0a, 0x12, 0x53,
|
||||
0x65, 0x61, 0x72, 0x63, 0x68, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x12, 0x26, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x05, 0x42, 0x04, 0xe2, 0x41, 0x01, 0x01, 0x48, 0x00, 0x52, 0x08, 0x70, 0x61,
|
||||
0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x88, 0x01, 0x01, 0x12, 0x23, 0x0a, 0x0a, 0x70, 0x61, 0x67,
|
||||
0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x04, 0xe2,
|
||||
0x41, 0x01, 0x01, 0x52, 0x09, 0x70, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x14,
|
||||
0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71,
|
||||
0x75, 0x65, 0x72, 0x79, 0x12, 0x3f, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x27, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6d, 0x65,
|
||||
0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30,
|
||||
0x2e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x04, 0xe2, 0x41, 0x01, 0x01,
|
||||
0x52, 0x03, 0x72, 0x65, 0x66, 0x12, 0x59, 0x0a, 0x0c, 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, 0x0c, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
|
||||
0x12, 0x66, 0x0a, 0x13, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f,
|
||||
0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x06, 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, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x04,
|
||||
0xe2, 0x41, 0x01, 0x01, 0x52, 0x12, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x70, 0x61, 0x67,
|
||||
0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x22, 0xf6, 0x01, 0x0a, 0x13, 0x53, 0x65, 0x61, 0x72, 0x63,
|
||||
0x68, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d,
|
||||
0x0a, 0x07, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
@@ -1244,132 +1344,140 @@ var file_opencloud_services_search_v0_search_proto_rawDesc = []byte{
|
||||
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,
|
||||
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x74, 0x6f, 0x22, 0x82, 0x01, 0x0a, 0x11, 0x41, 0x67,
|
||||
0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 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,
|
||||
0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x18, 0x02,
|
||||
0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x12, 0x41, 0x0a, 0x06, 0x72,
|
||||
0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x03, 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, 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, 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,
|
||||
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,
|
||||
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, 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,
|
||||
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, 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, 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, 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, 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,
|
||||
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, 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, 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, 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,7 +1493,7 @@ func file_opencloud_services_search_v0_search_proto_rawDescGZIP() []byte {
|
||||
}
|
||||
|
||||
var file_opencloud_services_search_v0_search_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
|
||||
var file_opencloud_services_search_v0_search_proto_msgTypes = make([]protoimpl.MessageInfo, 13)
|
||||
var file_opencloud_services_search_v0_search_proto_msgTypes = make([]protoimpl.MessageInfo, 14)
|
||||
var file_opencloud_services_search_v0_search_proto_goTypes = []interface{}{
|
||||
(MetricKind)(0), // 0: opencloud.services.search.v0.MetricKind
|
||||
(BucketSortBy)(0), // 1: opencloud.services.search.v0.BucketSortBy
|
||||
@@ -1397,46 +1505,50 @@ var file_opencloud_services_search_v0_search_proto_goTypes = []interface{}{
|
||||
(*MetricDefinition)(nil), // 7: opencloud.services.search.v0.MetricDefinition
|
||||
(*BucketDefinition)(nil), // 8: opencloud.services.search.v0.BucketDefinition
|
||||
(*BucketRange)(nil), // 9: opencloud.services.search.v0.BucketRange
|
||||
(*AggregationResult)(nil), // 10: opencloud.services.search.v0.AggregationResult
|
||||
(*Metric)(nil), // 11: opencloud.services.search.v0.Metric
|
||||
(*Bucket)(nil), // 12: opencloud.services.search.v0.Bucket
|
||||
(*IndexSpaceRequest)(nil), // 13: opencloud.services.search.v0.IndexSpaceRequest
|
||||
(*IndexSpaceResponse)(nil), // 14: opencloud.services.search.v0.IndexSpaceResponse
|
||||
(*v0.Reference)(nil), // 15: opencloud.messages.search.v0.Reference
|
||||
(*v0.Match)(nil), // 16: opencloud.messages.search.v0.Match
|
||||
(*durationpb.Duration)(nil), // 17: google.protobuf.Duration
|
||||
(*AggregationFilter)(nil), // 10: opencloud.services.search.v0.AggregationFilter
|
||||
(*AggregationResult)(nil), // 11: opencloud.services.search.v0.AggregationResult
|
||||
(*Metric)(nil), // 12: opencloud.services.search.v0.Metric
|
||||
(*Bucket)(nil), // 13: opencloud.services.search.v0.Bucket
|
||||
(*IndexSpaceRequest)(nil), // 14: opencloud.services.search.v0.IndexSpaceRequest
|
||||
(*IndexSpaceResponse)(nil), // 15: opencloud.services.search.v0.IndexSpaceResponse
|
||||
(*v0.Reference)(nil), // 16: opencloud.messages.search.v0.Reference
|
||||
(*v0.Match)(nil), // 17: opencloud.messages.search.v0.Match
|
||||
(*durationpb.Duration)(nil), // 18: google.protobuf.Duration
|
||||
}
|
||||
var file_opencloud_services_search_v0_search_proto_depIdxs = []int32{
|
||||
15, // 0: opencloud.services.search.v0.SearchRequest.ref:type_name -> opencloud.messages.search.v0.Reference
|
||||
16, // 0: opencloud.services.search.v0.SearchRequest.ref:type_name -> opencloud.messages.search.v0.Reference
|
||||
6, // 1: opencloud.services.search.v0.SearchRequest.aggregations:type_name -> opencloud.services.search.v0.AggregationOption
|
||||
16, // 2: opencloud.services.search.v0.SearchResponse.matches:type_name -> opencloud.messages.search.v0.Match
|
||||
10, // 3: opencloud.services.search.v0.SearchResponse.aggregations:type_name -> opencloud.services.search.v0.AggregationResult
|
||||
15, // 4: opencloud.services.search.v0.SearchIndexRequest.ref:type_name -> opencloud.messages.search.v0.Reference
|
||||
6, // 5: opencloud.services.search.v0.SearchIndexRequest.aggregations:type_name -> opencloud.services.search.v0.AggregationOption
|
||||
16, // 6: opencloud.services.search.v0.SearchIndexResponse.matches:type_name -> opencloud.messages.search.v0.Match
|
||||
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
|
||||
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
|
||||
10, // 2: opencloud.services.search.v0.SearchRequest.aggregation_filters:type_name -> opencloud.services.search.v0.AggregationFilter
|
||||
17, // 3: opencloud.services.search.v0.SearchResponse.matches:type_name -> opencloud.messages.search.v0.Match
|
||||
11, // 4: opencloud.services.search.v0.SearchResponse.aggregations:type_name -> opencloud.services.search.v0.AggregationResult
|
||||
16, // 5: opencloud.services.search.v0.SearchIndexRequest.ref:type_name -> opencloud.messages.search.v0.Reference
|
||||
6, // 6: opencloud.services.search.v0.SearchIndexRequest.aggregations:type_name -> opencloud.services.search.v0.AggregationOption
|
||||
10, // 7: opencloud.services.search.v0.SearchIndexRequest.aggregation_filters:type_name -> opencloud.services.search.v0.AggregationFilter
|
||||
17, // 8: opencloud.services.search.v0.SearchIndexResponse.matches:type_name -> opencloud.messages.search.v0.Match
|
||||
11, // 9: opencloud.services.search.v0.SearchIndexResponse.aggregations:type_name -> opencloud.services.search.v0.AggregationResult
|
||||
8, // 10: opencloud.services.search.v0.AggregationOption.bucket_definition:type_name -> opencloud.services.search.v0.BucketDefinition
|
||||
7, // 11: opencloud.services.search.v0.AggregationOption.metric_definition:type_name -> opencloud.services.search.v0.MetricDefinition
|
||||
6, // 12: opencloud.services.search.v0.AggregationOption.sub_aggregations:type_name -> opencloud.services.search.v0.AggregationOption
|
||||
0, // 13: opencloud.services.search.v0.MetricDefinition.kind:type_name -> opencloud.services.search.v0.MetricKind
|
||||
1, // 14: opencloud.services.search.v0.BucketDefinition.sort_by:type_name -> opencloud.services.search.v0.BucketSortBy
|
||||
9, // 15: opencloud.services.search.v0.BucketDefinition.ranges:type_name -> opencloud.services.search.v0.BucketRange
|
||||
9, // 16: opencloud.services.search.v0.AggregationFilter.ranges:type_name -> opencloud.services.search.v0.BucketRange
|
||||
13, // 17: opencloud.services.search.v0.AggregationResult.buckets:type_name -> opencloud.services.search.v0.Bucket
|
||||
12, // 18: opencloud.services.search.v0.AggregationResult.metric:type_name -> opencloud.services.search.v0.Metric
|
||||
0, // 19: opencloud.services.search.v0.Metric.kind:type_name -> opencloud.services.search.v0.MetricKind
|
||||
11, // 20: opencloud.services.search.v0.Bucket.sub_aggregations:type_name -> opencloud.services.search.v0.AggregationResult
|
||||
18, // 21: opencloud.services.search.v0.IndexSpaceResponse.space_duration:type_name -> google.protobuf.Duration
|
||||
2, // 22: opencloud.services.search.v0.SearchProvider.Search:input_type -> opencloud.services.search.v0.SearchRequest
|
||||
14, // 23: opencloud.services.search.v0.SearchProvider.IndexSpace:input_type -> opencloud.services.search.v0.IndexSpaceRequest
|
||||
4, // 24: opencloud.services.search.v0.IndexProvider.Search:input_type -> opencloud.services.search.v0.SearchIndexRequest
|
||||
3, // 25: opencloud.services.search.v0.SearchProvider.Search:output_type -> opencloud.services.search.v0.SearchResponse
|
||||
15, // 26: opencloud.services.search.v0.SearchProvider.IndexSpace:output_type -> opencloud.services.search.v0.IndexSpaceResponse
|
||||
5, // 27: opencloud.services.search.v0.IndexProvider.Search:output_type -> opencloud.services.search.v0.SearchIndexResponse
|
||||
25, // [25:28] is the sub-list for method output_type
|
||||
22, // [22:25] is the sub-list for method input_type
|
||||
22, // [22:22] is the sub-list for extension type_name
|
||||
22, // [22:22] is the sub-list for extension extendee
|
||||
0, // [0:22] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_opencloud_services_search_v0_search_proto_init() }
|
||||
@@ -1542,7 +1654,7 @@ func file_opencloud_services_search_v0_search_proto_init() {
|
||||
}
|
||||
}
|
||||
file_opencloud_services_search_v0_search_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*AggregationResult); i {
|
||||
switch v := v.(*AggregationFilter); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
@@ -1554,7 +1666,7 @@ func file_opencloud_services_search_v0_search_proto_init() {
|
||||
}
|
||||
}
|
||||
file_opencloud_services_search_v0_search_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Metric); i {
|
||||
switch v := v.(*AggregationResult); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
@@ -1566,7 +1678,7 @@ func file_opencloud_services_search_v0_search_proto_init() {
|
||||
}
|
||||
}
|
||||
file_opencloud_services_search_v0_search_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Bucket); i {
|
||||
switch v := v.(*Metric); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
@@ -1578,7 +1690,7 @@ func file_opencloud_services_search_v0_search_proto_init() {
|
||||
}
|
||||
}
|
||||
file_opencloud_services_search_v0_search_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*IndexSpaceRequest); i {
|
||||
switch v := v.(*Bucket); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
@@ -1590,6 +1702,18 @@ func file_opencloud_services_search_v0_search_proto_init() {
|
||||
}
|
||||
}
|
||||
file_opencloud_services_search_v0_search_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*IndexSpaceRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_opencloud_services_search_v0_search_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*IndexSpaceResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -1604,14 +1728,14 @@ func file_opencloud_services_search_v0_search_proto_init() {
|
||||
}
|
||||
file_opencloud_services_search_v0_search_proto_msgTypes[0].OneofWrappers = []interface{}{}
|
||||
file_opencloud_services_search_v0_search_proto_msgTypes[2].OneofWrappers = []interface{}{}
|
||||
file_opencloud_services_search_v0_search_proto_msgTypes[9].OneofWrappers = []interface{}{}
|
||||
file_opencloud_services_search_v0_search_proto_msgTypes[10].OneofWrappers = []interface{}{}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_opencloud_services_search_v0_search_proto_rawDesc,
|
||||
NumEnums: 2,
|
||||
NumMessages: 13,
|
||||
NumMessages: 14,
|
||||
NumExtensions: 0,
|
||||
NumServices: 2,
|
||||
},
|
||||
|
||||
@@ -167,6 +167,29 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"v0AggregationFilter": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string"
|
||||
},
|
||||
"terms": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Bucket keys of a terms aggregation, matched exactly and case-sensitively."
|
||||
},
|
||||
"ranges": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v0BucketRange"
|
||||
},
|
||||
"description": "Buckets of a range aggregation."
|
||||
}
|
||||
},
|
||||
"description": "AggregationFilter narrows the matches to buckets of one field. A match has\nto be in any of the listed buckets."
|
||||
},
|
||||
"v0AggregationOption": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -697,6 +720,13 @@
|
||||
"$ref": "#/definitions/v0AggregationOption"
|
||||
},
|
||||
"description": "Optional. Per-space aggregations (facets) to compute alongside the matches."
|
||||
},
|
||||
"aggregationFilters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v0AggregationFilter"
|
||||
},
|
||||
"description": "Optional. Filters narrowing the matches to buckets of a prior\naggregation; the engine ANDs them with `query`."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -751,6 +781,13 @@
|
||||
},
|
||||
"description": "Optional. Aggregations (facets) to compute alongside the matches."
|
||||
},
|
||||
"aggregationFilters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v0AggregationFilter"
|
||||
},
|
||||
"description": "Optional. Filters narrowing the matches to buckets of a prior\naggregation. Combined with `query` and with each other via AND."
|
||||
},
|
||||
"from": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
|
||||
@@ -76,6 +76,10 @@ message SearchRequest {
|
||||
// Optional. Aggregations (facets) to compute alongside the matches.
|
||||
repeated AggregationOption aggregations = 5 [(google.api.field_behavior) = OPTIONAL];
|
||||
|
||||
// Optional. Filters narrowing the matches to buckets of a prior
|
||||
// aggregation. Combined with `query` and with each other via AND.
|
||||
repeated AggregationFilter aggregation_filters = 6 [(google.api.field_behavior) = OPTIONAL];
|
||||
|
||||
// Optional. Number of leading matches to skip in the globally sorted,
|
||||
// cross-space merged result list.
|
||||
int32 from = 7 [(google.api.field_behavior) = OPTIONAL];
|
||||
@@ -107,6 +111,10 @@ message SearchIndexRequest {
|
||||
|
||||
// Optional. Per-space aggregations (facets) to compute alongside the matches.
|
||||
repeated AggregationOption aggregations = 5 [(google.api.field_behavior) = OPTIONAL];
|
||||
|
||||
// Optional. Filters narrowing the matches to buckets of a prior
|
||||
// aggregation; the engine ANDs them with `query`.
|
||||
repeated AggregationFilter aggregation_filters = 6 [(google.api.field_behavior) = OPTIONAL];
|
||||
}
|
||||
|
||||
message SearchIndexResponse {
|
||||
@@ -181,6 +189,16 @@ message BucketRange {
|
||||
string to = 2;
|
||||
}
|
||||
|
||||
// AggregationFilter narrows the matches to buckets of one field. A match has
|
||||
// to be in any of the listed buckets.
|
||||
message AggregationFilter {
|
||||
string field = 1;
|
||||
// Bucket keys of a terms aggregation, matched exactly and case-sensitively.
|
||||
repeated string terms = 2;
|
||||
// Buckets of a range aggregation.
|
||||
repeated BucketRange ranges = 3;
|
||||
}
|
||||
|
||||
// One result per AggregationOption, at the same position.
|
||||
message AggregationResult {
|
||||
string field = 1;
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package filtertoken_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestFilterToken(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Filter Token Suite")
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
// Package filtertoken encodes and decodes the MS Graph aggregationFilterToken:
|
||||
// quoted hex terms tokens behind a U+01C2 pair, and range(from, to) range tokens.
|
||||
package filtertoken
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// U+01C2 LATIN LETTER ALVEOLAR CLICK, twice
|
||||
const termPrefix = "ǂǂ"
|
||||
|
||||
const (
|
||||
openLower = "min"
|
||||
openUpper = "max"
|
||||
// MS Graph appends this to an open upper bound; the bound itself is
|
||||
// still max, so the marker carries no extra information.
|
||||
openUpperMarker = `to="le"`
|
||||
)
|
||||
|
||||
// Filter is a decoded aggregationFilters entry: the buckets of one field a
|
||||
// match has to be in, any of them.
|
||||
type Filter struct {
|
||||
Field string
|
||||
Terms []string
|
||||
Ranges []Range
|
||||
}
|
||||
|
||||
// Range has an empty bound on an open side.
|
||||
type Range struct {
|
||||
From, To string
|
||||
}
|
||||
|
||||
// EncodeTermsToken hex-encodes the bucket key; the quotes are part of the token value.
|
||||
func EncodeTermsToken(key string) string {
|
||||
return `"` + termPrefix + hex.EncodeToString([]byte(key)) + `"`
|
||||
}
|
||||
|
||||
// EncodeRangeToken uses the MS Graph spelling: open bounds are min/max, an open
|
||||
// upper bound carries to="le".
|
||||
func EncodeRangeToken(from, to string) string {
|
||||
if from == "" {
|
||||
from = openLower
|
||||
}
|
||||
if to == "" {
|
||||
return "range(" + from + ", " + openUpper + ", " + openUpperMarker + ")"
|
||||
}
|
||||
return "range(" + from + ", " + to + ")"
|
||||
}
|
||||
|
||||
// DecodeFilter reads {field}:{token}, the token being a terms token, a range
|
||||
// token or an or(...) of them. Anything not shaped like a server-issued token
|
||||
// is rejected; the bounds of a range are returned as written.
|
||||
func DecodeFilter(filter string) (Filter, error) {
|
||||
field, token, ok := strings.Cut(filter, ":")
|
||||
if !ok || field == "" || token == "" {
|
||||
return Filter{}, fmt.Errorf("invalid aggregation filter %q", filter)
|
||||
}
|
||||
f := Filter{Field: field}
|
||||
tokens := []string{token}
|
||||
if inner, ok := trimCall(token, "or"); ok {
|
||||
tokens = splitArguments(inner)
|
||||
}
|
||||
for _, t := range tokens {
|
||||
if inner, ok := trimCall(t, "range"); ok {
|
||||
r, err := decodeRange(inner)
|
||||
if err != nil {
|
||||
return Filter{}, fmt.Errorf("invalid range token %q: %w", t, err)
|
||||
}
|
||||
f.Ranges = append(f.Ranges, r)
|
||||
continue
|
||||
}
|
||||
term, err := decodeTerm(t)
|
||||
if err != nil {
|
||||
return Filter{}, err
|
||||
}
|
||||
f.Terms = append(f.Terms, term)
|
||||
}
|
||||
// the server issues one kind of token per field
|
||||
if len(f.Terms) > 0 && len(f.Ranges) > 0 {
|
||||
return Filter{}, fmt.Errorf("invalid aggregation filter %q: terms and ranges in one or()", filter)
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func decodeTerm(token string) (string, error) {
|
||||
inner, ok := strings.CutPrefix(token, `"`+termPrefix)
|
||||
if ok {
|
||||
inner, ok = strings.CutSuffix(inner, `"`)
|
||||
}
|
||||
// the encoder writes lowercase hex, and no bucket has an empty key
|
||||
if !ok || inner == "" || inner != strings.ToLower(inner) {
|
||||
return "", fmt.Errorf("invalid terms token %q", token)
|
||||
}
|
||||
b, err := hex.DecodeString(inner)
|
||||
if err != nil || !utf8.Valid(b) {
|
||||
return "", fmt.Errorf("invalid terms token %q", token)
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
func decodeRange(arguments string) (Range, error) {
|
||||
segments := splitArguments(arguments)
|
||||
if len(segments) == 3 && segments[1] == openUpper && segments[2] == openUpperMarker {
|
||||
segments = segments[:2]
|
||||
}
|
||||
if len(segments) != 2 || segments[0] == "" || segments[1] == "" {
|
||||
return Range{}, fmt.Errorf("expected a lower and an upper bound")
|
||||
}
|
||||
var r Range
|
||||
if segments[0] != openLower {
|
||||
r.From = segments[0]
|
||||
}
|
||||
if segments[1] != openUpper {
|
||||
r.To = segments[1]
|
||||
}
|
||||
if r.From == "" && r.To == "" {
|
||||
return Range{}, fmt.Errorf("no bounds")
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func trimCall(token, name string) (string, bool) {
|
||||
if !strings.HasPrefix(token, name+"(") || !strings.HasSuffix(token, ")") {
|
||||
return "", false
|
||||
}
|
||||
return token[len(name)+1 : len(token)-1], true
|
||||
}
|
||||
|
||||
// splitArguments splits on the commas outside of parentheses and trims the
|
||||
// optional whitespace around each argument.
|
||||
func splitArguments(s string) []string {
|
||||
var out []string
|
||||
depth, start := 0, 0
|
||||
for i, r := range s {
|
||||
switch r {
|
||||
case '(':
|
||||
depth++
|
||||
case ')':
|
||||
depth--
|
||||
case ',':
|
||||
if depth == 0 {
|
||||
out = append(out, strings.TrimSpace(s[start:i]))
|
||||
start = i + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
return append(out, strings.TrimSpace(s[start:]))
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package filtertoken_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/opencloud-eu/opencloud/services/graph/pkg/filtertoken"
|
||||
)
|
||||
|
||||
var _ = Describe("Token", func() {
|
||||
Describe("EncodeTermsToken", func() {
|
||||
It("encodes the key as quoted ǂǂ-prefixed lowercase hex", func() {
|
||||
Expect(filtertoken.EncodeTermsToken("And the Bands Played On")).To(Equal(`"ǂǂ416e64207468652042616e647320506c61796564204f6e"`))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("EncodeRangeToken", func() {
|
||||
DescribeTable("bounds, and the token decodes back to them",
|
||||
func(from, to, want string) {
|
||||
Expect(filtertoken.EncodeRangeToken(from, to)).To(Equal(want))
|
||||
got, err := filtertoken.DecodeFilter("Size:" + want)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(got).To(Equal(filtertoken.Filter{Field: "Size", Ranges: []filtertoken.Range{{From: from, To: to}}}))
|
||||
},
|
||||
Entry("closed", "0", "100", "range(0, 100)"),
|
||||
Entry("open lower", "", "100", "range(min, 100)"),
|
||||
Entry("open upper", "0", "", `range(0, max, to="le")`),
|
||||
)
|
||||
})
|
||||
|
||||
Describe("DecodeFilter", func() {
|
||||
DescribeTable("valid tokens",
|
||||
func(filter string, want filtertoken.Filter) {
|
||||
got, err := filtertoken.DecodeFilter(filter)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(got).To(Equal(want))
|
||||
},
|
||||
Entry("terms", `audio.artist:"ǂǂ5361786f6e"`,
|
||||
filtertoken.Filter{Field: "audio.artist", Terms: []string{"Saxon"}}),
|
||||
Entry("closed range", "Size:range(0,100)",
|
||||
filtertoken.Filter{Field: "Size", Ranges: []filtertoken.Range{{From: "0", To: "100"}}}),
|
||||
Entry("open lower range", "Size:range(min,100)",
|
||||
filtertoken.Filter{Field: "Size", Ranges: []filtertoken.Range{{To: "100"}}}),
|
||||
Entry("open upper range", "Size:range(0,max)",
|
||||
filtertoken.Filter{Field: "Size", Ranges: []filtertoken.Range{{From: "0"}}}),
|
||||
Entry("open upper range with le marker", `Size:range(0, max, to="le")`,
|
||||
filtertoken.Filter{Field: "Size", Ranges: []filtertoken.Range{{From: "0"}}}),
|
||||
Entry("date range, the colons of the bounds are no field separator",
|
||||
"photo.takenDateTime:range(2018-08-11T00:00:00Z, 2018-08-12T00:00:00Z)",
|
||||
filtertoken.Filter{Field: "photo.takenDateTime", Ranges: []filtertoken.Range{{From: "2018-08-11T00:00:00Z", To: "2018-08-12T00:00:00Z"}}}),
|
||||
Entry("or of two terms with spaces",
|
||||
`audio.artist:or("ǂǂ5361786f6e", "ǂǂ49726f6e204d616964656e")`,
|
||||
filtertoken.Filter{Field: "audio.artist", Terms: []string{"Saxon", "Iron Maiden"}}),
|
||||
Entry("or of two ranges, the commas of a range do not split it",
|
||||
`audio.year:or(range(min, 1980),range(2010, max, to="le"))`,
|
||||
filtertoken.Filter{Field: "audio.year", Ranges: []filtertoken.Range{{To: "1980"}, {From: "2010"}}}),
|
||||
)
|
||||
|
||||
DescribeTable("rejected tokens",
|
||||
func(filter string) {
|
||||
_, err := filtertoken.DecodeFilter(filter)
|
||||
Expect(err).To(HaveOccurred())
|
||||
},
|
||||
Entry("no colon", `audio.artist"ǂǂ00"`),
|
||||
Entry("empty field", `:"ǂǂ00"`),
|
||||
Entry("missing ǂǂ prefix", `audio.artist:"deadbeef"`),
|
||||
Entry("missing quotes", `audio.artist:ǂǂ5361786f6e`),
|
||||
Entry("odd hex", `audio.artist:"ǂǂabc"`),
|
||||
Entry("uppercase hex, the server issues lowercase", `audio.artist:"ǂǂ5361786F6E"`),
|
||||
Entry("empty key", `audio.artist:"ǂǂ"`),
|
||||
Entry("hex that is no UTF-8", `audio.artist:"ǂǂff"`),
|
||||
Entry("terms and ranges in one or", `audio.year:or("ǂǂ31393830", range(1990, max, to="le"))`),
|
||||
Entry("range without bounds", "Size:range(min,max)"),
|
||||
Entry("range with one argument", "Size:range(100)"),
|
||||
Entry("range with an empty bound", "Size:range(,100)"),
|
||||
Entry("le marker on a closed range", `Size:range(0, 100, to="le")`),
|
||||
Entry("empty or", "audio.artist:or()"),
|
||||
Entry("nested or", `audio.artist:or(or("ǂǂ5361786f6e"))`),
|
||||
Entry("plain value", "audio.artist:Saxon"),
|
||||
)
|
||||
|
||||
DescribeTable("round-trips every terms key, a term is data and no query syntax",
|
||||
func(key string) {
|
||||
got, err := filtertoken.DecodeFilter("audio.artist:" + filtertoken.EncodeTermsToken(key))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(got).To(Equal(filtertoken.Filter{Field: "audio.artist", Terms: []string{key}}))
|
||||
},
|
||||
Entry("slash", "AC/DC"),
|
||||
Entry("double quote", `The "Chirping" Crickets`),
|
||||
Entry("wildcards", "Wh*t?"),
|
||||
Entry("comma and parentheses", "Emerson, Lake (and Palmer)"),
|
||||
Entry("colon", "Sunn O))): live"),
|
||||
Entry("umlaut", "Motörhead"),
|
||||
)
|
||||
|
||||
It("returns range bounds as written, they never become query syntax", func() {
|
||||
got, err := filtertoken.DecodeFilter("audio.year:range(1980 OR Name:*, 1990)")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(got.Ranges).To(Equal([]filtertoken.Range{{From: "1980 OR Name:*", To: "1990"}}))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -89,9 +89,6 @@ func unsupportedProperty(sr libregraph.SearchRequest) string {
|
||||
if len(sr.SortProperties) > 0 {
|
||||
return "sortProperties"
|
||||
}
|
||||
if len(sr.AggregationFilters) > 0 {
|
||||
return "aggregationFilters"
|
||||
}
|
||||
var geohash func(aggs []libregraph.AggregationOption) bool
|
||||
geohash = func(aggs []libregraph.AggregationOption) bool {
|
||||
for _, a := range aggs {
|
||||
@@ -121,18 +118,29 @@ func searchRequestOf(sr libregraph.SearchRequest) (*searchsvc.SearchRequest, err
|
||||
return nil, err
|
||||
}
|
||||
aggregations := libregraphAggregationsToSearch(sr.Aggregations)
|
||||
filters, err := aggregationFiltersToSearch(sr.AggregationFilters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
requestFieldType := func(field string) string { return search.AggregatableFieldType(indexField(field)) }
|
||||
if err := aggregation.ValidateOptions(aggregations, requestFieldType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := aggregation.ValidateFilters(filters, requestFieldType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resolveFields(aggregations)
|
||||
for _, f := range filters {
|
||||
f.Field = indexField(f.Field)
|
||||
}
|
||||
|
||||
from, size := pagination(sr.From, sr.Size)
|
||||
return &searchsvc.SearchRequest{
|
||||
Query: sr.Query.QueryString,
|
||||
From: from,
|
||||
PageSize: &size,
|
||||
Aggregations: aggregations,
|
||||
Query: sr.Query.QueryString,
|
||||
From: from,
|
||||
PageSize: &size,
|
||||
Aggregations: aggregations,
|
||||
AggregationFilters: filters,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
|
||||
searchmsg "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/messages/search/v0"
|
||||
searchsvc "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/services/search/v0"
|
||||
"github.com/opencloud-eu/opencloud/services/graph/pkg/filtertoken"
|
||||
"github.com/opencloud-eu/opencloud/services/search/pkg/aggregation"
|
||||
"github.com/opencloud-eu/opencloud/services/search/pkg/mapping"
|
||||
)
|
||||
|
||||
@@ -59,6 +61,24 @@ func resolveFields(opts []*searchsvc.AggregationOption) {
|
||||
}
|
||||
}
|
||||
|
||||
// the token format is graph API surface: decode here, the search service only
|
||||
// sees the selected buckets
|
||||
func aggregationFiltersToSearch(filters []string) ([]*searchsvc.AggregationFilter, error) {
|
||||
out := make([]*searchsvc.AggregationFilter, 0, len(filters))
|
||||
for _, f := range filters {
|
||||
decoded, err := filtertoken.DecodeFilter(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
filter := &searchsvc.AggregationFilter{Field: decoded.Field, Terms: decoded.Terms}
|
||||
for _, r := range decoded.Ranges {
|
||||
filter.Ranges = append(filter.Ranges, &searchsvc.BucketRange{From: r.From, To: r.To})
|
||||
}
|
||||
out = append(out, filter)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func libregraphAggregationsToSearch(in []libregraph.AggregationOption) []*searchsvc.AggregationOption {
|
||||
out := make([]*searchsvc.AggregationOption, 0, len(in))
|
||||
for _, a := range in {
|
||||
@@ -124,11 +144,13 @@ func searchAggregationsToLibregraph(in []*searchsvc.AggregationResult, defs []li
|
||||
out = append(out, agg)
|
||||
continue
|
||||
}
|
||||
ranges := libregraphRangesToSearch(def.BucketDefinition.GetRanges())
|
||||
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()),
|
||||
AggregationFilterToken: libregraph.PtrString(aggregationFilterToken(b.GetKey(), ranges)),
|
||||
LibreGraphSubAggregations: searchAggregationsToLibregraph(b.GetSubAggregations(), def.LibreGraphSubAggregations),
|
||||
})
|
||||
}
|
||||
@@ -137,6 +159,17 @@ func searchAggregationsToLibregraph(in []*searchsvc.AggregationResult, defs []li
|
||||
return out
|
||||
}
|
||||
|
||||
// aggregationFilterToken issues a range token for the bucket of a range
|
||||
// aggregation, found by its key, and a terms token otherwise.
|
||||
func aggregationFilterToken(key string, ranges []*searchsvc.BucketRange) string {
|
||||
for _, r := range ranges {
|
||||
if aggregation.RangeKey(r) == key {
|
||||
return filtertoken.EncodeRangeToken(r.GetFrom(), r.GetTo())
|
||||
}
|
||||
}
|
||||
return filtertoken.EncodeTermsToken(key)
|
||||
}
|
||||
|
||||
func searchResourceID(id *searchmsg.ResourceID) string {
|
||||
return storagespace.FormatResourceID(&storageprovider.ResourceId{
|
||||
StorageId: id.GetStorageId(),
|
||||
|
||||
@@ -94,6 +94,7 @@ type searchAggregationJSON struct {
|
||||
Buckets []struct {
|
||||
Key string `json:"key"`
|
||||
Count int64 `json:"count"`
|
||||
Token string `json:"aggregationFilterToken"`
|
||||
SubAggregations []searchAggregationJSON `json:"@libre.graph.subAggregations"`
|
||||
} `json:"buckets"`
|
||||
}
|
||||
@@ -314,6 +315,9 @@ var _ = ginkgo.Describe("SearchQuery", func() {
|
||||
`"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"}]}}]`),
|
||||
// the token grammar is pinned in the filtertoken package
|
||||
ginkgo.Entry("an aggregation filter that is no server-issued token", `"aggregationFilters": ["audio.artist:\"not a token\""]`),
|
||||
ginkgo.Entry("an aggregation filter on an internal field", `"aggregationFilters": ["favorites:\"ǂǂ5361786f6e\""]`),
|
||||
)
|
||||
|
||||
ginkgo.It("translates the bucket definition for the search service", func() {
|
||||
@@ -338,10 +342,12 @@ var _ = ginkgo.Describe("SearchQuery", func() {
|
||||
Field: indexed,
|
||||
Buckets: []*searchsvc.Bucket{{Key: "audio/mpeg", Count: 3}},
|
||||
})
|
||||
rr := postSearchQuery(g, searchQueryBody(fmt.Sprintf(`"aggregations": [{"field": %q}]`, requested)))
|
||||
rr := postSearchQuery(g, searchQueryBody(fmt.Sprintf(`"aggregations": [{"field": %q}], "aggregationFilters": [%q]`, requested, requested+`:"ǂǂ617564696f2f6d706567"`)))
|
||||
Expect(rr.Code).To(Equal(http.StatusOK), rr.Body.String())
|
||||
|
||||
Expect(captured().GetAggregations()[0].GetField()).To(Equal(indexed))
|
||||
Expect(captured().GetAggregationFilters()[0].GetField()).To(Equal(indexed))
|
||||
Expect(captured().GetAggregationFilters()[0].GetTerms()).To(Equal([]string{"audio/mpeg"}))
|
||||
Expect(hitsContainer(rr).Aggregations[0].Field).To(Equal(requested))
|
||||
},
|
||||
ginkgo.Entry("mimeType", "mimeType", "MimeType"),
|
||||
@@ -407,7 +413,6 @@ var _ = ginkgo.Describe("SearchQuery", func() {
|
||||
Expect(rr.Body.String()).To(ContainSubstring(property))
|
||||
},
|
||||
ginkgo.Entry("sortProperties", `"sortProperties": [{"name": "name"}]`, "sortProperties"),
|
||||
ginkgo.Entry("aggregationFilters", `"aggregationFilters": ["audio.artist:\"ǂǂ5361786f6e\""]`, "aggregationFilters"),
|
||||
ginkgo.Entry("a geohash aggregation",
|
||||
`"aggregations": [{"field": "location", "@libre.graph.geohashDefinition": {"precision": 5}}]`, "geohashDefinition"),
|
||||
ginkgo.Entry("a nested geohash aggregation",
|
||||
@@ -450,4 +455,66 @@ var _ = ginkgo.Describe("SearchQuery", func() {
|
||||
ginkgo.Entry("an error that is no service error", errors.New("connection refused"),
|
||||
http.StatusInternalServerError, "generalException", "connection refused"),
|
||||
)
|
||||
|
||||
ginkgo.It("issues terms and range tokens by the definition at the position of the result", func() {
|
||||
// the range aggregation and the metric share a field: the result at
|
||||
// position 0 is the range aggregation, whatever follows on that field
|
||||
g, _ := graphWithAggregations(
|
||||
&searchsvc.AggregationResult{Field: "audio.year", Buckets: []*searchsvc.Bucket{
|
||||
{Key: "..1980", Count: 2},
|
||||
{Key: "1980..1990", Count: 1},
|
||||
{Key: "1990..", Count: 4},
|
||||
}},
|
||||
&searchsvc.AggregationResult{Field: "audio.artist", Buckets: []*searchsvc.Bucket{
|
||||
{Key: "Saxon", Count: 2, SubAggregations: []*searchsvc.AggregationResult{
|
||||
{Field: "audio.album", Buckets: []*searchsvc.Bucket{{Key: "Wheels of Steel", Count: 2}}},
|
||||
{Field: "audio.year", Buckets: []*searchsvc.Bucket{{Key: "1980..", Count: 2}}},
|
||||
}},
|
||||
}},
|
||||
&searchsvc.AggregationResult{Field: "audio.year", Metric: &searchsvc.Metric{Kind: searchsvc.MetricKind_METRIC_KIND_MAX}},
|
||||
)
|
||||
rr := postSearchQuery(g, searchQueryBody(`"aggregations": [
|
||||
{"field": "audio.year", "bucketDefinition": {"sortBy": "keyAsNumber", "ranges": [{"to": "1980"}, {"from": "1980", "to": "1990"}, {"from": "1990"}]}},
|
||||
{"field": "audio.artist", "@libre.graph.subAggregations": [
|
||||
{"field": "audio.album"},
|
||||
{"field": "audio.year", "bucketDefinition": {"sortBy": "keyAsNumber", "ranges": [{"from": "1980"}]}}
|
||||
]},
|
||||
{"field": "audio.year", "@libre.graph.metricDefinition": {"kind": "max"}}
|
||||
]`))
|
||||
Expect(rr.Code).To(Equal(http.StatusOK), rr.Body.String())
|
||||
|
||||
aggs := hitsContainer(rr).Aggregations
|
||||
Expect(aggs).To(HaveLen(3))
|
||||
|
||||
years := aggs[0].Buckets
|
||||
Expect(years).To(HaveLen(3))
|
||||
Expect(years[0].Token).To(Equal("range(min, 1980)"))
|
||||
Expect(years[1].Token).To(Equal("range(1980, 1990)"))
|
||||
Expect(years[2].Token).To(Equal(`range(1990, max, to="le")`))
|
||||
|
||||
saxon := aggs[1].Buckets[0]
|
||||
Expect(saxon.Token).To(Equal(`"ǂǂ5361786f6e"`))
|
||||
Expect(saxon.SubAggregations).To(HaveLen(2))
|
||||
Expect(saxon.SubAggregations[0].Buckets[0].Token).To(Equal(`"ǂǂ576865656c73206f6620537465656c"`))
|
||||
Expect(saxon.SubAggregations[1].Buckets[0].Token).To(Equal(`range(1980, max, to="le")`))
|
||||
|
||||
Expect(aggs[2].Metric).ToNot(BeNil())
|
||||
Expect(aggs[2].Buckets).To(BeEmpty())
|
||||
})
|
||||
|
||||
ginkgo.DescribeTable("hands the buckets of an aggregation filter to the search service",
|
||||
func(filter string, want *searchsvc.AggregationFilter) {
|
||||
g, captured := graphWithAggregations()
|
||||
rr := postSearchQuery(g, searchQueryBody(fmt.Sprintf(`"aggregationFilters": [%q]`, filter)))
|
||||
Expect(rr.Code).To(Equal(http.StatusOK), rr.Body.String())
|
||||
|
||||
Expect(captured().GetAggregationFilters()).To(BeComparableTo([]*searchsvc.AggregationFilter{want}, protocmp.Transform()))
|
||||
},
|
||||
ginkgo.Entry("several terms", `audio.artist:or("ǂǂ5361786f6e", "ǂǂ49726f6e204d616964656e")`,
|
||||
&searchsvc.AggregationFilter{Field: "audio.artist", Terms: []string{"Saxon", "Iron Maiden"}}),
|
||||
ginkgo.Entry("several ranges", `audio.year:or(range(min, 1980),range(2010, max, to="le"))`,
|
||||
&searchsvc.AggregationFilter{Field: "audio.year", Ranges: []*searchsvc.BucketRange{{To: "1980"}, {From: "2010"}}}),
|
||||
ginkgo.Entry("a date range", `photo.takenDateTime:range(2018-08-11T00:00:00Z, 2018-08-12T00:00:00Z)`,
|
||||
&searchsvc.AggregationFilter{Field: "photo.takenDateTime", Ranges: []*searchsvc.BucketRange{{From: "2018-08-11T00:00:00Z", To: "2018-08-12T00:00:00Z"}}}),
|
||||
)
|
||||
})
|
||||
@@ -0,0 +1,78 @@
|
||||
package aggregation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
searchsvc "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/services/search/v0"
|
||||
"github.com/opencloud-eu/opencloud/services/search/pkg/mapping"
|
||||
)
|
||||
|
||||
// Buckets are the buckets an aggregation filter selects, read as the type of
|
||||
// its field.
|
||||
type Buckets struct {
|
||||
Terms []Term
|
||||
Ranges Ranges
|
||||
}
|
||||
|
||||
// Term is a bucket key of a terms aggregation. A numeric or bool field also
|
||||
// carries the key as its value.
|
||||
type Term struct {
|
||||
Key string
|
||||
Number *float64
|
||||
Bool *bool
|
||||
}
|
||||
|
||||
// ParseFilter reads the buckets of a filter on a field of the given mapping
|
||||
// type and rejects what no aggregation on that field could have answered.
|
||||
func ParseFilter(f *searchsvc.AggregationFilter, fieldType string) (Buckets, error) {
|
||||
field := f.GetField()
|
||||
if fieldType == "" {
|
||||
return Buckets{}, fmt.Errorf("unknown aggregation filter field %q", field)
|
||||
}
|
||||
if len(f.GetTerms())+len(f.GetRanges()) == 0 {
|
||||
return Buckets{}, fmt.Errorf("aggregation filter on field %q selects no bucket", field)
|
||||
}
|
||||
ranges, err := ParseRanges(field, f.GetRanges())
|
||||
if err != nil {
|
||||
return Buckets{}, err
|
||||
}
|
||||
if err := validateRangeField(field, fieldType, ranges); err != nil {
|
||||
return Buckets{}, err
|
||||
}
|
||||
buckets := Buckets{Ranges: ranges}
|
||||
if len(f.GetTerms()) == 0 {
|
||||
return buckets, nil
|
||||
}
|
||||
if err := validateTermsField(field, fieldType); err != nil {
|
||||
return Buckets{}, err
|
||||
}
|
||||
for _, key := range f.GetTerms() {
|
||||
term, err := parseTerm(fieldType, key)
|
||||
if err != nil {
|
||||
return Buckets{}, fmt.Errorf("invalid bucket key on field %q: %w", field, err)
|
||||
}
|
||||
buckets.Terms = append(buckets.Terms, term)
|
||||
}
|
||||
return buckets, nil
|
||||
}
|
||||
|
||||
func parseTerm(fieldType, key string) (Term, error) {
|
||||
term := Term{Key: key}
|
||||
switch fieldType {
|
||||
case mapping.TypeNumeric:
|
||||
// the key as a bucket spells it, no other spelling of the number
|
||||
v, err := parseNumber(key)
|
||||
if err != nil || v == nil || NumberKey(*v) != key {
|
||||
return Term{}, fmt.Errorf("%q is no bucket key of a number", key)
|
||||
}
|
||||
term.Number = v
|
||||
case mapping.TypeBool:
|
||||
v, err := strconv.ParseBool(key)
|
||||
if err != nil || key != BoolKey(v) {
|
||||
return Term{}, fmt.Errorf("%q is neither true nor false", key)
|
||||
}
|
||||
term.Bool = &v
|
||||
}
|
||||
return term, nil
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package aggregation_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/opencloud-eu/opencloud/pkg/conversions"
|
||||
searchsvc "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/services/search/v0"
|
||||
"github.com/opencloud-eu/opencloud/services/search/pkg/aggregation"
|
||||
)
|
||||
|
||||
var _ = Describe("ParseFilter", func() {
|
||||
parse := func(f *searchsvc.AggregationFilter) (aggregation.Buckets, error) {
|
||||
return aggregation.ParseFilter(f, fieldTypes(f.GetField()))
|
||||
}
|
||||
|
||||
DescribeTable("reads a key in the type of the field",
|
||||
func(field string, keys []string, want []aggregation.Term) {
|
||||
got, err := parse(&searchsvc.AggregationFilter{Field: field, Terms: keys})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(got.Terms).To(Equal(want))
|
||||
},
|
||||
Entry("a keyword as it is", "audio.artist", []string{`Wh*t? "Live"`, "true", "1980"},
|
||||
[]aggregation.Term{{Key: `Wh*t? "Live"`}, {Key: "true"}, {Key: "1980"}}),
|
||||
Entry("a number", "audio.year", []string{"1980", "4.5"},
|
||||
[]aggregation.Term{{Key: "1980", Number: conversions.ToPointer(1980.0)}, {Key: "4.5", Number: conversions.ToPointer(4.5)}}),
|
||||
Entry("a bool", "audio.hasDrm", []string{"true", "false"},
|
||||
[]aggregation.Term{{Key: "true", Bool: conversions.ToPointer(true)}, {Key: "false", Bool: conversions.ToPointer(false)}}),
|
||||
)
|
||||
|
||||
It("reads the ranges of a filter", func() {
|
||||
got, err := parse(&searchsvc.AggregationFilter{Field: "audio.year", Ranges: []*searchsvc.BucketRange{{To: "1980"}, {From: "2010"}}})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(got.Terms).To(BeEmpty())
|
||||
Expect(got.Ranges.Numeric).To(HaveLen(2))
|
||||
})
|
||||
|
||||
DescribeTable("rejects what no aggregation on the field could have answered",
|
||||
func(f *searchsvc.AggregationFilter) {
|
||||
_, err := parse(f)
|
||||
Expect(err).To(HaveOccurred())
|
||||
},
|
||||
Entry("an unknown field", &searchsvc.AggregationFilter{Field: "audio.nonexistent", Terms: []string{"Saxon"}}),
|
||||
Entry("no bucket at all", &searchsvc.AggregationFilter{Field: "audio.artist"}),
|
||||
Entry("a key that is no number on a numeric field", &searchsvc.AggregationFilter{Field: "audio.year", Terms: []string{"nineteen-eighty"}}),
|
||||
Entry("an empty key on a numeric field", &searchsvc.AggregationFilter{Field: "audio.year", Terms: []string{""}}),
|
||||
Entry("a number spelled otherwise than its bucket key", &searchsvc.AggregationFilter{Field: "audio.year", Terms: []string{"1980.0"}}),
|
||||
Entry("a number in scientific notation", &searchsvc.AggregationFilter{Field: "audio.year", Terms: []string{"1e3"}}),
|
||||
Entry("a number with a plus sign", &searchsvc.AggregationFilter{Field: "audio.year", Terms: []string{"+1980"}}),
|
||||
Entry("the bleve spelling of a bool", &searchsvc.AggregationFilter{Field: "audio.hasDrm", Terms: []string{"T"}}),
|
||||
Entry("another spelling of a bool", &searchsvc.AggregationFilter{Field: "audio.hasDrm", Terms: []string{"1"}}),
|
||||
Entry("a key on a date field", &searchsvc.AggregationFilter{Field: "photo.takenDateTime", Terms: []string{"2018-08-11T00:00:00Z"}}),
|
||||
Entry("a key on full text", &searchsvc.AggregationFilter{Field: "Content", Terms: []string{"fox"}}),
|
||||
Entry("a numeric range on a keyword", &searchsvc.AggregationFilter{Field: "audio.artist", Ranges: []*searchsvc.BucketRange{{From: "1980"}}}),
|
||||
Entry("a date range on a number", &searchsvc.AggregationFilter{Field: "audio.year", Ranges: []*searchsvc.BucketRange{{From: "2018-08-11T00:00:00Z"}}}),
|
||||
)
|
||||
})
|
||||
@@ -65,3 +65,13 @@ func validateRangeField(field, fieldType string, ranges Ranges) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateFilters holds filters to the same rules as ValidateOptions.
|
||||
func ValidateFilters(filters []*searchsvc.AggregationFilter, fieldType func(string) string) error {
|
||||
for _, f := range filters {
|
||||
if _, err := ParseFilter(f, fieldType(f.GetField())); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -61,6 +61,12 @@ func (b *Backend) Search(ctx context.Context, sir *searchService.SearchIndexRequ
|
||||
createdQuery,
|
||||
)
|
||||
|
||||
filters, err := aggregationFilterQueries(sir.GetAggregationFilters())
|
||||
if err != nil {
|
||||
return nil, errtypes.BadRequest(err.Error())
|
||||
}
|
||||
q.Conjuncts = append(q.Conjuncts, filters...)
|
||||
|
||||
if sir.Ref != nil {
|
||||
q.Conjuncts = append(
|
||||
q.Conjuncts,
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package bleve
|
||||
|
||||
import (
|
||||
"github.com/blevesearch/bleve/v2"
|
||||
"github.com/blevesearch/bleve/v2/search/query"
|
||||
|
||||
searchService "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/services/search/v0"
|
||||
"github.com/opencloud-eu/opencloud/services/search/pkg/aggregation"
|
||||
"github.com/opencloud-eu/opencloud/services/search/pkg/search"
|
||||
)
|
||||
|
||||
// fieldQuery is a bleve query on one field.
|
||||
type fieldQuery interface {
|
||||
query.Query
|
||||
SetField(string)
|
||||
SetBoost(float64)
|
||||
}
|
||||
|
||||
// aggregationFilterQueries builds one query per filter, matching any of its
|
||||
// buckets the way the aggregation counted them: a key is the exact value of
|
||||
// the field, a range is from-inclusive and to-exclusive. A filter narrows and
|
||||
// does not rank: bleve has no filter context, a boost of zero keeps the
|
||||
// filters out of the score.
|
||||
func aggregationFilterQueries(filters []*searchService.AggregationFilter) ([]query.Query, error) {
|
||||
inclusive, exclusive := true, false
|
||||
out := make([]query.Query, 0, len(filters))
|
||||
for _, f := range filters {
|
||||
buckets, err := aggregation.ParseFilter(f, search.FieldType(f.GetField()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var selected []query.Query
|
||||
add := func(q fieldQuery) {
|
||||
q.SetField(f.GetField())
|
||||
q.SetBoost(0)
|
||||
selected = append(selected, q)
|
||||
}
|
||||
for _, term := range buckets.Terms {
|
||||
switch {
|
||||
case term.Number != nil:
|
||||
add(bleve.NewNumericRangeInclusiveQuery(term.Number, term.Number, &inclusive, &inclusive))
|
||||
case term.Bool != nil:
|
||||
add(bleve.NewBoolFieldQuery(*term.Bool))
|
||||
default:
|
||||
add(bleve.NewTermQuery(term.Key))
|
||||
}
|
||||
}
|
||||
for _, r := range buckets.Ranges.Numeric {
|
||||
add(bleve.NewNumericRangeInclusiveQuery(r.From, r.To, &inclusive, &exclusive))
|
||||
}
|
||||
for _, r := range buckets.Ranges.Dates {
|
||||
add(bleve.NewDateRangeInclusiveQuery(r.From, r.To, &inclusive, &exclusive))
|
||||
}
|
||||
out = append(out, bleve.NewDisjunctionQuery(selected...))
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
@@ -84,6 +84,12 @@ func (b *Backend) Search(ctx context.Context, sir *searchService.SearchIndexRequ
|
||||
return nil, fmt.Errorf("failed to convert KQL query to OpenSearch bool query: %w", err)
|
||||
}
|
||||
|
||||
filters, err := aggregationFilterQueries(sir.GetAggregationFilters())
|
||||
if err != nil {
|
||||
return nil, errtypes.BadRequest(err.Error())
|
||||
}
|
||||
boolQuery.Filter(filters...)
|
||||
|
||||
// filter out deleted resources
|
||||
boolQuery.Filter(
|
||||
osu.NewTermQuery[bool]("Deleted").Value(false),
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package opensearch
|
||||
|
||||
import (
|
||||
searchService "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/services/search/v0"
|
||||
"github.com/opencloud-eu/opencloud/services/search/pkg/aggregation"
|
||||
"github.com/opencloud-eu/opencloud/services/search/pkg/opensearch/internal/osu"
|
||||
"github.com/opencloud-eu/opencloud/services/search/pkg/search"
|
||||
)
|
||||
|
||||
// Keys and bounds stay strings, OpenSearch reads them as the type of the field.
|
||||
func aggregationFilterQueries(filters []*searchService.AggregationFilter) ([]osu.Builder, error) {
|
||||
out := make([]osu.Builder, 0, len(filters))
|
||||
for _, f := range filters {
|
||||
if _, err := aggregation.ParseFilter(f, search.FieldType(f.GetField())); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// a bool query of only should clauses needs one of them to match
|
||||
selected := osu.NewBoolQuery()
|
||||
for _, key := range f.GetTerms() {
|
||||
selected.Should(osu.NewTermQuery[string](f.GetField()).Value(key))
|
||||
}
|
||||
for _, r := range f.GetRanges() {
|
||||
q := osu.NewRangeQuery[string](f.GetField())
|
||||
if r.GetFrom() != "" {
|
||||
q = q.Gte(r.GetFrom())
|
||||
}
|
||||
if r.GetTo() != "" {
|
||||
q = q.Lt(r.GetTo())
|
||||
}
|
||||
selected.Should(q)
|
||||
}
|
||||
out = append(out, selected)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
@@ -722,8 +722,8 @@ Fixtures:
|
||||
|
||||
Fixtures:
|
||||
|
||||
- `a.mp3`, MimeType = audio/mpeg
|
||||
- `b.mp3`, MimeType = audio/mpeg
|
||||
- `a.mp3`, MimeType = audio/mpeg, Tags = live, remaster
|
||||
- `b.mp3`, MimeType = audio/mpeg, Tags = live
|
||||
- `c.mp3`, MimeType = audio/mpeg
|
||||
- `d.mp3`, MimeType = audio/mpeg
|
||||
- `e.mp3`, MimeType = audio/mpeg
|
||||
@@ -733,6 +733,7 @@ Fixtures:
|
||||
- `b.jpg`, MimeType = image/jpeg
|
||||
- `c.jpg`, MimeType = image/jpeg
|
||||
- `d.jpg`, MimeType = image/jpeg
|
||||
- `live.txt`
|
||||
|
||||
| Case | Query | expected | bleve | OpenSearch | same? |
|
||||
|---|---|---|---|---|---|
|
||||
@@ -754,12 +755,27 @@ Fixtures:
|
||||
| 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-22 | `mediatype:audio` reads `filtered to the Saxon bucket, as many matches as the bucket counted` | a.mp3, audio.artist Saxon=2, b.mp3 | a.mp3, audio.artist Saxon=2, b.mp3 | a.mp3, audio.artist Saxon=2, b.mp3 | ✅ |
|
||||
| AGG-23 | `mediatype:audio` reads `filtered to saxon, a bucket key matches case-sensitively` | no match | no match | no match | ✅ |
|
||||
| AGG-24 | `mediatype:audio` reads `filtered to the Saxon or the Motörhead bucket` | a.mp3, b.mp3, c.mp3, d.mp3, e.mp3 | a.mp3, b.mp3, c.mp3, d.mp3, e.mp3 | a.mp3, b.mp3, c.mp3, d.mp3, e.mp3 | ✅ |
|
||||
| AGG-25 | `mediatype:audio` reads `filtered to audio.year 1980-1990` | c.mp3 | c.mp3 | c.mp3 | ✅ |
|
||||
| AGG-26 | `mediatype:audio` reads `filtered to audio.year 1982-1999, from-inclusive and to-exclusive` | c.mp3 | c.mp3 | c.mp3 | ✅ |
|
||||
| AGG-27 | `mediatype:audio` reads `filtered to audio.year 2000-` | e.mp3, f.mp3, g.mp3 | e.mp3, f.mp3, g.mp3 | e.mp3, f.mp3, g.mp3 | ✅ |
|
||||
| AGG-28 | `mediatype:audio` reads `filtered to audio.year -1980 or 2005-` | a.mp3, b.mp3, f.mp3, g.mp3 | a.mp3, b.mp3, f.mp3, g.mp3 | a.mp3, b.mp3, f.mp3, g.mp3 | ✅ |
|
||||
| AGG-29 | `mediatype:image` reads `filtered to photo.takenDateTime on 2018-08-11` | a.jpg, b.jpg | a.jpg, b.jpg | a.jpg, b.jpg | ✅ |
|
||||
| AGG-30 | `mediatype:audio` reads `filtered to the Motörhead bucket and to audio.year 1990-2010` | d.mp3, e.mp3 | d.mp3, e.mp3 | d.mp3, e.mp3 | ✅ |
|
||||
| AGG-31 | `*` reads `filtered to the bucket Wh*t? "Live", a key is data` | live.txt | live.txt | live.txt | ✅ |
|
||||
| AGG-32 | `mediatype:audio` reads `filtered to Sax*, a key is no wildcard pattern` | no match | no match | no match | ✅ |
|
||||
| AGG-33 | `mediatype:audio` reads `filtered to a malformed range` | bad request | bad request | bad request | ✅ |
|
||||
| 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-36 | `mediatype:audio` reads `filtered to the audio.year bucket 1982` | c.mp3 | c.mp3 | c.mp3 | ✅ |
|
||||
| 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-38 | `mediatype:audio` reads `filtered to the audio.hasDrm bucket true` | a.mp3 | a.mp3 | a.mp3 | ✅ |
|
||||
| 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 | ✅ |
|
||||
| AGG-45 | `mediatype:audio` reads `filtered to the Tags bucket live, a filter narrows and does not rank` | a.mp3, b.mp3 | a.mp3, b.mp3 | a.mp3, b.mp3 | ✅ |
|
||||
|
||||
### cardinality
|
||||
|
||||
|
||||
@@ -21,9 +21,12 @@ import (
|
||||
// answer is rendered to strings (one per non-empty bucket or metric), so the
|
||||
// matrix machinery can carry it like any query answer.
|
||||
type aggCase struct {
|
||||
id int
|
||||
query string
|
||||
aggs []*searchService.AggregationOption
|
||||
id int
|
||||
query string
|
||||
aggs []*searchService.AggregationOption
|
||||
// filters narrow the matches to buckets; the answer then starts with the
|
||||
// names of the matches
|
||||
filters []*searchService.AggregationFilter
|
||||
reads string
|
||||
want []string
|
||||
wantCount *int
|
||||
@@ -80,8 +83,8 @@ func withDrm(r search.Resource, drm bool) search.Resource {
|
||||
func aggregationFixtures() []search.Resource {
|
||||
return []search.Resource{
|
||||
// years: 1971, 1975, 1982, 1999, 2001, 2005, 2009
|
||||
withDrm(song("a.mp3", "Saxon", "Wheels of Steel", 1971), true),
|
||||
withDrm(song("b.mp3", "Saxon", "Wheels of Steel", 1975), false),
|
||||
withDrm(song("a.mp3", "Saxon", "Wheels of Steel", 1971, withTags("live", "remaster")), true),
|
||||
withDrm(song("b.mp3", "Saxon", "Wheels of Steel", 1975, withTags("live")), false),
|
||||
song("c.mp3", "Motörhead", "Bomber", 1982),
|
||||
song("d.mp3", "Motörhead", "Bomber", 1999),
|
||||
song("e.mp3", "Motörhead", "Ace of Spades", 2001),
|
||||
@@ -91,6 +94,8 @@ func aggregationFixtures() []search.Resource {
|
||||
photo("b.jpg", "2018-08-11T19:42:00Z"),
|
||||
photo("c.jpg", "2018-09-01T12:00:00Z"),
|
||||
photo("d.jpg", "2021-08-11T08:00:00Z"),
|
||||
// no audio mime type: stays out of the mediatype:audio cases
|
||||
fixtureDoc("live.txt", withAudio(&libregraph.Audio{Artist: libregraph.PtrString(`Wh*t? "Live"`)})),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,6 +106,12 @@ func aggregationCases() []aggCase {
|
||||
metric := func(field string, kind searchService.MetricKind) *searchService.AggregationOption {
|
||||
return &searchService.AggregationOption{Field: field, MetricDefinition: &searchService.MetricDefinition{Kind: kind}}
|
||||
}
|
||||
terms := func(field string, keys ...string) *searchService.AggregationFilter {
|
||||
return &searchService.AggregationFilter{Field: field, Terms: keys}
|
||||
}
|
||||
within := func(field string, rs ...*searchService.BucketRange) *searchService.AggregationFilter {
|
||||
return &searchService.AggregationFilter{Field: field, Ranges: rs}
|
||||
}
|
||||
|
||||
return []aggCase{
|
||||
{id: 1, query: "mediatype:audio", reads: "term buckets on audio.artist",
|
||||
@@ -256,6 +267,44 @@ func aggregationCases() []aggCase {
|
||||
metric("audio.year", searchService.MetricKind_METRIC_KIND_AVG),
|
||||
},
|
||||
want: []string{"audio.year sum none", "audio.year min none", "audio.year avg none"}},
|
||||
{id: 22, query: "mediatype:audio", reads: "filtered to the Saxon bucket, as many matches as the bucket counted",
|
||||
filters: []*searchService.AggregationFilter{terms("audio.artist", "Saxon")},
|
||||
aggs: []*searchService.AggregationOption{{Field: "audio.artist"}},
|
||||
want: []string{"a.mp3", "b.mp3", "audio.artist Saxon=2"}},
|
||||
{id: 23, query: "mediatype:audio", reads: "filtered to saxon, a bucket key matches case-sensitively",
|
||||
filters: []*searchService.AggregationFilter{terms("audio.artist", "saxon")}},
|
||||
{id: 24, query: "mediatype:audio", reads: "filtered to the Saxon or the Motörhead bucket",
|
||||
filters: []*searchService.AggregationFilter{terms("audio.artist", "Saxon", "Motörhead")},
|
||||
want: []string{"a.mp3", "b.mp3", "c.mp3", "d.mp3", "e.mp3"}},
|
||||
{id: 25, query: "mediatype:audio", reads: "filtered to audio.year 1980-1990",
|
||||
filters: []*searchService.AggregationFilter{within("audio.year", &searchService.BucketRange{From: "1980", To: "1990"})},
|
||||
want: []string{"c.mp3"}},
|
||||
{id: 26, query: "mediatype:audio", reads: "filtered to audio.year 1982-1999, from-inclusive and to-exclusive",
|
||||
filters: []*searchService.AggregationFilter{within("audio.year", &searchService.BucketRange{From: "1982", To: "1999"})},
|
||||
want: []string{"c.mp3"}},
|
||||
{id: 27, query: "mediatype:audio", reads: "filtered to audio.year 2000-",
|
||||
filters: []*searchService.AggregationFilter{within("audio.year", &searchService.BucketRange{From: "2000"})},
|
||||
want: []string{"e.mp3", "f.mp3", "g.mp3"}},
|
||||
{id: 28, query: "mediatype:audio", reads: "filtered to audio.year -1980 or 2005-",
|
||||
filters: []*searchService.AggregationFilter{within("audio.year", &searchService.BucketRange{To: "1980"}, &searchService.BucketRange{From: "2005"})},
|
||||
want: []string{"a.mp3", "b.mp3", "f.mp3", "g.mp3"}},
|
||||
{id: 29, query: "mediatype:image", reads: "filtered to photo.takenDateTime on 2018-08-11",
|
||||
filters: []*searchService.AggregationFilter{within("photo.takenDateTime", &searchService.BucketRange{From: "2018-08-11T00:00:00Z", To: "2018-08-12T00:00:00Z"})},
|
||||
want: []string{"a.jpg", "b.jpg"}},
|
||||
{id: 30, query: "mediatype:audio", reads: "filtered to the Motörhead bucket and to audio.year 1990-2010",
|
||||
filters: []*searchService.AggregationFilter{
|
||||
terms("audio.artist", "Motörhead"),
|
||||
within("audio.year", &searchService.BucketRange{From: "1990", To: "2010"}),
|
||||
},
|
||||
want: []string{"d.mp3", "e.mp3"}},
|
||||
{id: 31, query: "*", reads: `filtered to the bucket Wh*t? "Live", a key is data`,
|
||||
filters: []*searchService.AggregationFilter{terms("audio.artist", `Wh*t? "Live"`)},
|
||||
want: []string{"live.txt"}},
|
||||
{id: 32, query: "mediatype:audio", reads: "filtered to Sax*, a key is no wildcard pattern",
|
||||
filters: []*searchService.AggregationFilter{terms("audio.artist", "Sax*")}},
|
||||
{id: 33, query: "mediatype:audio", reads: "filtered to a malformed range",
|
||||
filters: []*searchService.AggregationFilter{within("audio.year", &searchService.BucketRange{From: "1980 OR Name:*", To: "1990"})},
|
||||
wantBadRequest: true},
|
||||
{id: 34, query: "mediatype:audio", reads: "term buckets on the numeric audio.year",
|
||||
aggs: []*searchService.AggregationOption{{Field: "audio.year"}},
|
||||
want: []string{
|
||||
@@ -269,9 +318,15 @@ func aggregationCases() []aggCase {
|
||||
"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: 36, query: "mediatype:audio", reads: "filtered to the audio.year bucket 1982",
|
||||
filters: []*searchService.AggregationFilter{terms("audio.year", "1982")},
|
||||
want: []string{"c.mp3"}},
|
||||
{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: 38, query: "mediatype:audio", reads: "filtered to the audio.hasDrm bucket true",
|
||||
filters: []*searchService.AggregationFilter{terms("audio.hasDrm", "true")},
|
||||
want: []string{"a.mp3"}},
|
||||
{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",
|
||||
@@ -279,6 +334,9 @@ func aggregationCases() []aggCase {
|
||||
{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"}},
|
||||
{id: 45, query: "mediatype:audio", reads: "filtered to the Tags bucket live, a filter narrows and does not rank",
|
||||
filters: []*searchService.AggregationFilter{terms("Tags", "live")}, listMatches: true,
|
||||
want: []string{"a.mp3", "b.mp3"}},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,15 +448,16 @@ var _ = Describe("Aggregations", func() {
|
||||
}
|
||||
|
||||
request := &searchService.SearchIndexRequest{
|
||||
Query: c.query,
|
||||
Aggregations: c.aggs,
|
||||
Query: c.query,
|
||||
Aggregations: c.aggs,
|
||||
AggregationFilters: c.filters,
|
||||
}
|
||||
if c.pageSize != 0 {
|
||||
request.PageSize = conversions.ToPointer(c.pageSize)
|
||||
}
|
||||
resp, err := e.backend.Search(context.Background(), request)
|
||||
answer := renderAggregations(resp, err)
|
||||
if err == nil && c.listMatches {
|
||||
if err == nil && (len(c.filters) > 0 || c.listMatches) {
|
||||
names := make([]string, 0, len(resp.Matches))
|
||||
for _, m := range resp.Matches {
|
||||
names = append(names, m.GetEntity().GetName())
|
||||
|
||||
@@ -106,6 +106,9 @@ func (s *Service) Search(ctx context.Context, req *searchsvc.SearchRequest) (*se
|
||||
if err := aggregation.ValidateOptions(req.GetAggregations(), AggregatableFieldType); err != nil {
|
||||
return nil, errtypes.BadRequest(err.Error())
|
||||
}
|
||||
if err := aggregation.ValidateFilters(req.GetAggregationFilters(), AggregatableFieldType); err != nil {
|
||||
return nil, errtypes.BadRequest(err.Error())
|
||||
}
|
||||
s.logger.Debug().Str("query", req.Query).Msg("performing a search")
|
||||
|
||||
// collect metrics
|
||||
@@ -463,8 +466,9 @@ func (s *Service) searchIndex(ctx context.Context, req *searchsvc.SearchRequest,
|
||||
}
|
||||
|
||||
searchRequest := &searchsvc.SearchIndexRequest{
|
||||
Query: req.Query,
|
||||
Aggregations: req.GetAggregations(),
|
||||
Query: req.Query,
|
||||
Aggregations: req.GetAggregations(),
|
||||
AggregationFilters: req.GetAggregationFilters(),
|
||||
Ref: &searchmsg.Reference{
|
||||
ResourceId: searchRootID,
|
||||
Path: searchPathPrefix,
|
||||
|
||||
@@ -289,6 +289,8 @@ var _ = Describe("Searchprovider", func() {
|
||||
// the rules are pinned in the aggregation package
|
||||
Entry("an aggregation on an internal field", &searchsvc.SearchRequest{Query: "foo",
|
||||
Aggregations: []*searchsvc.AggregationOption{{Field: "Favorites"}}}),
|
||||
Entry("a filter on an internal field", &searchsvc.SearchRequest{Query: "foo",
|
||||
AggregationFilters: []*searchsvc.AggregationFilter{{Field: "Favorites", Terms: []string{"user-a"}}}}),
|
||||
)
|
||||
|
||||
// two personal spaces, the engine answers each by its space id
|
||||
|
||||
Reference in new issue
Block a user