From 51e373c3f0822bd2b720cc86e7737c2525de6dbd Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Thu, 1 Oct 2020 15:12:39 +0300 Subject: [PATCH] [#61] object/search: Support latest search filters Refactor query to match object and its parents in a single call. Support KeyRoot and KeyLeaf filters. Signed-off-by: Leonard Lyubich --- pkg/services/object/search/local.go | 19 ++--- pkg/services/object/search/query/query.go | 2 +- pkg/services/object/search/query/v1/keys.go | 32 ++++---- pkg/services/object/search/query/v1/v1.go | 75 ++++++------------- .../object/search/query/v1/v1_test.go | 53 ++++++++----- pkg/services/object/search/v2/util.go | 17 +---- 6 files changed, 85 insertions(+), 113 deletions(-) diff --git a/pkg/services/object/search/local.go b/pkg/services/object/search/local.go index 7384887e..ed178fa6 100644 --- a/pkg/services/object/search/local.go +++ b/pkg/services/object/search/local.go @@ -51,17 +51,14 @@ func (s *localStream) stream(ctx context.Context, ch chan<- []*objectSDK.ID) err } func (f *searchQueryFilter) Pass(ctx context.Context, meta *localstore.ObjectMeta) *localstore.FilterResult { -loop: - for obj := meta.Head(); obj != nil; obj = obj.GetParent() { - if !f.cid.Equal(obj.GetContainerID()) || !f.query.Match(obj) { - continue - } - - select { - case <-ctx.Done(): - break loop - case f.ch <- []*objectSDK.ID{obj.GetID()}: - } + if obj := meta.Head(); f.cid.Equal(obj.GetContainerID()) { + f.query.Match(meta.Head(), func(id *objectSDK.ID) { + select { + case <-ctx.Done(): + return + case f.ch <- []*objectSDK.ID{id}: + } + }) } return localstore.ResultPass() diff --git a/pkg/services/object/search/query/query.go b/pkg/services/object/search/query/query.go index 33051d3d..1efebd55 100644 --- a/pkg/services/object/search/query/query.go +++ b/pkg/services/object/search/query/query.go @@ -7,5 +7,5 @@ import ( type Query interface { ToSearchFilters() objectSDK.SearchFilters - Match(*object.Object) bool + Match(*object.Object, func(*objectSDK.ID)) } diff --git a/pkg/services/object/search/query/v1/keys.go b/pkg/services/object/search/query/v1/keys.go index e39cbcfc..f4a43e96 100644 --- a/pkg/services/object/search/query/v1/keys.go +++ b/pkg/services/object/search/query/v1/keys.go @@ -13,24 +13,24 @@ const keyNoChildrenField = "Object.Header.Split.NoChildren" const keyParentIDField = "Object.Header.Split.Parent" -func NewEmptyChildrenFilter() *Filter { - return NewFilterEqual(keyNoChildrenField, "") -} - -func NewParentIDFilter(par *object.ID) *Filter { - return NewFilterEqual(keyParentIDField, idValue(par)) -} - func NewRightChildQuery(par *object.ID) query.Query { - return New( - NewParentIDFilter(par), - NewEmptyChildrenFilter(), - ) + q := &Query{ + filters: make(object.SearchFilters, 0, 2), + } + + q.filters.AddFilter(keyParentIDField, idValue(par), object.MatchStringEqual) + q.filters.AddFilter(keyNoChildrenField, "", object.MatchStringEqual) + + return q } func NewLinkingQuery(par *object.ID) query.Query { - return New( - NewParentIDFilter(par), - NewFilterEqual(keyParentField, ""), - ) + q := &Query{ + filters: make(object.SearchFilters, 0, 2), + } + + q.filters.AddFilter(keyParentIDField, idValue(par), object.MatchStringEqual) + q.filters.AddFilter(keyParentField, "", object.MatchStringEqual) + + return q } diff --git a/pkg/services/object/search/query/v1/v1.go b/pkg/services/object/search/query/v1/v1.go index 6dbb9f6e..0cc44881 100644 --- a/pkg/services/object/search/query/v1/v1.go +++ b/pkg/services/object/search/query/v1/v1.go @@ -2,7 +2,6 @@ package query import ( "encoding/hex" - "fmt" "github.com/nspcc-dev/neofs-api-go/pkg/container" objectSDK "github.com/nspcc-dev/neofs-api-go/pkg/object" @@ -12,23 +11,10 @@ import ( ) type Query struct { - filters []*Filter + filters objectSDK.SearchFilters } -type matchType uint8 - -type Filter struct { - matchType matchType - - key, val string -} - -const ( - _ matchType = iota - matchStringEqual -) - -func New(filters ...*Filter) query.Query { +func New(filters objectSDK.SearchFilters) query.Query { return &Query{ filters: filters, } @@ -38,47 +24,38 @@ func idValue(id *objectSDK.ID) string { return hex.EncodeToString(id.ToV2().GetValue()) } -func NewIDEqualFilter(id *objectSDK.ID) *Filter { - return NewFilterEqual(objectSDK.HdrSysNameID, idValue(id)) -} - func cidValue(id *container.ID) string { return hex.EncodeToString(id.ToV2().GetValue()) } -func NewContainerIDEqualFilter(id *container.ID) *Filter { - return NewFilterEqual(objectSDK.HdrSysNameCID, cidValue(id)) -} - func ownerIDValue(id *owner.ID) string { return hex.EncodeToString(id.ToV2().GetValue()) } -func NewOwnerIDEqualFilter(id *owner.ID) *Filter { - return NewFilterEqual(objectSDK.HdrSysNameOwnerID, ownerIDValue(id)) -} +func (q *Query) Match(obj *object.Object, handler func(*objectSDK.ID)) { + for par := (*object.Object)(nil); obj != nil; par, obj = obj, obj.GetParent() { + match := true -func NewFilterEqual(key, val string) *Filter { - return &Filter{ - matchType: matchStringEqual, - key: key, - val: val, - } -} - -func (q *Query) Match(obj *object.Object) bool { - for _, f := range q.filters { - switch f.matchType { - case matchStringEqual: - if !headerEqual(obj, f.key, f.val) { - return false + for i := 0; match && i < len(q.filters); i++ { + switch typ := q.filters[i].Operation(); typ { + default: + match = false + case objectSDK.MatchStringEqual: + switch key := q.filters[i].Header(); key { + default: + match = headerEqual(obj, key, q.filters[i].Value()) + case objectSDK.KeyRoot: + match = !obj.HasParent() + case objectSDK.KeyLeaf: + match = par == nil + } } - default: - panic(fmt.Sprintf("unsupported match type %d", f.matchType)) + } + + if match { + handler(obj.GetID()) } } - - return true } func headerEqual(obj *object.Object, key, value string) bool { @@ -108,11 +85,5 @@ func headerEqual(obj *object.Object, key, value string) bool { } func (q *Query) ToSearchFilters() objectSDK.SearchFilters { - fs := make(objectSDK.SearchFilters, 0, len(q.filters)) - - for i := range q.filters { - fs.AddFilter(q.filters[i].key, q.filters[i].val, objectSDK.MatchStringEqual) - } - - return fs + return q.filters } diff --git a/pkg/services/object/search/query/v1/v1_test.go b/pkg/services/object/search/query/v1/v1_test.go index 9540b591..771b10d7 100644 --- a/pkg/services/object/search/query/v1/v1_test.go +++ b/pkg/services/object/search/query/v1/v1_test.go @@ -9,6 +9,7 @@ import ( objectSDK "github.com/nspcc-dev/neofs-api-go/pkg/object" "github.com/nspcc-dev/neofs-api-go/pkg/owner" "github.com/nspcc-dev/neofs-node/pkg/core/object" + "github.com/nspcc-dev/neofs-node/pkg/services/object/search/query" "github.com/stretchr/testify/require" ) @@ -48,6 +49,14 @@ func testOwnerID(t *testing.T) *owner.ID { return id } +func matchesQuery(q query.Query, obj *object.Object) (res bool) { + q.Match(obj, func(id *objectSDK.ID) { + res = id.Equal(obj.GetID()) + }) + + return +} + func TestQ_Match(t *testing.T) { t.Run("object identifier equal", func(t *testing.T) { obj := object.NewRaw() @@ -55,15 +64,16 @@ func TestQ_Match(t *testing.T) { id := testID(t) obj.SetID(id) - q := New( - NewIDEqualFilter(id), - ) + fs := objectSDK.SearchFilters{} + fs.AddFilter(objectSDK.HdrSysNameID, idValue(id), objectSDK.MatchStringEqual) - require.True(t, q.Match(obj.Object())) + q := New(fs) + + require.True(t, matchesQuery(q, obj.Object())) obj.SetID(testID(t)) - require.False(t, q.Match(obj.Object())) + require.False(t, matchesQuery(q, obj.Object())) }) t.Run("container identifier equal", func(t *testing.T) { @@ -72,15 +82,16 @@ func TestQ_Match(t *testing.T) { id := testCID(t) obj.SetContainerID(id) - q := New( - NewContainerIDEqualFilter(id), - ) + fs := objectSDK.SearchFilters{} + fs.AddFilter(objectSDK.HdrSysNameCID, cidValue(id), objectSDK.MatchStringEqual) - require.True(t, q.Match(obj.Object())) + q := New(fs) + + require.True(t, matchesQuery(q, obj.Object())) obj.SetContainerID(testCID(t)) - require.False(t, q.Match(obj.Object())) + require.False(t, matchesQuery(q, obj.Object())) }) t.Run("owner identifier equal", func(t *testing.T) { @@ -89,15 +100,16 @@ func TestQ_Match(t *testing.T) { id := testOwnerID(t) obj.SetOwnerID(id) - q := New( - NewOwnerIDEqualFilter(id), - ) + fs := objectSDK.SearchFilters{} + fs.AddFilter(objectSDK.HdrSysNameOwnerID, ownerIDValue(id), objectSDK.MatchStringEqual) - require.True(t, q.Match(obj.Object())) + q := New(fs) + + require.True(t, matchesQuery(q, obj.Object())) obj.SetOwnerID(testOwnerID(t)) - require.False(t, q.Match(obj.Object())) + require.False(t, matchesQuery(q, obj.Object())) }) t.Run("attribute equal", func(t *testing.T) { @@ -110,14 +122,15 @@ func TestQ_Match(t *testing.T) { obj.SetAttributes(a) - q := New( - NewFilterEqual(k, v), - ) + fs := objectSDK.SearchFilters{} + fs.AddFilter(k, v, objectSDK.MatchStringEqual) - require.True(t, q.Match(obj.Object())) + q := New(fs) + + require.True(t, matchesQuery(q, obj.Object())) a.SetKey(k + "1") - require.False(t, q.Match(obj.Object())) + require.False(t, matchesQuery(q, obj.Object())) }) } diff --git a/pkg/services/object/search/v2/util.go b/pkg/services/object/search/v2/util.go index 8e11f7f8..d6e579a4 100644 --- a/pkg/services/object/search/v2/util.go +++ b/pkg/services/object/search/v2/util.go @@ -2,6 +2,7 @@ package searchsvc import ( "github.com/nspcc-dev/neofs-api-go/pkg/container" + objectSDK "github.com/nspcc-dev/neofs-api-go/pkg/object" "github.com/nspcc-dev/neofs-api-go/v2/object" "github.com/nspcc-dev/neofs-api-go/v2/refs" searchsvc "github.com/nspcc-dev/neofs-node/pkg/services/object/search" @@ -18,19 +19,9 @@ func toPrm(body *object.SearchRequestBody, req *object.SearchRequest) (*searchsv default: return nil, errors.Errorf("unsupported query version #%d", v) case 1: - fs := body.GetFilters() - fsV1 := make([]*queryV1.Filter, 0, len(fs)) - - for _, f := range fs { - switch mt := f.GetMatchType(); mt { - default: - return nil, errors.Errorf("unsupported match type %d in query version #%d", mt, v) - case object.MatchStringEqual: - fsV1 = append(fsV1, queryV1.NewFilterEqual(f.GetName(), f.GetValue())) - } - } - - q = queryV1.New(fsV1...) + q = queryV1.New( + objectSDK.NewSearchFiltersFromV2(body.GetFilters()), + ) } return new(searchsvc.Prm).