Provide Search filter for locations #OCIS-3705

This commit is contained in:
Roman Perekhod
2023-06-28 16:55:52 +02:00
parent 67f8107911
commit 3487749509
4 changed files with 119 additions and 14 deletions

View File

@@ -0,0 +1,8 @@
Enhancement: Provide Search filter for locations
The search result REPORT response now can be restricted the by the current folder via api (recursive)
The scope needed for "current folder" (default is to search all available spaces) - part of the oc:pattern:"scope:<uuid>
/Test"
https://github.com/owncloud/ocis/pull/6713
OCIS-3705

View File

@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"regexp"
"strings"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
@@ -13,6 +14,7 @@ import (
ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
"github.com/cs3org/reva/v2/pkg/errtypes"
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
"github.com/cs3org/reva/v2/pkg/storagespace"
"github.com/cs3org/reva/v2/pkg/utils"
"github.com/owncloud/ocis/v2/ocis-pkg/log"
searchmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/search/v0"
@@ -20,6 +22,8 @@ import (
"google.golang.org/grpc/metadata"
)
var scopeRegex = regexp.MustCompile(`scope:\s*([^" "\n\r]*)`)
// ResolveReference makes sure the path is relative to the space root
func ResolveReference(ctx context.Context, ref *provider.Reference, ri *provider.ResourceInfo, gatewaySelector pool.Selectable[gateway.GatewayAPIClient]) (*provider.Reference, error) {
if ref.GetResourceId().GetOpaqueId() == ref.GetResourceId().GetSpaceId() {
@@ -155,3 +159,28 @@ func convertToWebDAVPermissions(isShared, isMountpoint, isDir bool, p *provider.
}
return b.String()
}
func extractScope(path string) (*searchmsg.Reference, error) {
ref, err := storagespace.ParseReference(path)
if err != nil {
return nil, err
}
return &searchmsg.Reference{
ResourceId: &searchmsg.ResourceID{
StorageId: ref.ResourceId.StorageId,
SpaceId: ref.ResourceId.SpaceId,
OpaqueId: ref.ResourceId.OpaqueId,
},
Path: ref.GetPath(),
}, nil
}
// ParseScope extract a scope value from the query string and returns search, scope strings
func ParseScope(query string) (string, string) {
match := scopeRegex.FindStringSubmatch(query)
if len(match) >= 2 {
cut := match[0]
return strings.TrimSpace(strings.ReplaceAll(query, cut, "")), strings.TrimSpace(match[1])
}
return query, ""
}

View File

@@ -72,10 +72,31 @@ func NewService(gatewaySelector pool.Selectable[gateway.GatewayAPIClient], eng e
// Search processes a search request and passes it down to the engine.
func (s *Service) Search(ctx context.Context, req *searchsvc.SearchRequest) (*searchsvc.SearchResponse, error) {
if req.Query == "" {
s.logger.Debug().Str("query", req.Query).Msg("performing a search")
query, scope := ParseScope(req.Query)
if query == "" {
return nil, errtypes.BadRequest("empty query provided")
}
s.logger.Debug().Str("query", req.Query).Msg("performing a search")
req.Query = query
var filters []*provider.ListStorageSpacesRequest_Filter
if len(scope) > 0 {
scopeRef, err := extractScope(scope)
if err != nil {
return nil, err
}
if req.Ref == nil {
req.Ref = scopeRef
}
req.Ref.Path = scopeRef.GetPath()
if scopeRef.GetResourceId().OpaqueId != "" {
filters = []*provider.ListStorageSpacesRequest_Filter{
{
Type: provider.ListStorageSpacesRequest_Filter_TYPE_ID,
Term: &provider.ListStorageSpacesRequest_Filter_Id{Id: &provider.StorageSpaceId{OpaqueId: scopeRef.GetResourceId().OpaqueId}},
},
}
}
}
gatewayClient, err := s.gatewaySelector.Next()
if err != nil {
@@ -84,18 +105,17 @@ func (s *Service) Search(ctx context.Context, req *searchsvc.SearchRequest) (*se
currentUser := revactx.ContextMustGetUser(ctx)
listSpacesRes, err := gatewayClient.ListStorageSpaces(ctx, &provider.ListStorageSpacesRequest{
Filters: []*provider.ListStorageSpacesRequest_Filter{
{
Type: provider.ListStorageSpacesRequest_Filter_TYPE_USER,
Term: &provider.ListStorageSpacesRequest_Filter_User{User: currentUser.GetId()},
},
{
Type: provider.ListStorageSpacesRequest_Filter_TYPE_SPACE_TYPE,
Term: &provider.ListStorageSpacesRequest_Filter_SpaceType{SpaceType: "+grant"},
},
filters = append(filters, []*provider.ListStorageSpacesRequest_Filter{
{
Type: provider.ListStorageSpacesRequest_Filter_TYPE_USER,
Term: &provider.ListStorageSpacesRequest_Filter_User{User: currentUser.GetId()},
},
})
{
Type: provider.ListStorageSpacesRequest_Filter_TYPE_SPACE_TYPE,
Term: &provider.ListStorageSpacesRequest_Filter_SpaceType{SpaceType: "+grant"},
},
}...)
listSpacesRes, err := gatewayClient.ListStorageSpaces(ctx, &provider.ListStorageSpacesRequest{Filters: filters})
if err != nil {
s.logger.Error().Err(err).Msg("failed to list the user's storage spaces")
return nil, err
@@ -229,7 +249,7 @@ func (s *Service) searchIndex(ctx context.Context, req *searchsvc.SearchRequest,
rootName string
permissions *provider.ResourcePermissions
)
mountpointPrefix := ""
mountpointPrefix := req.GetRef().GetPath()
switch space.SpaceType {
case "mountpoint":
return nil, errSkipSpace // mountpoint spaces are only "links" to the shared spaces. we have to search the shared "grant" space instead

View File

@@ -391,3 +391,51 @@ var _ = Describe("Searchprovider", func() {
})
})
})
var _ = DescribeTable("Parse Scope",
func(pattern, wantSearch, wantScope string) {
gotSearch, gotScope := search.ParseScope(pattern)
Expect(gotSearch).To(Equal(wantSearch))
Expect(gotScope).To(Equal(wantScope))
},
Entry("When scope is at the end of the line",
`+Name:*file* +Tags:&quot;foo&quot; scope:<uuid>/folder/subfolder`,
`+Name:*file* +Tags:&quot;foo&quot;`,
`<uuid>/folder/subfolder`,
),
Entry("When scope is at the end of the line 2",
`+Name:*file* +Tags:&quot;foo&quot; scope:<uuid>/folder`,
`+Name:*file* +Tags:&quot;foo&quot;`,
`<uuid>/folder`,
),
Entry("When scope is at the end of the line 3",
`file scope:<uuid>/folder/subfolder`,
`file`,
`<uuid>/folder/subfolder`,
),
Entry("When scope is at the end of the line with a space",
`+Name:*file* +Tags:&quot;foo&quot; scope: <uuid>/folder/subfolder`,
`+Name:*file* +Tags:&quot;foo&quot;`,
`<uuid>/folder/subfolder`,
),
Entry("When scope is in the middle of the line",
`+Name:*file* scope:<uuid>/folder/subfolder +Tags:&quot;foo&quot;`,
`+Name:*file* +Tags:&quot;foo&quot;`,
`<uuid>/folder/subfolder`,
),
Entry("When scope is at the end of the line",
`scope:<uuid>/folder/subfolder +Name:*file*`,
`+Name:*file*`,
`<uuid>/folder/subfolder`,
),
Entry("When scope is at the begging of the line",
`scope:<uuid>/folder/subfolder file`,
`file`,
`<uuid>/folder/subfolder`,
),
Entry("When no scope",
`+Name:*file* +Tags:&quot;foo&quot;`,
`+Name:*file* +Tags:&quot;foo&quot;`,
``,
),
)