github.com/cs3org/reva/v2@v2.27.7/pkg/storage/utils/indexer/index/non_unique.go (about)

     1  // Copyright 2018-2022 CERN
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // In applying this license, CERN does not waive the privileges and immunities
    16  // granted to it by virtue of its status as an Intergovernmental Organization
    17  // or submit itself to any jurisdiction.
    18  
    19  package index
    20  
    21  import (
    22  	"context"
    23  	"os"
    24  	"path"
    25  	"path/filepath"
    26  	"strings"
    27  
    28  	idxerrs "github.com/cs3org/reva/v2/pkg/storage/utils/indexer/errors"
    29  	"github.com/cs3org/reva/v2/pkg/storage/utils/indexer/option"
    30  	metadata "github.com/cs3org/reva/v2/pkg/storage/utils/metadata"
    31  )
    32  
    33  // NonUnique are fields for an index of type non_unique.
    34  type NonUnique struct {
    35  	caseInsensitive bool
    36  	indexBy         option.IndexBy
    37  	typeName        string
    38  	filesDir        string
    39  	indexBaseDir    string
    40  	indexRootDir    string
    41  
    42  	storage metadata.Storage
    43  }
    44  
    45  // NewNonUniqueIndexWithOptions instantiates a new NonUniqueIndex instance.
    46  // /tmp/ocis/accounts/index.cs3/Pets/Bro*
    47  // ├── Brown/
    48  // │   └── rebef-123 -> /tmp/testfiles-395764020/pets/rebef-123
    49  // ├── Green/
    50  // │    ├── goefe-789 -> /tmp/testfiles-395764020/pets/goefe-789
    51  // │    └── xadaf-189 -> /tmp/testfiles-395764020/pets/xadaf-189
    52  // └── White/
    53  // |    └── wefwe-456 -> /tmp/testfiles-395764020/pets/wefwe-456
    54  func NewNonUniqueIndexWithOptions(storage metadata.Storage, o ...option.Option) Index {
    55  	opts := &option.Options{}
    56  	for _, opt := range o {
    57  		opt(opts)
    58  	}
    59  
    60  	return &NonUnique{
    61  		storage:         storage,
    62  		caseInsensitive: opts.CaseInsensitive,
    63  		indexBy:         opts.IndexBy,
    64  		typeName:        opts.TypeName,
    65  		filesDir:        opts.FilesDir,
    66  		indexBaseDir:    path.Join(opts.Prefix, "index."+storage.Backend()),
    67  		indexRootDir:    path.Join(opts.Prefix, "index."+storage.Backend(), strings.Join([]string{"non_unique", opts.TypeName, opts.IndexBy.String()}, ".")),
    68  	}
    69  }
    70  
    71  // Init initializes a non_unique index.
    72  func (idx *NonUnique) Init() error {
    73  	if err := idx.storage.MakeDirIfNotExist(context.Background(), idx.indexBaseDir); err != nil {
    74  		return err
    75  	}
    76  
    77  	return idx.storage.MakeDirIfNotExist(context.Background(), idx.indexRootDir)
    78  }
    79  
    80  // Lookup exact lookup by value.
    81  func (idx *NonUnique) Lookup(v string) ([]string, error) {
    82  	return idx.LookupCtx(context.Background(), v)
    83  }
    84  
    85  // LookupCtx retieves multiple exact values and allows passing in a context
    86  func (idx *NonUnique) LookupCtx(ctx context.Context, values ...string) ([]string, error) {
    87  	// prefetch all values with one request
    88  	entries, err := idx.storage.ReadDir(context.Background(), idx.indexRootDir)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  	// convert known values to set
    93  	allValues := make(map[string]struct{}, len(entries))
    94  	for _, e := range entries {
    95  		allValues[path.Base(e)] = struct{}{}
    96  	}
    97  
    98  	// convert requested values to set
    99  	valueSet := make(map[string]struct{}, len(values))
   100  	if idx.caseInsensitive {
   101  		for _, v := range values {
   102  			valueSet[strings.ToLower(v)] = struct{}{}
   103  		}
   104  	} else {
   105  		for _, v := range values {
   106  			valueSet[v] = struct{}{}
   107  		}
   108  	}
   109  
   110  	var matches = map[string]struct{}{}
   111  	for v := range valueSet {
   112  		if _, ok := allValues[v]; ok {
   113  			children, err := idx.storage.ReadDir(context.Background(), filepath.Join(idx.indexRootDir, v))
   114  			if err != nil {
   115  				continue
   116  			}
   117  			for _, c := range children {
   118  				matches[path.Base(c)] = struct{}{}
   119  			}
   120  		}
   121  	}
   122  
   123  	if len(matches) == 0 {
   124  		var v string
   125  		switch len(values) {
   126  		case 0:
   127  			v = "none"
   128  		case 1:
   129  			v = values[0]
   130  		default:
   131  			v = "multiple"
   132  		}
   133  		return nil, &idxerrs.NotFoundErr{TypeName: idx.typeName, IndexBy: idx.indexBy, Value: v}
   134  	}
   135  
   136  	ret := make([]string, 0, len(matches))
   137  	for m := range matches {
   138  		ret = append(ret, m)
   139  	}
   140  	return ret, nil
   141  }
   142  
   143  // Add a new value to the index.
   144  func (idx *NonUnique) Add(id, v string) (string, error) {
   145  	if v == "" {
   146  		return "", nil
   147  	}
   148  	if idx.caseInsensitive {
   149  		v = strings.ToLower(v)
   150  	}
   151  
   152  	newName := path.Join(idx.indexRootDir, v)
   153  	if err := idx.storage.MakeDirIfNotExist(context.Background(), newName); err != nil {
   154  		return "", err
   155  	}
   156  
   157  	if err := idx.storage.CreateSymlink(context.Background(), id, path.Join(newName, id)); err != nil {
   158  		if os.IsExist(err) {
   159  			return "", &idxerrs.AlreadyExistsErr{TypeName: idx.typeName, IndexBy: idx.indexBy, Value: v}
   160  		}
   161  
   162  		return "", err
   163  	}
   164  
   165  	return newName, nil
   166  }
   167  
   168  // Remove a value v from an index.
   169  func (idx *NonUnique) Remove(id string, v string) error {
   170  	if v == "" {
   171  		return nil
   172  	}
   173  	if idx.caseInsensitive {
   174  		v = strings.ToLower(v)
   175  	}
   176  
   177  	deletePath := path.Join(idx.indexRootDir, v, id)
   178  	err := idx.storage.Delete(context.Background(), deletePath)
   179  	if err != nil {
   180  		return err
   181  	}
   182  
   183  	toStat := path.Join(idx.indexRootDir, v)
   184  	infos, err := idx.storage.ReadDir(context.Background(), toStat)
   185  	if err != nil {
   186  		return err
   187  	}
   188  
   189  	if len(infos) == 0 {
   190  		deletePath = path.Join(idx.indexRootDir, v)
   191  		err := idx.storage.Delete(context.Background(), deletePath)
   192  		if err != nil {
   193  			return err
   194  		}
   195  	}
   196  
   197  	return nil
   198  }
   199  
   200  // Update index from <oldV> to <newV>.
   201  func (idx *NonUnique) Update(id, oldV, newV string) error {
   202  	if idx.caseInsensitive {
   203  		oldV = strings.ToLower(oldV)
   204  		newV = strings.ToLower(newV)
   205  	}
   206  
   207  	if err := idx.Remove(id, oldV); err != nil {
   208  		return err
   209  	}
   210  
   211  	if _, err := idx.Add(id, newV); err != nil {
   212  		return err
   213  	}
   214  
   215  	return nil
   216  }
   217  
   218  // Search allows for glob search on the index.
   219  func (idx *NonUnique) Search(pattern string) ([]string, error) {
   220  	if idx.caseInsensitive {
   221  		pattern = strings.ToLower(pattern)
   222  	}
   223  
   224  	foldersMatched := make([]string, 0)
   225  	matches := make([]string, 0)
   226  	paths, err := idx.storage.ReadDir(context.Background(), idx.indexRootDir)
   227  
   228  	if err != nil {
   229  		return nil, err
   230  	}
   231  
   232  	for _, p := range paths {
   233  		if found, err := filepath.Match(pattern, path.Base(p)); found {
   234  			if err != nil {
   235  				return nil, err
   236  			}
   237  
   238  			foldersMatched = append(foldersMatched, p)
   239  		}
   240  	}
   241  
   242  	for i := range foldersMatched {
   243  		paths, _ := idx.storage.ReadDir(context.Background(), foldersMatched[i])
   244  
   245  		for _, p := range paths {
   246  			matches = append(matches, path.Base(p))
   247  		}
   248  	}
   249  
   250  	if len(matches) == 0 {
   251  		return nil, &idxerrs.NotFoundErr{TypeName: idx.typeName, IndexBy: idx.indexBy, Value: pattern}
   252  	}
   253  
   254  	return matches, nil
   255  }
   256  
   257  // CaseInsensitive undocumented.
   258  func (idx *NonUnique) CaseInsensitive() bool {
   259  	return idx.caseInsensitive
   260  }
   261  
   262  // IndexBy undocumented.
   263  func (idx *NonUnique) IndexBy() option.IndexBy {
   264  	return idx.indexBy
   265  }
   266  
   267  // TypeName undocumented.
   268  func (idx *NonUnique) TypeName() string {
   269  	return idx.typeName
   270  }
   271  
   272  // FilesDir  undocumented.
   273  func (idx *NonUnique) FilesDir() string {
   274  	return idx.filesDir
   275  }
   276  
   277  // Delete deletes the index folder from its storage.
   278  func (idx *NonUnique) Delete() error {
   279  	return idx.storage.Delete(context.Background(), idx.indexRootDir)
   280  }