302 lines
7.0 KiB
Go
302 lines
7.0 KiB
Go
package object
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strconv"
|
|
|
|
v2object "github.com/TrueCloudLab/frostfs-api-go/v2/object"
|
|
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
|
|
oid "github.com/TrueCloudLab/frostfs-sdk-go/object/id"
|
|
"github.com/TrueCloudLab/frostfs-sdk-go/user"
|
|
"github.com/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
|
|
)
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
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()))
|
|
}
|
|
|
|
// 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
|
|
}
|