Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,16 +236,18 @@ type ShardID string

// InventoryIndex contains all configuration parameters of a single index of a collection in a database inventory.
type InventoryIndex struct {
ID string `json:"id,omitempty"`
Type string `json:"type,omitempty"`
Fields []string `json:"fields,omitempty"`
Unique bool `json:"unique"`
Sparse bool `json:"sparse"`
Deduplicate bool `json:"deduplicate"`
MinLength int `json:"minLength,omitempty"`
GeoJSON bool `json:"geoJson,omitempty"`
Name string `json:"name,omitempty"`
ExpireAfter int `json:"expireAfter,omitempty"`
ID string `json:"id,omitempty"`
Type string `json:"type,omitempty"`
Fields []string `json:"fields,omitempty"`
Unique bool `json:"unique"`
Sparse bool `json:"sparse"`
Deduplicate bool `json:"deduplicate"`
MinLength int `json:"minLength,omitempty"`
GeoJSON bool `json:"geoJson,omitempty"`
Name string `json:"name,omitempty"`
ExpireAfter int `json:"expireAfter,omitempty"`
Estimates bool `json:"estimates,omitempty"`
FieldValueTypes string `json:"fieldValueTypes,omitempty"`
}

// FieldsEqual returns true when the given fields list equals the
Expand Down
20 changes: 20 additions & 0 deletions collection_indexes.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ type CollectionIndexes interface {
// EnsureTTLIndex creates a TLL collection, if it does not already exist.
// The index is returned, together with a boolean indicating if the index was newly created (true) or pre-existing (false).
EnsureTTLIndex(ctx context.Context, field string, expireAfter int, options *EnsureTTLIndexOptions) (Index, bool, error)

// EnsureZKDIndex creates a ZKD multi-dimensional index for the collection, if it does not already exist.
// Note that zkd indexes are an experimental feature in ArangoDB 3.9.
EnsureZKDIndex(ctx context.Context, fields []string, options *EnsureZKDIndexOptions) (Index, bool, error)
}

// EnsureFullTextIndexOptions contains specific options for creating a full text index.
Expand Down Expand Up @@ -157,3 +161,19 @@ type EnsureTTLIndexOptions struct {
// Estimates determines if the to-be-created index should maintain selectivity estimates or not.
Estimates *bool
}

// EnsureZKDIndexOptions provides specific options for creating a ZKD index
type EnsureZKDIndexOptions struct {
// If true, then create a unique index.
Unique bool
// InBackground if true will not hold an exclusive collection lock for the entire index creation period (rocksdb only).
InBackground bool
// Name optional user defined name used for hints in AQL queries
Name string
// fieldValueTypes is required and the only allowed value is "double". Future extensions of the index will allow other types.
FieldValueTypes string

// If true, then create a sparse index.
// TODO: The sparse property is not supported yet
// Sparse bool
}
62 changes: 40 additions & 22 deletions collection_indexes_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,28 +28,23 @@ import (
)

type indexData struct {
ID string `json:"id,omitempty"`
Type string `json:"type"`
Fields []string `json:"fields,omitempty"`
Unique *bool `json:"unique,omitempty"`
Deduplicate *bool `json:"deduplicate,omitempty"`
Sparse *bool `json:"sparse,omitempty"`
GeoJSON *bool `json:"geoJson,omitempty"`
InBackground *bool `json:"inBackground,omitempty"`
Estimates *bool `json:"estimates,omitempty"`
MinLength int `json:"minLength,omitempty"`
ExpireAfter int `json:"expireAfter,omitempty"`
Name string `json:"name,omitempty"`
}

type genericIndexData struct {
ID string `json:"id,omitempty"`
Type string `json:"type"`
Name string `json:"name,omitempty"`
ID string `json:"id,omitempty"`
Type string `json:"type"`
Fields []string `json:"fields,omitempty"`
Unique *bool `json:"unique,omitempty"`
Deduplicate *bool `json:"deduplicate,omitempty"`
Sparse *bool `json:"sparse,omitempty"`
GeoJSON *bool `json:"geoJson,omitempty"`
InBackground *bool `json:"inBackground,omitempty"`
Estimates *bool `json:"estimates,omitempty"`
MinLength int `json:"minLength,omitempty"`
ExpireAfter int `json:"expireAfter,omitempty"`
Name string `json:"name,omitempty"`
FieldValueTypes string `json:"fieldValueTypes,omitempty"`
}

type indexListResponse struct {
Indexes []genericIndexData `json:"indexes,omitempty"`
Indexes []indexData `json:"indexes,omitempty"`
}

// Index opens a connection to an existing index within the collection.
Expand All @@ -70,7 +65,7 @@ func (c *collection) Index(ctx context.Context, name string) (Index, error) {
if err := resp.ParseBody("", &data); err != nil {
return nil, WithStack(err)
}
idx, err := newIndex(data.ID, data.Type, data.Name, c)
idx, err := newIndex(data, c)
if err != nil {
return nil, WithStack(err)
}
Expand Down Expand Up @@ -116,7 +111,7 @@ func (c *collection) Indexes(ctx context.Context) ([]Index, error) {
}
result := make([]Index, 0, len(data.Indexes))
for _, x := range data.Indexes {
idx, err := newIndex(x.ID, x.Type, x.Name, c)
idx, err := newIndex(x, c)
if err != nil {
return nil, WithStack(err)
}
Expand Down Expand Up @@ -269,6 +264,29 @@ func (c *collection) EnsureTTLIndex(ctx context.Context, field string, expireAft
return idx, created, nil
}

// EnsureZKDIndex creates a ZKD index in the collection, if it does not already exist.
// Fields is a slice of attribute paths.
// The index is returned, together with a boolean indicating if the index was newly created (true) or pre-existing (false).
func (c *collection) EnsureZKDIndex(ctx context.Context, fields []string, options *EnsureZKDIndexOptions) (Index, bool, error) {
input := indexData{
Type: string(ZKDIndex),
Fields: fields,
// fieldValueTypes is required and the only allowed value is "double". Future extensions of the index will allow other types.
FieldValueTypes: "double",
}
if options != nil {
input.InBackground = &options.InBackground
input.Name = options.Name
input.Unique = &options.Unique
//input.Sparse = &options.Sparse
}
idx, created, err := c.ensureIndex(ctx, input)
if err != nil {
return nil, false, WithStack(err)
}
return idx, created, nil
}

// ensureIndex creates a persistent index in the collection, if it does not already exist.
// Fields is a slice of attribute paths.
// The index is returned, together with a boolean indicating if the index was newly created (true) or pre-existing (false).
Expand All @@ -293,7 +311,7 @@ func (c *collection) ensureIndex(ctx context.Context, options indexData) (Index,
if err := resp.ParseBody("", &data); err != nil {
return nil, false, WithStack(err)
}
idx, err := newIndex(data.ID, data.Type, data.Name, c)
idx, err := newIndex(data, c)
if err != nil {
return nil, false, WithStack(err)
}
Expand Down
11 changes: 11 additions & 0 deletions edge_collection_indexes_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,14 @@ func (c *edgeCollection) EnsureTTLIndex(ctx context.Context, field string, expir
}
return result, created, nil
}

// EnsureZKDIndex creates a ZKD index in the collection, if it does not already exist.
// Fields is a slice of attribute paths.
// The index is returned, together with a boolean indicating if the index was newly created (true) or pre-existing (false).
func (c *edgeCollection) EnsureZKDIndex(ctx context.Context, fields []string, options *EnsureZKDIndexOptions) (Index, bool, error) {
result, created, err := c.rawCollection().EnsureZKDIndex(ctx, fields, options)
if err != nil {
return nil, false, WithStack(err)
}
return result, created, nil
}
32 changes: 30 additions & 2 deletions index.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const (
GeoIndex = IndexType("geo")
EdgeIndex = IndexType("edge")
TTLIndex = IndexType("ttl")
ZKDIndex = IndexType("zkd")
)

// Index provides access to a single index in a single collection.
Expand All @@ -45,11 +46,11 @@ type Index interface {
// the require a index _name_.
Name() string

// ID returns the ID of the index. Effectivly this is `<collection-name>/<index.Name()>`.
// ID returns the ID of the index. Effectively this is `<collection-name>/<index.Name()>`.
ID() string

// UserName returns the user provided name of the index or empty string if non is provided. This _name_
// is used in querys to provides hints for the optimizer about preferred indexes.
// is used in query to provide hints for the optimizer about preferred indexes.
UserName() string

// Type returns the type of the index
Expand All @@ -58,4 +59,31 @@ type Index interface {
// Remove removes the entire index.
// If the index does not exist, a NotFoundError is returned.
Remove(ctx context.Context) error

// Fields returns a list of attributes of this index.
Fields() []string

// Unique returns if this index is unique.
Unique() bool

// Deduplicate returns deduplicate setting of this index.
Deduplicate() bool

// Sparse returns if this is a sparse index or not.
Sparse() bool

// GeoJSON returns if geo json was set for this index or not.
GeoJSON() bool

// InBackground if true will not hold an exclusive collection lock for the entire index creation period (rocksdb only).
InBackground() bool

// Estimates determines if the to-be-created index should maintain selectivity estimates or not.
Estimates() bool

// MinLength returns min length for this index if set.
MinLength() int

// ExpireAfter returns an expire after for this index if set.
ExpireAfter() int
}
87 changes: 75 additions & 12 deletions index_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,30 +47,31 @@ func indexStringToType(indexTypeString string) (IndexType, error) {
return EdgeIndex, nil
case string(TTLIndex):
return TTLIndex, nil
case string(ZKDIndex):
return ZKDIndex, nil
default:
return "", WithStack(InvalidArgumentError{Message: "unknown index type"})
}
}

// newIndex creates a new Index implementation.
func newIndex(id string, indexTypeString string, name string, col *collection) (Index, error) {
if id == "" {
func newIndex(data indexData, col *collection) (Index, error) {
if data.ID == "" {
return nil, WithStack(InvalidArgumentError{Message: "id is empty"})
}
parts := strings.Split(id, "/")
parts := strings.Split(data.ID, "/")
if len(parts) != 2 {
return nil, WithStack(InvalidArgumentError{Message: "id must be `collection/name`"})
}
if col == nil {
return nil, WithStack(InvalidArgumentError{Message: "col is nil"})
}
indexType, err := indexStringToType(indexTypeString)
indexType, err := indexStringToType(data.Type)
if err != nil {
return nil, WithStack(err)
}
return &index{
id: id,
name: name,
indexData: data,
indexType: indexType,
col: col,
db: col.db,
Expand All @@ -79,8 +80,7 @@ func newIndex(id string, indexTypeString string, name string, col *collection) (
}

type index struct {
id string
name string
indexData
indexType IndexType
db *database
col *collection
Expand All @@ -94,29 +94,92 @@ func (i *index) relPath() string {

// Name returns the name of the index.
func (i *index) Name() string {
parts := strings.Split(i.id, "/")
parts := strings.Split(i.indexData.ID, "/")
return parts[1]
}

// ID returns the ID of the index.
func (i *index) ID() string {
return i.id
return i.indexData.ID
}

// UserName returns the user provided name of the index or empty string if non is provided.
func (i *index) UserName() string {
return i.name
return i.indexData.Name
}

// Type returns the type of the index
func (i *index) Type() IndexType {
return i.indexType
}

// Fields returns a list of attributes of this index.
func (i *index) Fields() []string {
return i.indexData.Fields
}

// Unique returns if this index is unique.
func (i *index) Unique() bool {
if i.indexData.Unique == nil {
return false
}
return *i.indexData.Unique
}

// Deduplicate returns deduplicate setting of this index.
func (i *index) Deduplicate() bool {
if i.indexData.Deduplicate == nil {
return false
}
return *i.indexData.Deduplicate
}

// Sparse returns if this is a sparse index or not.
func (i *index) Sparse() bool {
if i.indexData.Sparse == nil {
return false
}
return *i.indexData.Sparse
}

// GeoJSON returns if geo json was set for this index or not.
func (i *index) GeoJSON() bool {
if i.indexData.GeoJSON == nil {
return false
}
return *i.indexData.GeoJSON
}

// InBackground if true will not hold an exclusive collection lock for the entire index creation period (rocksdb only).
func (i *index) InBackground() bool {
if i.indexData.InBackground == nil {
return false
}
return *i.indexData.InBackground
}

// Estimates determines if the to-be-created index should maintain selectivity estimates or not.
func (i *index) Estimates() bool {
if i.indexData.Estimates == nil {
return false
}
return *i.indexData.Estimates
}

// MinLength returns min length for this index if set.
func (i *index) MinLength() int {
return i.indexData.MinLength
}

// ExpireAfter returns an expire after for this index if set.
func (i *index) ExpireAfter() int {
return i.indexData.ExpireAfter
}

// Remove removes the entire index.
// If the index does not exist, a NotFoundError is returned.
func (i *index) Remove(ctx context.Context) error {
req, err := i.conn.NewRequest("DELETE", path.Join(i.relPath(), i.id))
req, err := i.conn.NewRequest("DELETE", path.Join(i.relPath(), i.indexData.ID))
if err != nil {
return WithStack(err)
}
Expand Down
Loading