diff --git a/pkg/local_object_storage/metabase/db.go b/pkg/local_object_storage/metabase/db.go index bee091a6b1..29a3b7f75f 100644 --- a/pkg/local_object_storage/metabase/db.go +++ b/pkg/local_object_storage/metabase/db.go @@ -6,6 +6,7 @@ import ( "io/fs" "os" "strconv" + "strings" "github.com/nspcc-dev/neofs-api-go/pkg/object" v2object "github.com/nspcc-dev/neofs-api-go/v2/object" @@ -58,6 +59,7 @@ func New(opts ...Option) *DB { object.MatchUnknown: unknownMatcher, object.MatchStringEqual: stringEqualMatcher, object.MatchStringNotEqual: stringNotEqualMatcher, + object.MatchCommonPrefix: stringCommonPrefixMatcher, }, } } @@ -81,6 +83,10 @@ func stringNotEqualMatcher(key string, objVal []byte, filterVal string) bool { return stringifyValue(key, objVal) != filterVal } +func stringCommonPrefixMatcher(key string, objVal []byte, filterVal string) bool { + return strings.HasPrefix(stringifyValue(key, objVal), filterVal) +} + func unknownMatcher(_ string, _ []byte, _ string) bool { return false } diff --git a/pkg/local_object_storage/metabase/select.go b/pkg/local_object_storage/metabase/select.go index 0a70346544..6325caed8a 100644 --- a/pkg/local_object_storage/metabase/select.go +++ b/pkg/local_object_storage/metabase/select.go @@ -258,6 +258,12 @@ func bucketNamesForType(cid *cid.ID, mType object.SearchMatchType, typeVal strin } case object.MatchStringEqual: appendNames(typeVal) + case object.MatchCommonPrefix: + for key := range mBucketNaming { + if strings.HasPrefix(key, typeVal) { + appendNames(key) + } + } } return diff --git a/pkg/local_object_storage/metabase/select_test.go b/pkg/local_object_storage/metabase/select_test.go index 1d8bc9a33e..e8511983a6 100644 --- a/pkg/local_object_storage/metabase/select_test.go +++ b/pkg/local_object_storage/metabase/select_test.go @@ -38,6 +38,24 @@ func TestDB_SelectUserAttributes(t *testing.T) { err = putBig(db, raw3.Object()) require.NoError(t, err) + raw4 := generateRawObjectWithCID(t, cid) + addAttribute(raw4, "path", "test/1/2") + + err = putBig(db, raw4.Object()) + require.NoError(t, err) + + raw5 := generateRawObjectWithCID(t, cid) + addAttribute(raw5, "path", "test/1/3") + + err = putBig(db, raw5.Object()) + require.NoError(t, err) + + raw6 := generateRawObjectWithCID(t, cid) + addAttribute(raw6, "path", "test/2/3") + + err = putBig(db, raw6.Object()) + require.NoError(t, err) + fs := objectSDK.SearchFilters{} fs.AddFilter("foo", "bar", objectSDK.MatchStringEqual) testSelect(t, db, cid, fs, @@ -63,17 +81,31 @@ func TestDB_SelectUserAttributes(t *testing.T) { fs = objectSDK.SearchFilters{} fs.AddFilter("foo", "", objectSDK.MatchNotPresent) - testSelect(t, db, cid, fs, raw3.Object().Address()) + testSelect(t, db, cid, fs, + raw3.Object().Address(), + raw4.Object().Address(), + raw5.Object().Address(), + raw6.Object().Address(), + ) fs = objectSDK.SearchFilters{} fs.AddFilter("a", "", objectSDK.MatchNotPresent) - testSelect(t, db, cid, fs, raw1.Object().Address(), raw2.Object().Address()) + testSelect(t, db, cid, fs, + raw1.Object().Address(), + raw2.Object().Address(), + raw4.Object().Address(), + raw5.Object().Address(), + raw6.Object().Address(), + ) fs = objectSDK.SearchFilters{} testSelect(t, db, cid, fs, raw1.Object().Address(), raw2.Object().Address(), raw3.Object().Address(), + raw4.Object().Address(), + raw5.Object().Address(), + raw6.Object().Address(), ) fs = objectSDK.SearchFilters{} @@ -82,6 +114,24 @@ func TestDB_SelectUserAttributes(t *testing.T) { raw1.Object().Address(), raw2.Object().Address(), raw3.Object().Address(), + raw4.Object().Address(), + raw5.Object().Address(), + raw6.Object().Address(), + ) + + fs = objectSDK.SearchFilters{} + fs.AddFilter("path", "test", objectSDK.MatchCommonPrefix) + testSelect(t, db, cid, fs, + raw4.Object().Address(), + raw5.Object().Address(), + raw6.Object().Address(), + ) + + fs = objectSDK.SearchFilters{} + fs.AddFilter("path", "test/1", objectSDK.MatchCommonPrefix) + testSelect(t, db, cid, fs, + raw4.Object().Address(), + raw5.Object().Address(), ) }