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 }