Skip to content

Commit

Permalink
Merge pull request #6713 from 2403905/OCIS-3705
Browse files Browse the repository at this point in the history
Provide Search filter for locations
  • Loading branch information
2403905 authored Jul 12, 2023
2 parents 67f8107 + 99b8cdc commit aad05c9
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -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
4 changes: 3 additions & 1 deletion services/search/pkg/engine/bleve.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,10 @@ func (b *Bleve) Search(_ context.Context, sir *searchService.SearchIndexRequest)
}

matches := make([]*searchMessage.Match, 0, len(res.Hits))
totalMatches := res.Total
for _, hit := range res.Hits {
if sir.Ref != nil && !strings.HasPrefix(getFieldValue[string](hit.Fields, "Path"), utils.MakeRelativePath(path.Join(sir.Ref.Path, "/"))) {
totalMatches--
continue
}

Expand Down Expand Up @@ -206,7 +208,7 @@ func (b *Bleve) Search(_ context.Context, sir *searchService.SearchIndexRequest)

return &searchService.SearchIndexResponse{
Matches: matches,
TotalMatches: int32(res.Total),
TotalMatches: int32(totalMatches),
}, nil
}

Expand Down
29 changes: 29 additions & 0 deletions services/search/pkg/search/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"regexp"
"strings"

gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
Expand All @@ -13,13 +14,16 @@ 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"
"github.com/owncloud/ocis/v2/services/search/pkg/engine"
"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() {
Expand Down Expand Up @@ -155,3 +159,28 @@ func convertToWebDAVPermissions(isShared, isMountpoint, isDir bool, p *provider.
}
return b.String()
}

func extractScope(path string) (*provider.Reference, error) {
ref, err := storagespace.ParseReference(path)
if err != nil {
return nil, err
}
return &provider.Reference{
ResourceId: &provider.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, ""
}
98 changes: 73 additions & 25 deletions services/search/pkg/search/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,18 @@ import (
"github.com/owncloud/ocis/v2/services/search/pkg/content"
"github.com/owncloud/ocis/v2/services/search/pkg/engine"
"golang.org/x/sync/errgroup"
"google.golang.org/protobuf/types/known/fieldmaskpb"
)

//go:generate mockery --name=Searcher

const (
_spaceStateTrashed = "trashed"
_slowQueryDuration = 500 * time.Millisecond
_spaceStateTrashed = "trashed"
_spaceTypeMountpoint = "mountpoint"
_spaceTypePersonal = "personal"
_spaceTypeProject = "project"
_spaceTypeGrant = "grant"
_slowQueryDuration = 500 * time.Millisecond
)

// Searcher is the interface to the SearchService
Expand Down Expand Up @@ -72,47 +77,87 @@ 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 == "" {
return nil, errtypes.BadRequest("empty query provided")
}
s.logger.Debug().Str("query", req.Query).Msg("performing a search")

gatewayClient, err := s.gatewaySelector.Next()
if err != nil {
return nil, err
}

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"},
// Extract scope from query if set
query, scope := ParseScope(req.Query)
if query == "" {
return nil, errtypes.BadRequest("empty query provided")
}
req.Query = query
if len(scope) > 0 {
// if req.Ref != nil {
// return nil, errtypes.BadRequest("cannot scope a search that is limited to a resource")
// }
scopeRef, err := extractScope(scope)
if err != nil {
return nil, err
}
// Stat the scope to get the resource id
statRes, err := gatewayClient.Stat(ctx, &provider.StatRequest{
Ref: scopeRef,
FieldMask: &fieldmaskpb.FieldMask{Paths: []string{"space"}},
})
if err != nil {
return nil, err
}
// GetPath the scope to get the full path in the space
gpRes, err := gatewayClient.GetPath(ctx, &provider.GetPathRequest{
ResourceId: statRes.GetInfo().GetId(),
})
if err != nil {
return nil, err
}

req.Ref = &searchmsg.Reference{
ResourceId: &searchmsg.ResourceID{
StorageId: statRes.GetInfo().GetSpace().GetRoot().GetStorageId(),
SpaceId: statRes.GetInfo().GetSpace().GetRoot().GetSpaceId(),
OpaqueId: statRes.GetInfo().GetSpace().GetRoot().GetOpaqueId(),
},
Path: gpRes.Path,
}
}
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"},
},
}

// Get the spaces to search
spaces := []*provider.StorageSpace{}
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
}

spaces := []*provider.StorageSpace{}
for _, space := range listSpacesRes.StorageSpaces {
if utils.ReadPlainFromOpaque(space.Opaque, "trashed") == _spaceStateTrashed {
// Do not consider disabled spaces
continue
}
if space.SpaceType != "mountpoint" && req.Ref != nil && (req.Ref.GetResourceId().GetSpaceId() != space.Root.GetSpaceId()) {
// Do not search (non-mountpoint) spaces that do not match the given scope (if a scope is set)
// We still need the mountpoint in order to map the result paths to the according share
continue
}
spaces = append(spaces, space)
}

mountpointMap := map[string]string{}
for _, space := range spaces {
if space.SpaceType != "mountpoint" {
if space.SpaceType != _spaceTypeMountpoint {
continue
}
opaqueMap := sdk.DecodeOpaqueMap(space.Opaque)
Expand Down Expand Up @@ -213,8 +258,7 @@ func (s *Service) Search(ctx context.Context, req *searchsvc.SearchRequest) (*se
func (s *Service) searchIndex(ctx context.Context, req *searchsvc.SearchRequest, space *provider.StorageSpace, mountpointID string) (*searchsvc.SearchIndexResponse, error) {
if req.Ref != nil &&
(req.Ref.ResourceId.StorageId != space.Root.StorageId ||
req.Ref.ResourceId.SpaceId != space.Root.SpaceId ||
req.Ref.ResourceId.OpaqueId != space.Root.OpaqueId) {
req.Ref.ResourceId.SpaceId != space.Root.SpaceId) {
return nil, errSkipSpace
}

Expand All @@ -230,10 +274,11 @@ func (s *Service) searchIndex(ctx context.Context, req *searchsvc.SearchRequest,
permissions *provider.ResourcePermissions
)
mountpointPrefix := ""
searchPathPrefix := req.Ref.GetPath()
switch space.SpaceType {
case "mountpoint":
case _spaceTypeMountpoint:
return nil, errSkipSpace // mountpoint spaces are only "links" to the shared spaces. we have to search the shared "grant" space instead
case "grant":
case _spaceTypeGrant:
// In case of grant spaces we search the root of the outer space and translate the paths to the according mountpoint
searchRootID.OpaqueId = space.Root.SpaceId
if mountpointID == "" {
Expand Down Expand Up @@ -272,6 +317,9 @@ func (s *Service) searchIndex(ctx context.Context, req *searchsvc.SearchRequest,
return nil, errSkipSpace
}
mountpointPrefix = utils.MakeRelativePath(gpRes.Path)
if searchPathPrefix == "" {
searchPathPrefix = mountpointPrefix
}
sid, spid, oid, err := storagespace.SplitID(mountpointID)
if err != nil {
s.logger.Error().Err(err).Str("space", space.Id.OpaqueId).Str("mountpointId", mountpointID).Msg("invalid mountpoint space id")
Expand All @@ -285,15 +333,15 @@ func (s *Service) searchIndex(ctx context.Context, req *searchsvc.SearchRequest,
rootName = space.GetRootInfo().GetPath()
permissions = space.GetRootInfo().GetPermissionSet()
s.logger.Debug().Interface("grantSpace", space).Interface("mountpointRootId", mountpointRootID).Msg("searching a grant")
case "personal", "project":
case _spaceTypePersonal, _spaceTypeProject:
permissions = space.GetRootInfo().GetPermissionSet()
}

searchRequest := &searchsvc.SearchIndexRequest{
Query: req.Query,
Ref: &searchmsg.Reference{
ResourceId: searchRootID,
Path: mountpointPrefix,
Path: searchPathPrefix,
},
PageSize: req.PageSize,
}
Expand Down
Loading

0 comments on commit aad05c9

Please sign in to comment.