diff --git a/Makefile b/Makefile index 0635429d86..0c29bbe836 100644 --- a/Makefile +++ b/Makefile @@ -181,7 +181,7 @@ go-coverage: .PHONY: protobuf protobuf: - @for mod in $(OCIS_MODULES); do \ + @for mod in ./services/thumbnails ./services/store ./services/settings; do \ echo -n "% protobuf $$mod: "; $(MAKE) --no-print-directory -C $$mod protobuf || exit 1; \ done diff --git a/changelog/unreleased/add-total-to-search-results.md b/changelog/unreleased/add-total-to-search-results.md new file mode 100644 index 0000000000..043e8220c8 --- /dev/null +++ b/changelog/unreleased/add-total-to-search-results.md @@ -0,0 +1,6 @@ +Enhancement: Add number of total matches to the search result + +The search service now returns the number of total matches alongside the +results. + +https://github.com/owncloud/ocis/issues/4189 diff --git a/protogen/gen/ocis/services/search/v0/search.pb.go b/protogen/gen/ocis/services/search/v0/search.pb.go index 69aec7639d..5a43c9fbe5 100644 --- a/protogen/gen/ocis/services/search/v0/search.pb.go +++ b/protogen/gen/ocis/services/search/v0/search.pb.go @@ -99,6 +99,7 @@ type SearchResponse struct { // Token to retrieve the next page of results, or empty if there are no // more results in the list NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` + TotalMatches int32 `protobuf:"varint,3,opt,name=total_matches,json=totalMatches,proto3" json:"total_matches,omitempty"` } func (x *SearchResponse) Reset() { @@ -147,6 +148,13 @@ func (x *SearchResponse) GetNextPageToken() string { return "" } +func (x *SearchResponse) GetTotalMatches() int32 { + if x != nil { + return x.TotalMatches + } + return 0 +} + type SearchIndexRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -230,6 +238,7 @@ type SearchIndexResponse struct { // Token to retrieve the next page of results, or empty if there are no // more results in the list NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` + TotalMatches int32 `protobuf:"varint,3,opt,name=total_matches,json=totalMatches,proto3" json:"total_matches,omitempty"` } func (x *SearchIndexResponse) Reset() { @@ -278,6 +287,13 @@ func (x *SearchIndexResponse) GetNextPageToken() string { return "" } +func (x *SearchIndexResponse) GetTotalMatches() int32 { + if x != nil { + return x.TotalMatches + } + return 0 +} + type IndexSpaceRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -396,89 +412,94 @@ var file_ocis_services_search_v0_search_proto_rawDesc = []byte{ 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, 0x22, 0x72, 0x0a, 0x0e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x07, 0x6d, 0x61, 0x74, 0x63, 0x68, - 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6f, 0x63, 0x69, 0x73, 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, 0x22, 0xae, 0x01, 0x0a, 0x12, 0x53, 0x65, - 0x61, 0x72, 0x63, 0x68, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x21, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x05, 0x42, 0x04, 0xe2, 0x41, 0x01, 0x01, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, - 0x69, 0x7a, 0x65, 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, 0x3a, - 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6f, 0x63, - 0x69, 0x73, 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, 0x22, 0x77, 0x0a, 0x13, 0x53, 0x65, - 0x61, 0x72, 0x63, 0x68, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x38, 0x0a, 0x07, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6f, 0x63, 0x69, 0x73, 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, 0x22, 0x47, 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, 0x22, 0x14, 0x0a, 0x12, - 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x32, 0x9c, 0x02, 0x0a, 0x0e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x50, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x7b, 0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, - 0x26, 0x2e, 0x6f, 0x63, 0x69, 0x73, 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, 0x27, 0x2e, 0x6f, 0x63, 0x69, 0x73, 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, 0x22, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, - 0x30, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x3a, - 0x01, 0x2a, 0x12, 0x8c, 0x01, 0x0a, 0x0a, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x70, 0x61, 0x63, - 0x65, 0x12, 0x2a, 0x2e, 0x6f, 0x63, 0x69, 0x73, 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, 0x2b, 0x2e, + 0x71, 0x75, 0x65, 0x72, 0x79, 0x22, 0x97, 0x01, 0x0a, 0x0e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x07, 0x6d, 0x61, 0x74, 0x63, + 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6f, 0x63, 0x69, 0x73, + 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, 0x22, + 0xae, 0x01, 0x0a, 0x12, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, + 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x42, 0x04, 0xe2, 0x41, 0x01, 0x01, 0x52, + 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 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, 0x3a, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x22, 0x2e, 0x6f, 0x63, 0x69, 0x73, 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, + 0x22, 0x9c, 0x01, 0x0a, 0x13, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x07, 0x6d, 0x61, 0x74, 0x63, + 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6f, 0x63, 0x69, 0x73, + 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, 0x22, + 0x47, 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, 0x22, 0x14, 0x0a, 0x12, 0x49, 0x6e, 0x64, 0x65, + 0x78, 0x53, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x9c, + 0x02, 0x0a, 0x0e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x12, 0x7b, 0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x26, 0x2e, 0x6f, 0x63, + 0x69, 0x73, 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, 0x27, 0x2e, 0x6f, 0x63, 0x69, 0x73, 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, 0x22, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x30, 0x2f, 0x73, 0x65, + 0x61, 0x72, 0x63, 0x68, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x3a, 0x01, 0x2a, 0x12, 0x8c, + 0x01, 0x0a, 0x0a, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x70, 0x61, 0x63, 0x65, 0x12, 0x2a, 0x2e, 0x6f, 0x63, 0x69, 0x73, 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, 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, 0x3a, 0x01, - 0x2a, 0x32, 0x9d, 0x01, 0x0a, 0x0d, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x64, 0x65, 0x72, 0x12, 0x8b, 0x01, 0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x2b, - 0x2e, 0x6f, 0x63, 0x69, 0x73, 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, 0x2c, 0x2e, 0x6f, 0x63, - 0x69, 0x73, 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, 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, 0x3a, 0x01, - 0x2a, 0x42, 0xdc, 0x02, 0x5a, 0x3c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x6f, 0x63, 0x69, 0x73, - 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, - 0x76, 0x30, 0x92, 0x41, 0x9a, 0x02, 0x12, 0xb4, 0x01, 0x0a, 0x1e, 0x6f, 0x77, 0x6e, 0x43, 0x6c, - 0x6f, 0x75, 0x64, 0x20, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x65, 0x20, 0x53, 0x63, 0x61, - 0x6c, 0x65, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x22, 0x47, 0x0a, 0x0d, 0x6f, 0x77, 0x6e, - 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x20, 0x47, 0x6d, 0x62, 0x48, 0x12, 0x20, 0x68, 0x74, 0x74, 0x70, - 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, - 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x1a, 0x14, 0x73, 0x75, - 0x70, 0x70, 0x6f, 0x72, 0x74, 0x40, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x63, - 0x6f, 0x6d, 0x2a, 0x42, 0x0a, 0x0a, 0x41, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2d, 0x32, 0x2e, 0x30, - 0x12, 0x34, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63, - 0x69, 0x73, 0x2f, 0x62, 0x6c, 0x6f, 0x62, 0x2f, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 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, 0x39, 0x0a, 0x10, 0x44, 0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70, - 0x65, 0x72, 0x20, 0x4d, 0x61, 0x6e, 0x75, 0x61, 0x6c, 0x12, 0x25, 0x68, 0x74, 0x74, 0x70, 0x73, - 0x3a, 0x2f, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x64, 0x65, 0x76, 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, + 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x6f, 0x63, 0x69, 0x73, + 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, 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, 0x3a, 0x01, 0x2a, 0x32, 0x9d, 0x01, + 0x0a, 0x0d, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, + 0x8b, 0x01, 0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x2b, 0x2e, 0x6f, 0x63, 0x69, + 0x73, 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, 0x2c, 0x2e, 0x6f, 0x63, 0x69, 0x73, 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, 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, 0x3a, 0x01, 0x2a, 0x42, 0xdc, 0x02, + 0x5a, 0x3c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x77, 0x6e, + 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x76, 0x30, 0x92, 0x41, + 0x9a, 0x02, 0x12, 0xb4, 0x01, 0x0a, 0x1e, 0x6f, 0x77, 0x6e, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x20, + 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x65, 0x20, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x20, 0x73, + 0x65, 0x61, 0x72, 0x63, 0x68, 0x22, 0x47, 0x0a, 0x0d, 0x6f, 0x77, 0x6e, 0x43, 0x6c, 0x6f, 0x75, + 0x64, 0x20, 0x47, 0x6d, 0x62, 0x48, 0x12, 0x20, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, + 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x1a, 0x14, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, + 0x74, 0x40, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x2a, 0x42, + 0x0a, 0x0a, 0x41, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2d, 0x32, 0x2e, 0x30, 0x12, 0x34, 0x68, 0x74, + 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x62, + 0x6c, 0x6f, 0x62, 0x2f, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 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, 0x39, 0x0a, 0x10, 0x44, 0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x72, 0x20, 0x4d, + 0x61, 0x6e, 0x75, 0x61, 0x6c, 0x12, 0x25, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x6f, + 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x64, 0x65, 0x76, 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 ( diff --git a/protogen/gen/ocis/services/search/v0/search.swagger.json b/protogen/gen/ocis/services/search/v0/search.swagger.json index 99b0f1cca4..5d0f8bd4dd 100644 --- a/protogen/gen/ocis/services/search/v0/search.swagger.json +++ b/protogen/gen/ocis/services/search/v0/search.swagger.json @@ -280,6 +280,10 @@ "nextPageToken": { "type": "string", "title": "Token to retrieve the next page of results, or empty if there are no\nmore results in the list" + }, + "totalMatches": { + "type": "integer", + "format": "int32" } } }, @@ -312,6 +316,10 @@ "nextPageToken": { "type": "string", "title": "Token to retrieve the next page of results, or empty if there are no\nmore results in the list" + }, + "totalMatches": { + "type": "integer", + "format": "int32" } } } diff --git a/protogen/proto/ocis/services/search/v0/search.proto b/protogen/proto/ocis/services/search/v0/search.proto index 5d071bf017..a5993bf166 100644 --- a/protogen/proto/ocis/services/search/v0/search.proto +++ b/protogen/proto/ocis/services/search/v0/search.proto @@ -76,6 +76,7 @@ message SearchResponse { // Token to retrieve the next page of results, or empty if there are no // more results in the list string next_page_token = 2; + int32 total_matches = 3; } message SearchIndexRequest { @@ -91,11 +92,12 @@ message SearchIndexRequest { } message SearchIndexResponse { - repeated ocis.messages.search.v0.Match matches = 1; + repeated ocis.messages.search.v0.Match matches = 1; - // Token to retrieve the next page of results, or empty if there are no - // more results in the list - string next_page_token = 2; + // Token to retrieve the next page of results, or empty if there are no + // more results in the list + string next_page_token = 2; + int32 total_matches = 3; } message IndexSpaceRequest { diff --git a/services/search/pkg/search/index/index.go b/services/search/pkg/search/index/index.go index 345a343358..271557e6ff 100644 --- a/services/search/pkg/search/index/index.go +++ b/services/search/pkg/search/index/index.go @@ -27,12 +27,13 @@ import ( "strings" "time" - "github.com/blevesearch/bleve/v2" + bleve "github.com/blevesearch/bleve/v2" "github.com/blevesearch/bleve/v2/analysis/analyzer/custom" "github.com/blevesearch/bleve/v2/analysis/analyzer/keyword" "github.com/blevesearch/bleve/v2/analysis/token/lowercase" "github.com/blevesearch/bleve/v2/analysis/tokenizer/single" "github.com/blevesearch/bleve/v2/mapping" + "github.com/blevesearch/bleve/v2/search" "github.com/cs3org/reva/v2/pkg/storagespace" "google.golang.org/protobuf/types/known/timestamppb" @@ -239,7 +240,7 @@ func (i *Index) Search(ctx context.Context, req *searchsvc.SearchIndexRequest) ( matches := []*searchmsg.Match{} for _, h := range res.Hits { - match, err := fromFields(h.Fields) + match, err := fromDocumentMatch(h) if err != nil { return nil, err } @@ -247,7 +248,8 @@ func (i *Index) Search(ctx context.Context, req *searchsvc.SearchIndexRequest) ( } return &searchsvc.SearchIndexResponse{ - Matches: matches, + Matches: matches, + TotalMatches: int32(res.Total), }, nil } @@ -311,32 +313,33 @@ func fieldsToEntity(fields map[string]interface{}) *indexDocument { return doc } -func fromFields(fields map[string]interface{}) (*searchmsg.Match, error) { - rootID, err := storagespace.ParseID(fields["RootID"].(string)) +func fromDocumentMatch(hit *search.DocumentMatch) (*searchmsg.Match, error) { + rootID, err := storagespace.ParseID(hit.Fields["RootID"].(string)) if err != nil { return nil, err } - rID, err := storagespace.ParseID(fields["ID"].(string)) + rID, err := storagespace.ParseID(hit.Fields["ID"].(string)) if err != nil { return nil, err } match := &searchmsg.Match{ + Score: float32(hit.Score), Entity: &searchmsg.Entity{ Ref: &searchmsg.Reference{ ResourceId: resourceIDtoSearchID(rootID), - Path: fields["Path"].(string), + Path: hit.Fields["Path"].(string), }, Id: resourceIDtoSearchID(rID), - Name: fields["Name"].(string), - Size: uint64(fields["Size"].(float64)), - Type: uint64(fields["Type"].(float64)), - MimeType: fields["MimeType"].(string), - Deleted: fields["Deleted"].(bool), + Name: hit.Fields["Name"].(string), + Size: uint64(hit.Fields["Size"].(float64)), + Type: uint64(hit.Fields["Type"].(float64)), + MimeType: hit.Fields["MimeType"].(string), + Deleted: hit.Fields["Deleted"].(bool), }, } - if mtime, err := time.Parse(time.RFC3339, fields["Mtime"].(string)); err == nil { + if mtime, err := time.Parse(time.RFC3339, hit.Fields["Mtime"].(string)); err == nil { match.Entity.LastModifiedTime = ×tamppb.Timestamp{Seconds: mtime.Unix(), Nanos: int32(mtime.Nanosecond())} } diff --git a/services/search/pkg/search/index/index_test.go b/services/search/pkg/search/index/index_test.go index 368c704979..036f1e20aa 100644 --- a/services/search/pkg/search/index/index_test.go +++ b/services/search/pkg/search/index/index_test.go @@ -190,6 +190,19 @@ var _ = Describe("Index", func() { assertDocCount(ref.ResourceId, "Name:*"+ref.ResourceId.OpaqueId+"*", 0) }) + It("returns the total number of hits", func() { + res, err := i.Search(ctx, &searchsvc.SearchIndexRequest{ + Query: "Name:foo.pdf", + Ref: &searchmsg.Reference{ + ResourceId: &searchmsg.ResourceID{ + StorageId: ref.ResourceId.StorageId, + OpaqueId: ref.ResourceId.OpaqueId, + }, + }, + }) + Expect(err).ToNot(HaveOccurred()) + Expect(res.TotalMatches).To(Equal(int32(1))) + }) It("returns all desired fields", func() { matches := assertDocCount(ref.ResourceId, "Name:foo.pdf", 1) match := matches[0] @@ -201,6 +214,7 @@ var _ = Describe("Index", func() { Expect(match.Entity.Type).To(Equal(uint64(ri.Type))) Expect(match.Entity.MimeType).To(Equal(ri.MimeType)) Expect(match.Entity.Deleted).To(BeFalse()) + Expect(match.Score > 0).To(BeTrue()) Expect(uint64(match.Entity.LastModifiedTime.AsTime().Unix())).To(Equal(ri.Mtime.Seconds)) }) diff --git a/services/search/pkg/search/provider/searchprovider.go b/services/search/pkg/search/provider/searchprovider.go index f19bed8069..a744699e6e 100644 --- a/services/search/pkg/search/provider/searchprovider.go +++ b/services/search/pkg/search/provider/searchprovider.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "path/filepath" + "sort" "strings" "time" @@ -44,6 +45,18 @@ type Provider struct { machineAuthAPIKey string } +type MatchArray []*searchmsg.Match + +func (s MatchArray) Len() int { + return len(s) +} +func (s MatchArray) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} +func (s MatchArray) Less(i, j int) bool { + return s[i].Score > s[j].Score +} + func New(gwClient gateway.GatewayAPIClient, indexClient search.IndexClient, machineAuthAPIKey string, eventsChan <-chan interface{}, logger log.Logger) *Provider { p := &Provider{ gwClient: gwClient, @@ -98,7 +111,8 @@ func (p *Provider) Search(ctx context.Context, req *searchsvc.SearchRequest) (*s mountpointMap[grantSpaceId] = space.Id.OpaqueId } - matches := []*searchmsg.Match{} + matches := MatchArray{} + total := int32(0) for _, space := range listSpacesRes.StorageSpaces { var mountpointRootId *searchmsg.ResourceID mountpointPrefix := "" @@ -154,6 +168,7 @@ func (p *Provider) Search(ctx context.Context, req *searchsvc.SearchRequest) (*s } p.logger.Debug().Str("space", space.Id.OpaqueId).Int("hits", len(res.Matches)).Msg("space search done") + total += res.TotalMatches for _, match := range res.Matches { if mountpointPrefix != "" { match.Entity.Ref.Path = utils.MakeRelativePath(strings.TrimPrefix(match.Entity.Ref.Path, mountpointPrefix)) @@ -165,8 +180,19 @@ func (p *Provider) Search(ctx context.Context, req *searchsvc.SearchRequest) (*s } } + // compile one sorted list of matches from all spaces and apply the limit if needed + sort.Sort(matches) + limit := req.PageSize + if limit == 0 { + limit = 200 + } + if int32(len(matches)) > limit { + matches = matches[0:limit] + } + return &searchsvc.SearchResponse{ - Matches: matches, + Matches: matches, + TotalMatches: total, }, nil } diff --git a/services/search/pkg/search/provider/searchprovider_test.go b/services/search/pkg/search/provider/searchprovider_test.go index ee131614a8..c04299c4dd 100644 --- a/services/search/pkg/search/provider/searchprovider_test.go +++ b/services/search/pkg/search/provider/searchprovider_test.go @@ -125,8 +125,10 @@ var _ = Describe("Searchprovider", func() { StorageSpaces: []*sprovider.StorageSpace{personalSpace}, }, nil) indexClient.On("Search", mock.Anything, mock.Anything).Return(&searchsvc.SearchIndexResponse{ + TotalMatches: 1, Matches: []*searchmsg.Match{ { + Score: 1, Entity: &searchmsg.Entity{ Ref: &searchmsg.Reference{ ResourceId: &searchmsg.ResourceID{ @@ -196,6 +198,7 @@ var _ = Describe("Searchprovider", func() { }) Expect(err).ToNot(HaveOccurred()) Expect(res).ToNot(BeNil()) + Expect(res.TotalMatches).To(Equal(int32(1))) Expect(len(res.Matches)).To(Equal(1)) match := res.Matches[0] Expect(match.Entity.Id.OpaqueId).To(Equal("foo-id")) @@ -248,6 +251,7 @@ var _ = Describe("Searchprovider", func() { StorageSpaces: []*sprovider.StorageSpace{grantSpace, mountpointSpace}, }, nil) indexClient.On("Search", mock.Anything, mock.Anything).Return(&searchsvc.SearchIndexResponse{ + TotalMatches: 1, Matches: []*searchmsg.Match{ { Entity: &searchmsg.Entity{ @@ -285,64 +289,101 @@ var _ = Describe("Searchprovider", func() { })) }) - It("finds matches in both the personal space AND the grant", func() { - gwClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&sprovider.ListStorageSpacesResponse{ - Status: status.NewOK(ctx), - StorageSpaces: []*sprovider.StorageSpace{personalSpace, grantSpace, mountpointSpace}, - }, nil) - indexClient.On("Search", mock.Anything, mock.MatchedBy(func(req *searchsvc.SearchIndexRequest) bool { - return req.Ref.ResourceId.StorageId == grantSpace.Root.StorageId - })).Return(&searchsvc.SearchIndexResponse{ - Matches: []*searchmsg.Match{ - { - Entity: &searchmsg.Entity{ - Ref: &searchmsg.Reference{ - ResourceId: &searchmsg.ResourceID{ + Context("when searching both spaces", func() { + BeforeEach(func() { + gwClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&sprovider.ListStorageSpacesResponse{ + Status: status.NewOK(ctx), + StorageSpaces: []*sprovider.StorageSpace{personalSpace, grantSpace, mountpointSpace}, + }, nil) + indexClient.On("Search", mock.Anything, mock.MatchedBy(func(req *searchsvc.SearchIndexRequest) bool { + return req.Ref.ResourceId.StorageId == grantSpace.Root.StorageId + })).Return(&searchsvc.SearchIndexResponse{ + TotalMatches: 2, + Matches: []*searchmsg.Match{ + { + Score: 2, + Entity: &searchmsg.Entity{ + Ref: &searchmsg.Reference{ + ResourceId: &searchmsg.ResourceID{ + StorageId: grantSpace.Root.StorageId, + OpaqueId: grantSpace.Root.OpaqueId, + }, + Path: "./grant/path/to/Shared.pdf", + }, + Id: &searchmsg.ResourceID{ StorageId: grantSpace.Root.StorageId, - OpaqueId: grantSpace.Root.OpaqueId, + OpaqueId: "grant-shared-id", }, - Path: "./grant/path/to/Shared.pdf", + Name: "Shared.pdf", }, - Id: &searchmsg.ResourceID{ - StorageId: grantSpace.Root.StorageId, - OpaqueId: "grant-shared-id", + }, + { + Score: 0.01, + Entity: &searchmsg.Entity{ + Ref: &searchmsg.Reference{ + ResourceId: &searchmsg.ResourceID{ + StorageId: grantSpace.Root.StorageId, + OpaqueId: grantSpace.Root.OpaqueId, + }, + Path: "./grant/path/to/Irrelevant.pdf", + }, + Id: &searchmsg.ResourceID{ + StorageId: grantSpace.Root.StorageId, + OpaqueId: "grant-irrelevant-id", + }, + Name: "Irrelevant.pdf", }, - Name: "Shared.pdf", }, }, - }, - }, nil) - indexClient.On("Search", mock.Anything, mock.MatchedBy(func(req *searchsvc.SearchIndexRequest) bool { - return req.Ref.ResourceId.StorageId == personalSpace.Root.StorageId - })).Return(&searchsvc.SearchIndexResponse{ - Matches: []*searchmsg.Match{ - { - Entity: &searchmsg.Entity{ - Ref: &searchmsg.Reference{ - ResourceId: &searchmsg.ResourceID{ + }, nil) + indexClient.On("Search", mock.Anything, mock.MatchedBy(func(req *searchsvc.SearchIndexRequest) bool { + return req.Ref.ResourceId.StorageId == personalSpace.Root.StorageId + })).Return(&searchsvc.SearchIndexResponse{ + TotalMatches: 1, + Matches: []*searchmsg.Match{ + { + Score: 1, + Entity: &searchmsg.Entity{ + Ref: &searchmsg.Reference{ + ResourceId: &searchmsg.ResourceID{ + StorageId: personalSpace.Root.StorageId, + OpaqueId: personalSpace.Root.OpaqueId, + }, + Path: "./path/to/Foo.pdf", + }, + Id: &searchmsg.ResourceID{ StorageId: personalSpace.Root.StorageId, - OpaqueId: personalSpace.Root.OpaqueId, + OpaqueId: "foo-id", }, - Path: "./path/to/Foo.pdf", + Name: "Foo.pdf", }, - Id: &searchmsg.ResourceID{ - StorageId: personalSpace.Root.StorageId, - OpaqueId: "foo-id", - }, - Name: "Foo.pdf", }, }, - }, - }, nil) - - res, err := p.Search(ctx, &searchsvc.SearchRequest{ - Query: "foo", + }, nil) + }) + + It("finds matches in both the personal space AND the grant", func() { + res, err := p.Search(ctx, &searchsvc.SearchRequest{ + Query: "foo", + }) + Expect(err).ToNot(HaveOccurred()) + Expect(res).ToNot(BeNil()) + Expect(len(res.Matches)).To(Equal(3)) + ids := []string{res.Matches[0].Entity.Id.OpaqueId, res.Matches[1].Entity.Id.OpaqueId, res.Matches[2].Entity.Id.OpaqueId} + Expect(ids).To(ConsistOf("foo-id", "grant-shared-id", "grant-irrelevant-id")) + }) + + It("sorts and limits the combined results from all spaces", func() { + res, err := p.Search(ctx, &searchsvc.SearchRequest{ + Query: "foo", + PageSize: 2, + }) + Expect(err).ToNot(HaveOccurred()) + Expect(res).ToNot(BeNil()) + Expect(len(res.Matches)).To(Equal(2)) + ids := []string{res.Matches[0].Entity.Id.OpaqueId, res.Matches[1].Entity.Id.OpaqueId} + Expect(ids).To(Equal([]string{"grant-shared-id", "foo-id"})) }) - Expect(err).ToNot(HaveOccurred()) - Expect(res).ToNot(BeNil()) - Expect(len(res.Matches)).To(Equal(2)) - ids := []string{res.Matches[0].Entity.Id.OpaqueId, res.Matches[1].Entity.Id.OpaqueId} - Expect(ids).To(ConsistOf("foo-id", "grant-shared-id")) }) }) }) diff --git a/services/search/pkg/service/v0/service.go b/services/search/pkg/service/v0/service.go index 9fd0da6d40..b46a7111dc 100644 --- a/services/search/pkg/service/v0/service.go +++ b/services/search/pkg/service/v0/service.go @@ -107,6 +107,7 @@ func (s Service) Search(ctx context.Context, in *searchsvc.SearchRequest, out *s } out.Matches = res.Matches + out.TotalMatches = res.TotalMatches out.NextPageToken = res.NextPageToken return nil } diff --git a/services/webdav/pkg/service/v0/search.go b/services/webdav/pkg/service/v0/search.go index 084475ab93..64d7cc0a5f 100644 --- a/services/webdav/pkg/service/v0/search.go +++ b/services/webdav/pkg/service/v0/search.go @@ -3,6 +3,7 @@ package svc import ( "context" "encoding/xml" + "fmt" "io" "net/http" "path" @@ -71,6 +72,9 @@ func (g Webdav) sendSearchResponse(rsp *searchsvc.SearchResponse, w http.Respons } w.Header().Set(net.HeaderDav, "1, 3, extended-mkcol") w.Header().Set(net.HeaderContentType, "application/xml; charset=utf-8") + if len(rsp.Matches) > 0 { + w.Header().Set(net.HeaderContentRange, fmt.Sprintf("rows 0-%d/%d", len(rsp.Matches)-1, rsp.TotalMatches)) + } w.WriteHeader(http.StatusMultiStatus) if _, err := w.Write(responsesXML); err != nil { g.log.Err(err).Msg("error writing response")