git.frostfs.info/TrueCloudLab/frostfs-sdk-go@v0.0.0-20241022124111-5361f0ecebd3/object/search.go (about)

     1  package object
     2  
     3  import (
     4  	"encoding/json"
     5  	"strconv"
     6  
     7  	v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
     8  	cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
     9  	oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
    10  	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
    11  	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version"
    12  )
    13  
    14  // SearchMatchType indicates match operation on specified header.
    15  type SearchMatchType uint32
    16  
    17  const (
    18  	MatchUnknown SearchMatchType = iota
    19  	MatchStringEqual
    20  	MatchStringNotEqual
    21  	MatchNotPresent
    22  	MatchCommonPrefix
    23  )
    24  
    25  func (m SearchMatchType) ToV2() v2object.MatchType {
    26  	switch m {
    27  	case MatchStringEqual:
    28  		return v2object.MatchStringEqual
    29  	case MatchStringNotEqual:
    30  		return v2object.MatchStringNotEqual
    31  	case MatchNotPresent:
    32  		return v2object.MatchNotPresent
    33  	case MatchCommonPrefix:
    34  		return v2object.MatchCommonPrefix
    35  	default:
    36  		return v2object.MatchUnknown
    37  	}
    38  }
    39  
    40  func SearchMatchFromV2(t v2object.MatchType) (m SearchMatchType) {
    41  	switch t {
    42  	case v2object.MatchStringEqual:
    43  		m = MatchStringEqual
    44  	case v2object.MatchStringNotEqual:
    45  		m = MatchStringNotEqual
    46  	case v2object.MatchNotPresent:
    47  		m = MatchNotPresent
    48  	case v2object.MatchCommonPrefix:
    49  		m = MatchCommonPrefix
    50  	default:
    51  		m = MatchUnknown
    52  	}
    53  
    54  	return m
    55  }
    56  
    57  // String returns string representation of SearchMatchType.
    58  //
    59  // String mapping:
    60  //   - MatchStringEqual: STRING_EQUAL;
    61  //   - MatchStringNotEqual: STRING_NOT_EQUAL;
    62  //   - MatchNotPresent: NOT_PRESENT;
    63  //   - MatchCommonPrefix: COMMON_PREFIX;
    64  //   - MatchUnknown, default: MATCH_TYPE_UNSPECIFIED.
    65  func (m SearchMatchType) String() string {
    66  	return m.ToV2().String()
    67  }
    68  
    69  // FromString parses SearchMatchType from a string representation.
    70  // It is a reverse action to String().
    71  //
    72  // Returns true if s was parsed successfully.
    73  func (m *SearchMatchType) FromString(s string) bool {
    74  	var g v2object.MatchType
    75  
    76  	ok := g.FromString(s)
    77  
    78  	if ok {
    79  		*m = SearchMatchFromV2(g)
    80  	}
    81  
    82  	return ok
    83  }
    84  
    85  type stringEncoder interface {
    86  	EncodeToString() string
    87  }
    88  
    89  type SearchFilter struct {
    90  	header filterKey
    91  	value  stringEncoder
    92  	op     SearchMatchType
    93  }
    94  
    95  type staticStringer string
    96  
    97  type filterKey struct {
    98  	typ filterKeyType
    99  
   100  	str string
   101  }
   102  
   103  // enumeration of reserved filter keys.
   104  type filterKeyType int
   105  
   106  type SearchFilters []SearchFilter
   107  
   108  const (
   109  	_ filterKeyType = iota
   110  	fKeyVersion
   111  	fKeyObjectID
   112  	fKeyContainerID
   113  	fKeyOwnerID
   114  	fKeyCreationEpoch
   115  	fKeyPayloadLength
   116  	fKeyPayloadHash
   117  	fKeyType
   118  	fKeyHomomorphicHash
   119  	fKeyParent
   120  	fKeySplitID
   121  	fKeyPropRoot
   122  	fKeyPropPhy
   123  	fKeyECParent
   124  )
   125  
   126  func (k filterKey) String() string {
   127  	switch k.typ {
   128  	default:
   129  		return k.str
   130  	case fKeyVersion:
   131  		return v2object.FilterHeaderVersion
   132  	case fKeyObjectID:
   133  		return v2object.FilterHeaderObjectID
   134  	case fKeyContainerID:
   135  		return v2object.FilterHeaderContainerID
   136  	case fKeyOwnerID:
   137  		return v2object.FilterHeaderOwnerID
   138  	case fKeyCreationEpoch:
   139  		return v2object.FilterHeaderCreationEpoch
   140  	case fKeyPayloadLength:
   141  		return v2object.FilterHeaderPayloadLength
   142  	case fKeyPayloadHash:
   143  		return v2object.FilterHeaderPayloadHash
   144  	case fKeyType:
   145  		return v2object.FilterHeaderObjectType
   146  	case fKeyHomomorphicHash:
   147  		return v2object.FilterHeaderHomomorphicHash
   148  	case fKeyParent:
   149  		return v2object.FilterHeaderParent
   150  	case fKeySplitID:
   151  		return v2object.FilterHeaderSplitID
   152  	case fKeyPropRoot:
   153  		return v2object.FilterPropertyRoot
   154  	case fKeyPropPhy:
   155  		return v2object.FilterPropertyPhy
   156  	case fKeyECParent:
   157  		return v2object.FilterHeaderECParent
   158  	}
   159  }
   160  
   161  func (s staticStringer) EncodeToString() string {
   162  	return string(s)
   163  }
   164  
   165  func (f *SearchFilter) Header() string {
   166  	return f.header.String()
   167  }
   168  
   169  func (f *SearchFilter) Value() string {
   170  	return f.value.EncodeToString()
   171  }
   172  
   173  func (f *SearchFilter) Operation() SearchMatchType {
   174  	return f.op
   175  }
   176  
   177  func NewSearchFilters() SearchFilters {
   178  	return SearchFilters{}
   179  }
   180  
   181  func NewSearchFiltersFromV2(v2 []v2object.SearchFilter) SearchFilters {
   182  	filters := make(SearchFilters, 0, len(v2))
   183  
   184  	for i := range v2 {
   185  		filters.AddFilter(
   186  			v2[i].GetKey(),
   187  			v2[i].GetValue(),
   188  			SearchMatchFromV2(v2[i].GetMatchType()),
   189  		)
   190  	}
   191  
   192  	return filters
   193  }
   194  
   195  func (f *SearchFilters) addFilter(op SearchMatchType, keyTyp filterKeyType, key string, val stringEncoder) {
   196  	if *f == nil {
   197  		*f = make(SearchFilters, 0, 1)
   198  	}
   199  
   200  	*f = append(*f, SearchFilter{
   201  		header: filterKey{
   202  			typ: keyTyp,
   203  			str: key,
   204  		},
   205  		value: val,
   206  		op:    op,
   207  	})
   208  }
   209  
   210  func (f *SearchFilters) AddFilter(header, value string, op SearchMatchType) {
   211  	f.addFilter(op, 0, header, staticStringer(value))
   212  }
   213  
   214  func (f *SearchFilters) addReservedFilter(op SearchMatchType, keyTyp filterKeyType, val stringEncoder) {
   215  	f.addFilter(op, keyTyp, "", val)
   216  }
   217  
   218  // addFlagFilters adds filters that works like flags: they don't need to have
   219  // specific match type or value. They processed by FrostFS nodes by the fact
   220  // of presence in search query. E.g.: PHY, ROOT.
   221  func (f *SearchFilters) addFlagFilter(keyTyp filterKeyType) {
   222  	f.addFilter(MatchUnknown, keyTyp, "", staticStringer(""))
   223  }
   224  
   225  func (f *SearchFilters) AddObjectVersionFilter(op SearchMatchType, v version.Version) {
   226  	f.addReservedFilter(op, fKeyVersion, staticStringer(version.EncodeToString(v)))
   227  }
   228  
   229  func (f *SearchFilters) AddObjectContainerIDFilter(m SearchMatchType, id cid.ID) {
   230  	f.addReservedFilter(m, fKeyContainerID, id)
   231  }
   232  
   233  func (f *SearchFilters) AddObjectOwnerIDFilter(m SearchMatchType, id user.ID) {
   234  	f.addReservedFilter(m, fKeyOwnerID, id)
   235  }
   236  
   237  func (f *SearchFilters) AddNotificationEpochFilter(epoch uint64) {
   238  	f.addFilter(MatchStringEqual, 0, v2object.SysAttributeTickEpoch, staticStringer(strconv.FormatUint(epoch, 10)))
   239  }
   240  
   241  func (f SearchFilters) ToV2() []v2object.SearchFilter {
   242  	result := make([]v2object.SearchFilter, len(f))
   243  
   244  	for i := range f {
   245  		result[i].SetKey(f[i].header.String())
   246  		result[i].SetValue(f[i].value.EncodeToString())
   247  		result[i].SetMatchType(f[i].op.ToV2())
   248  	}
   249  
   250  	return result
   251  }
   252  
   253  func (f *SearchFilters) addRootFilter() {
   254  	f.addFlagFilter(fKeyPropRoot)
   255  }
   256  
   257  func (f *SearchFilters) AddRootFilter() {
   258  	f.addRootFilter()
   259  }
   260  
   261  func (f *SearchFilters) addPhyFilter() {
   262  	f.addFlagFilter(fKeyPropPhy)
   263  }
   264  
   265  func (f *SearchFilters) AddPhyFilter() {
   266  	f.addPhyFilter()
   267  }
   268  
   269  // AddParentIDFilter adds filter by parent identifier.
   270  func (f *SearchFilters) AddParentIDFilter(m SearchMatchType, id oid.ID) {
   271  	f.addReservedFilter(m, fKeyParent, id)
   272  }
   273  
   274  // AddObjectIDFilter adds filter by object identifier.
   275  func (f *SearchFilters) AddObjectIDFilter(m SearchMatchType, id oid.ID) {
   276  	f.addReservedFilter(m, fKeyObjectID, id)
   277  }
   278  
   279  func (f *SearchFilters) AddSplitIDFilter(m SearchMatchType, id *SplitID) {
   280  	f.addReservedFilter(m, fKeySplitID, staticStringer(id.String()))
   281  }
   282  
   283  func (f *SearchFilters) AddECParentFilter(m SearchMatchType, parentID oid.ID) {
   284  	f.addReservedFilter(m, fKeyECParent, staticStringer(parentID.String()))
   285  }
   286  
   287  // AddTypeFilter adds filter by object type.
   288  func (f *SearchFilters) AddTypeFilter(m SearchMatchType, typ Type) {
   289  	f.addReservedFilter(m, fKeyType, staticStringer(typ.String()))
   290  }
   291  
   292  // MarshalJSON encodes SearchFilters to protobuf JSON format.
   293  func (f *SearchFilters) MarshalJSON() ([]byte, error) {
   294  	return json.Marshal(f.ToV2())
   295  }
   296  
   297  // UnmarshalJSON decodes SearchFilters from protobuf JSON format.
   298  func (f *SearchFilters) UnmarshalJSON(data []byte) error {
   299  	var fsV2 []v2object.SearchFilter
   300  
   301  	if err := json.Unmarshal(data, &fsV2); err != nil {
   302  		return err
   303  	}
   304  
   305  	*f = NewSearchFiltersFromV2(fsV2)
   306  
   307  	return nil
   308  }