You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							154 lines
						
					
					
						
							4.1 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							154 lines
						
					
					
						
							4.1 KiB
						
					
					
				| package arangodb | |
| 
 | |
| import ( | |
| 	"context" | |
| 	"crypto/md5" | |
| 	"encoding/binary" | |
| 	"encoding/hex" | |
| 	"io" | |
| 	"strings" | |
| 
 | |
| 	"github.com/arangodb/go-driver" | |
| 	"github.com/seaweedfs/seaweedfs/weed/util" | |
| ) | |
| 
 | |
| // convert a string into arango-key safe hex bytes hash | |
| func hashString(dir string) string { | |
| 	h := md5.New() | |
| 	io.WriteString(h, dir) | |
| 	b := h.Sum(nil) | |
| 	return hex.EncodeToString(b) | |
| } | |
| 
 | |
| // convert slice of bytes into slice of uint64 | |
| // the first uint64 indicates the length in bytes | |
| func bytesToArray(bs []byte) []uint64 { | |
| 	out := make([]uint64, 0, 2+len(bs)/8) | |
| 	out = append(out, uint64(len(bs))) | |
| 	for len(bs)%8 != 0 { | |
| 		bs = append(bs, 0) | |
| 	} | |
| 	for i := 0; i < len(bs); i = i + 8 { | |
| 		out = append(out, binary.BigEndian.Uint64(bs[i:])) | |
| 	} | |
| 	return out | |
| } | |
| 
 | |
| // convert from slice of uint64 back to bytes | |
| // if input length is 0 or 1, will return nil | |
| func arrayToBytes(xs []uint64) []byte { | |
| 	if len(xs) < 2 { | |
| 		return nil | |
| 	} | |
| 	first := xs[0] | |
| 	out := make([]byte, len(xs)*8) // i think this can actually be len(xs)*8-8, but i dont think an extra 8 bytes hurts... | |
| 	for i := 1; i < len(xs); i = i + 1 { | |
| 		binary.BigEndian.PutUint64(out[((i-1)*8):], xs[i]) | |
| 	} | |
| 	return out[:first] | |
| } | |
| 
 | |
| // gets the collection the bucket points to from filepath | |
| func (store *ArangodbStore) extractBucketCollection(ctx context.Context, fullpath util.FullPath) (c driver.Collection, err error) { | |
| 	bucket, _ := extractBucket(fullpath) | |
| 	if bucket == "" { | |
| 		bucket = DEFAULT_COLLECTION | |
| 	} | |
| 	c, err = store.ensureBucket(ctx, bucket) | |
| 	if err != nil { | |
| 		return nil, err | |
| 	} | |
| 	return c, err | |
| } | |
| 
 | |
| // called by extractBucketCollection | |
| func extractBucket(fullpath util.FullPath) (string, string) { | |
| 	if !strings.HasPrefix(string(fullpath), BUCKET_PREFIX+"/") { | |
| 		return "", string(fullpath) | |
| 	} | |
| 	if strings.Count(string(fullpath), "/") < 3 { | |
| 		return "", string(fullpath) | |
| 	} | |
| 	bucketAndObjectKey := string(fullpath)[len(BUCKET_PREFIX+"/"):] | |
| 	t := strings.Index(bucketAndObjectKey, "/") | |
| 	bucket := bucketAndObjectKey | |
| 	shortPath := "/" | |
| 	if t > 0 { | |
| 		bucket = bucketAndObjectKey[:t] | |
| 		shortPath = string(util.FullPath(bucketAndObjectKey[t:])) | |
| 	} | |
| 	return bucket, shortPath | |
| } | |
| 
 | |
| // get bucket collection from cache. if not exist, creates the buckets collection and grab it | |
| func (store *ArangodbStore) ensureBucket(ctx context.Context, bucket string) (bc driver.Collection, err error) { | |
| 	var ok bool | |
| 	store.mu.RLock() | |
| 	bc, ok = store.buckets[bucket] | |
| 	store.mu.RUnlock() | |
| 	if ok && bc != nil { | |
| 		return bc, nil | |
| 	} | |
| 	store.mu.Lock() | |
| 	defer store.mu.Unlock() | |
| 	store.buckets[bucket], err = store.ensureCollection(ctx, bucket) | |
| 	if err != nil { | |
| 		return nil, err | |
| 	} | |
| 	return store.buckets[bucket], nil | |
| } | |
| 
 | |
| // transform to an arango compliant name | |
| func bucketToCollectionName(s string) string { | |
| 	if len(s) == 0 { | |
| 		return "" | |
| 	} | |
| 	// replace all "." with _ | |
| 	s = strings.ReplaceAll(s, ".", "_") | |
| 
 | |
| 	// if starts with number or '.' then add a special prefix | |
| 	if (s[0] >= '0' && s[0] <= '9') || (s[0] == '.' || s[0] == '_' || s[0] == '-') { | |
| 		s = "xN--" + s | |
| 	} | |
| 	return s | |
| } | |
| 
 | |
| // creates collection if not exist, ensures indices if not exist | |
| func (store *ArangodbStore) ensureCollection(ctx context.Context, bucket_name string) (c driver.Collection, err error) { | |
| 	// convert the bucket to collection name | |
| 	name := bucketToCollectionName(bucket_name) | |
| 
 | |
| 	ok, err := store.database.CollectionExists(ctx, name) | |
| 	if err != nil { | |
| 		return | |
| 	} | |
| 	if ok { | |
| 		c, err = store.database.Collection(ctx, name) | |
| 	} else { | |
| 		c, err = store.database.CreateCollection(ctx, name, &driver.CreateCollectionOptions{}) | |
| 	} | |
| 	if err != nil { | |
| 		return | |
| 	} | |
| 	// ensure indices | |
| 	if _, _, err = c.EnsurePersistentIndex(ctx, []string{"directory", "name"}, | |
| 		&driver.EnsurePersistentIndexOptions{ | |
| 			Name: "directory_name_multi", Unique: true, | |
| 		}); err != nil { | |
| 		return | |
| 	} | |
| 	if _, _, err = c.EnsurePersistentIndex(ctx, []string{"directory"}, | |
| 		&driver.EnsurePersistentIndexOptions{Name: "IDX_directory"}); err != nil { | |
| 		return | |
| 	} | |
| 	if _, _, err = c.EnsureTTLIndex(ctx, "ttl", 1, | |
| 		&driver.EnsureTTLIndexOptions{Name: "IDX_TTL"}); err != nil { | |
| 		return | |
| 	} | |
| 	if _, _, err = c.EnsurePersistentIndex(ctx, []string{"name"}, &driver.EnsurePersistentIndexOptions{ | |
| 		Name: "IDX_name", | |
| 	}); err != nil { | |
| 		return | |
| 	} | |
| 	return c, nil | |
| }
 |