frostfs-sdk-go/object/search.go
Pavel Pogodaev 7d6ce3928b
All checks were successful
DCO / DCO (pull_request) Successful in 57s
Tests and linters / Tests (pull_request) Successful in 1m14s
Tests and linters / Lint (pull_request) Successful in 1m36s
[#276] Merge repo with frostfs-api-go
Signed-off-by: Pavel Pogodaev <p.pogodaev@yadro.com>
2024-10-12 12:48:38 +03:00

308 lines
7.3 KiB
Go

package object
import (
"encoding/json"
"strconv"
v2object "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/object"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version"
)
// SearchMatchType indicates match operation on specified header.
type SearchMatchType uint32
const (
MatchUnknown SearchMatchType = iota
MatchStringEqual
MatchStringNotEqual
MatchNotPresent
MatchCommonPrefix
)
func (m SearchMatchType) ToV2() v2object.MatchType {
switch m {
case MatchStringEqual:
return v2object.MatchStringEqual
case MatchStringNotEqual:
return v2object.MatchStringNotEqual
case MatchNotPresent:
return v2object.MatchNotPresent
case MatchCommonPrefix:
return v2object.MatchCommonPrefix
default:
return v2object.MatchUnknown
}
}
func SearchMatchFromV2(t v2object.MatchType) (m SearchMatchType) {
switch t {
case v2object.MatchStringEqual:
m = MatchStringEqual
case v2object.MatchStringNotEqual:
m = MatchStringNotEqual
case v2object.MatchNotPresent:
m = MatchNotPresent
case v2object.MatchCommonPrefix:
m = MatchCommonPrefix
default:
m = MatchUnknown
}
return m
}
// String returns string representation of SearchMatchType.
//
// String mapping:
// - MatchStringEqual: STRING_EQUAL;
// - MatchStringNotEqual: STRING_NOT_EQUAL;
// - MatchNotPresent: NOT_PRESENT;
// - MatchCommonPrefix: COMMON_PREFIX;
// - MatchUnknown, default: MATCH_TYPE_UNSPECIFIED.
func (m SearchMatchType) String() string {
return m.ToV2().String()
}
// FromString parses SearchMatchType from a string representation.
// It is a reverse action to String().
//
// Returns true if s was parsed successfully.
func (m *SearchMatchType) FromString(s string) bool {
var g v2object.MatchType
ok := g.FromString(s)
if ok {
*m = SearchMatchFromV2(g)
}
return ok
}
type stringEncoder interface {
EncodeToString() string
}
type SearchFilter struct {
header filterKey
value stringEncoder
op SearchMatchType
}
type staticStringer string
type filterKey struct {
typ filterKeyType
str string
}
// enumeration of reserved filter keys.
type filterKeyType int
type SearchFilters []SearchFilter
const (
_ filterKeyType = iota
fKeyVersion
fKeyObjectID
fKeyContainerID
fKeyOwnerID
fKeyCreationEpoch
fKeyPayloadLength
fKeyPayloadHash
fKeyType
fKeyHomomorphicHash
fKeyParent
fKeySplitID
fKeyPropRoot
fKeyPropPhy
fKeyECParent
)
func (k filterKey) String() string {
switch k.typ {
default:
return k.str
case fKeyVersion:
return v2object.FilterHeaderVersion
case fKeyObjectID:
return v2object.FilterHeaderObjectID
case fKeyContainerID:
return v2object.FilterHeaderContainerID
case fKeyOwnerID:
return v2object.FilterHeaderOwnerID
case fKeyCreationEpoch:
return v2object.FilterHeaderCreationEpoch
case fKeyPayloadLength:
return v2object.FilterHeaderPayloadLength
case fKeyPayloadHash:
return v2object.FilterHeaderPayloadHash
case fKeyType:
return v2object.FilterHeaderObjectType
case fKeyHomomorphicHash:
return v2object.FilterHeaderHomomorphicHash
case fKeyParent:
return v2object.FilterHeaderParent
case fKeySplitID:
return v2object.FilterHeaderSplitID
case fKeyPropRoot:
return v2object.FilterPropertyRoot
case fKeyPropPhy:
return v2object.FilterPropertyPhy
case fKeyECParent:
return v2object.FilterHeaderECParent
}
}
func (s staticStringer) EncodeToString() string {
return string(s)
}
func (f *SearchFilter) Header() string {
return f.header.String()
}
func (f *SearchFilter) Value() string {
return f.value.EncodeToString()
}
func (f *SearchFilter) Operation() SearchMatchType {
return f.op
}
func NewSearchFilters() SearchFilters {
return SearchFilters{}
}
func NewSearchFiltersFromV2(v2 []v2object.SearchFilter) SearchFilters {
filters := make(SearchFilters, 0, len(v2))
for i := range v2 {
filters.AddFilter(
v2[i].GetKey(),
v2[i].GetValue(),
SearchMatchFromV2(v2[i].GetMatchType()),
)
}
return filters
}
func (f *SearchFilters) addFilter(op SearchMatchType, keyTyp filterKeyType, key string, val stringEncoder) {
if *f == nil {
*f = make(SearchFilters, 0, 1)
}
*f = append(*f, SearchFilter{
header: filterKey{
typ: keyTyp,
str: key,
},
value: val,
op: op,
})
}
func (f *SearchFilters) AddFilter(header, value string, op SearchMatchType) {
f.addFilter(op, 0, header, staticStringer(value))
}
func (f *SearchFilters) addReservedFilter(op SearchMatchType, keyTyp filterKeyType, val stringEncoder) {
f.addFilter(op, keyTyp, "", val)
}
// addFlagFilters adds filters that works like flags: they don't need to have
// specific match type or value. They processed by FrostFS nodes by the fact
// of presence in search query. E.g.: PHY, ROOT.
func (f *SearchFilters) addFlagFilter(keyTyp filterKeyType) {
f.addFilter(MatchUnknown, keyTyp, "", staticStringer(""))
}
func (f *SearchFilters) AddObjectVersionFilter(op SearchMatchType, v version.Version) {
f.addReservedFilter(op, fKeyVersion, staticStringer(version.EncodeToString(v)))
}
func (f *SearchFilters) AddObjectContainerIDFilter(m SearchMatchType, id cid.ID) {
f.addReservedFilter(m, fKeyContainerID, id)
}
func (f *SearchFilters) AddObjectOwnerIDFilter(m SearchMatchType, id user.ID) {
f.addReservedFilter(m, fKeyOwnerID, id)
}
func (f *SearchFilters) AddNotificationEpochFilter(epoch uint64) {
f.addFilter(MatchStringEqual, 0, v2object.SysAttributeTickEpoch, staticStringer(strconv.FormatUint(epoch, 10)))
}
func (f SearchFilters) ToV2() []v2object.SearchFilter {
result := make([]v2object.SearchFilter, len(f))
for i := range f {
result[i].SetKey(f[i].header.String())
result[i].SetValue(f[i].value.EncodeToString())
result[i].SetMatchType(f[i].op.ToV2())
}
return result
}
func (f *SearchFilters) addRootFilter() {
f.addFlagFilter(fKeyPropRoot)
}
func (f *SearchFilters) AddRootFilter() {
f.addRootFilter()
}
func (f *SearchFilters) addPhyFilter() {
f.addFlagFilter(fKeyPropPhy)
}
func (f *SearchFilters) AddPhyFilter() {
f.addPhyFilter()
}
// AddParentIDFilter adds filter by parent identifier.
func (f *SearchFilters) AddParentIDFilter(m SearchMatchType, id oid.ID) {
f.addReservedFilter(m, fKeyParent, id)
}
// AddObjectIDFilter adds filter by object identifier.
func (f *SearchFilters) AddObjectIDFilter(m SearchMatchType, id oid.ID) {
f.addReservedFilter(m, fKeyObjectID, id)
}
func (f *SearchFilters) AddSplitIDFilter(m SearchMatchType, id *SplitID) {
f.addReservedFilter(m, fKeySplitID, staticStringer(id.String()))
}
func (f *SearchFilters) AddECParentFilter(m SearchMatchType, parentID oid.ID) {
f.addReservedFilter(m, fKeyECParent, staticStringer(parentID.String()))
}
// AddTypeFilter adds filter by object type.
func (f *SearchFilters) AddTypeFilter(m SearchMatchType, typ Type) {
f.addReservedFilter(m, fKeyType, staticStringer(typ.String()))
}
// MarshalJSON encodes SearchFilters to protobuf JSON format.
func (f *SearchFilters) MarshalJSON() ([]byte, error) {
return json.Marshal(f.ToV2())
}
// UnmarshalJSON decodes SearchFilters from protobuf JSON format.
func (f *SearchFilters) UnmarshalJSON(data []byte) error {
var fsV2 []v2object.SearchFilter
if err := json.Unmarshal(data, &fsV2); err != nil {
return err
}
*f = NewSearchFiltersFromV2(fsV2)
return nil
}