github.com/cs3org/reva/v2@v2.27.7/pkg/storage/utils/indexer/index/autoincrement.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  	"sort"
    27  	"strconv"
    28  	"strings"
    29  
    30  	idxerrs "github.com/cs3org/reva/v2/pkg/storage/utils/indexer/errors"
    31  	"github.com/cs3org/reva/v2/pkg/storage/utils/indexer/option"
    32  	metadata "github.com/cs3org/reva/v2/pkg/storage/utils/metadata"
    33  )
    34  
    35  // Autoincrement are fields for an index of type autoincrement.
    36  type Autoincrement struct {
    37  	indexBy      option.IndexBy
    38  	typeName     string
    39  	filesDir     string
    40  	indexBaseDir string
    41  	indexRootDir string
    42  
    43  	bound   *option.Bound
    44  	storage metadata.Storage
    45  }
    46  
    47  // NewAutoincrementIndex instantiates a new AutoincrementIndex instance.
    48  func NewAutoincrementIndex(storage metadata.Storage, o ...option.Option) Index {
    49  	opts := &option.Options{}
    50  	for _, opt := range o {
    51  		opt(opts)
    52  	}
    53  
    54  	u := &Autoincrement{
    55  		storage:      storage,
    56  		indexBy:      opts.IndexBy,
    57  		typeName:     opts.TypeName,
    58  		filesDir:     opts.FilesDir,
    59  		bound:        opts.Bound,
    60  		indexBaseDir: path.Join(opts.Prefix, "index."+storage.Backend()),
    61  		indexRootDir: path.Join(opts.Prefix, "index."+storage.Backend(), strings.Join([]string{"autoincrement", opts.TypeName, opts.IndexBy.String()}, ".")),
    62  	}
    63  
    64  	return u
    65  }
    66  
    67  // Init initializes an autoincrement index.
    68  func (idx *Autoincrement) Init() error {
    69  	if err := idx.storage.MakeDirIfNotExist(context.Background(), idx.indexBaseDir); err != nil {
    70  		return err
    71  	}
    72  
    73  	return idx.storage.MakeDirIfNotExist(context.Background(), idx.indexRootDir)
    74  }
    75  
    76  // Lookup exact lookup by value.
    77  func (idx *Autoincrement) Lookup(v string) ([]string, error) {
    78  	return idx.LookupCtx(context.Background(), v)
    79  }
    80  
    81  // LookupCtx retieves multiple exact values and allows passing in a context
    82  func (idx *Autoincrement) LookupCtx(ctx context.Context, values ...string) ([]string, error) {
    83  	var allValues map[string]struct{}
    84  	if len(values) != 1 {
    85  		// prefetch all values with one request
    86  		entries, err := idx.storage.ReadDir(context.Background(), idx.indexRootDir)
    87  		if err != nil {
    88  			return nil, err
    89  		}
    90  		// convert known values to set
    91  		allValues = make(map[string]struct{}, len(entries))
    92  		for _, e := range entries {
    93  			allValues[path.Base(e)] = struct{}{}
    94  		}
    95  	}
    96  
    97  	// convert requested values to set
    98  	valueSet := make(map[string]struct{}, len(values))
    99  	for _, v := range values {
   100  		valueSet[v] = struct{}{}
   101  	}
   102  
   103  	var matches = []string{}
   104  	for v := range valueSet {
   105  		if _, ok := allValues[v]; ok || len(allValues) == 0 {
   106  			oldname, err := idx.storage.ResolveSymlink(context.Background(), path.Join(idx.indexRootDir, v))
   107  			if err != nil {
   108  				continue
   109  			}
   110  			matches = append(matches, oldname)
   111  		}
   112  	}
   113  
   114  	if len(matches) == 0 {
   115  		var v string
   116  		switch len(values) {
   117  		case 0:
   118  			v = "none"
   119  		case 1:
   120  			v = values[0]
   121  		default:
   122  			v = "multiple"
   123  		}
   124  		return nil, &idxerrs.NotFoundErr{TypeName: idx.typeName, IndexBy: idx.indexBy, Value: v}
   125  	}
   126  
   127  	return matches, nil
   128  }
   129  
   130  // Add a new value to the index.
   131  func (idx *Autoincrement) Add(id, v string) (string, error) {
   132  	var newName string
   133  	if v == "" {
   134  		next, err := idx.next()
   135  		if err != nil {
   136  			return "", err
   137  		}
   138  		newName = path.Join(idx.indexRootDir, strconv.Itoa(next))
   139  	} else {
   140  		newName = path.Join(idx.indexRootDir, v)
   141  	}
   142  	if err := idx.storage.CreateSymlink(context.Background(), id, newName); err != nil {
   143  		if os.IsExist(err) {
   144  			return "", &idxerrs.AlreadyExistsErr{TypeName: idx.typeName, IndexBy: idx.indexBy, Value: v}
   145  		}
   146  
   147  		return "", err
   148  	}
   149  
   150  	return newName, nil
   151  }
   152  
   153  // Remove a value v from an index.
   154  func (idx *Autoincrement) Remove(_ string, v string) error {
   155  	if v == "" {
   156  		return nil
   157  	}
   158  	searchPath := path.Join(idx.indexRootDir, v)
   159  	_, err := idx.storage.ResolveSymlink(context.Background(), searchPath)
   160  	if err != nil {
   161  		if os.IsNotExist(err) {
   162  			err = &idxerrs.NotFoundErr{TypeName: idx.typeName, IndexBy: idx.indexBy, Value: v}
   163  		}
   164  
   165  		return err
   166  	}
   167  
   168  	deletePath := path.Join(idx.indexRootDir, v)
   169  	return idx.storage.Delete(context.Background(), deletePath)
   170  }
   171  
   172  // Update index from <oldV> to <newV>.
   173  func (idx *Autoincrement) Update(id, oldV, newV string) error {
   174  	if err := idx.Remove(id, oldV); err != nil {
   175  		return err
   176  	}
   177  
   178  	_, err := idx.Add(id, newV)
   179  	return err
   180  }
   181  
   182  // Search allows for glob search on the index.
   183  func (idx *Autoincrement) Search(pattern string) ([]string, error) {
   184  	paths, err := idx.storage.ReadDir(context.Background(), idx.indexRootDir)
   185  	if err != nil {
   186  		return nil, err
   187  	}
   188  
   189  	searchPath := idx.indexRootDir
   190  	matches := make([]string, 0)
   191  	for _, p := range paths {
   192  		if found, err := filepath.Match(pattern, path.Base(p)); found {
   193  			if err != nil {
   194  				return nil, err
   195  			}
   196  
   197  			oldPath, err := idx.storage.ResolveSymlink(context.Background(), path.Join(searchPath, path.Base(p)))
   198  			if err != nil {
   199  				return nil, err
   200  			}
   201  			matches = append(matches, oldPath)
   202  		}
   203  	}
   204  
   205  	return matches, nil
   206  }
   207  
   208  // CaseInsensitive undocumented.
   209  func (idx *Autoincrement) CaseInsensitive() bool {
   210  	return false
   211  }
   212  
   213  // IndexBy undocumented.
   214  func (idx *Autoincrement) IndexBy() option.IndexBy {
   215  	return idx.indexBy
   216  }
   217  
   218  // TypeName undocumented.
   219  func (idx *Autoincrement) TypeName() string {
   220  	return idx.typeName
   221  }
   222  
   223  // FilesDir  undocumented.
   224  func (idx *Autoincrement) FilesDir() string {
   225  	return idx.filesDir
   226  }
   227  
   228  func (idx *Autoincrement) next() (int, error) {
   229  	paths, err := idx.storage.ReadDir(context.Background(), idx.indexRootDir)
   230  
   231  	if err != nil {
   232  		return -1, err
   233  	}
   234  
   235  	if len(paths) == 0 {
   236  		return int(idx.bound.Lower), nil
   237  	}
   238  
   239  	sort.Slice(paths, func(i, j int) bool {
   240  		a, _ := strconv.Atoi(path.Base(paths[i]))
   241  		b, _ := strconv.Atoi(path.Base(paths[j]))
   242  		return a < b
   243  	})
   244  
   245  	latest, err := strconv.Atoi(path.Base(paths[len(paths)-1])) // would returning a string be a better interface?
   246  	if err != nil {
   247  		return -1, err
   248  	}
   249  
   250  	if int64(latest) < idx.bound.Lower {
   251  		return int(idx.bound.Lower), nil
   252  	}
   253  
   254  	return latest + 1, nil
   255  }
   256  
   257  // Delete deletes the index folder from its storage.
   258  func (idx *Autoincrement) Delete() error {
   259  	return idx.storage.Delete(context.Background(), idx.indexRootDir)
   260  }