Skip to content

Commit 969f7da

Browse files
authored
Drivers 3.9: ZKD indexes (TG-205) (#356)
1 parent 2896b19 commit 969f7da

File tree

9 files changed

+305
-50
lines changed

9 files changed

+305
-50
lines changed

cluster.go

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -236,16 +236,18 @@ type ShardID string
236236

237237
// InventoryIndex contains all configuration parameters of a single index of a collection in a database inventory.
238238
type InventoryIndex struct {
239-
ID string `json:"id,omitempty"`
240-
Type string `json:"type,omitempty"`
241-
Fields []string `json:"fields,omitempty"`
242-
Unique bool `json:"unique"`
243-
Sparse bool `json:"sparse"`
244-
Deduplicate bool `json:"deduplicate"`
245-
MinLength int `json:"minLength,omitempty"`
246-
GeoJSON bool `json:"geoJson,omitempty"`
247-
Name string `json:"name,omitempty"`
248-
ExpireAfter int `json:"expireAfter,omitempty"`
239+
ID string `json:"id,omitempty"`
240+
Type string `json:"type,omitempty"`
241+
Fields []string `json:"fields,omitempty"`
242+
Unique bool `json:"unique"`
243+
Sparse bool `json:"sparse"`
244+
Deduplicate bool `json:"deduplicate"`
245+
MinLength int `json:"minLength,omitempty"`
246+
GeoJSON bool `json:"geoJson,omitempty"`
247+
Name string `json:"name,omitempty"`
248+
ExpireAfter int `json:"expireAfter,omitempty"`
249+
Estimates bool `json:"estimates,omitempty"`
250+
FieldValueTypes string `json:"fieldValueTypes,omitempty"`
249251
}
250252

251253
// FieldsEqual returns true when the given fields list equals the

collection_indexes.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ type CollectionIndexes interface {
7070
// EnsureTTLIndex creates a TLL collection, if it does not already exist.
7171
// The index is returned, together with a boolean indicating if the index was newly created (true) or pre-existing (false).
7272
EnsureTTLIndex(ctx context.Context, field string, expireAfter int, options *EnsureTTLIndexOptions) (Index, bool, error)
73+
74+
// EnsureZKDIndex creates a ZKD multi-dimensional index for the collection, if it does not already exist.
75+
// Note that zkd indexes are an experimental feature in ArangoDB 3.9.
76+
EnsureZKDIndex(ctx context.Context, fields []string, options *EnsureZKDIndexOptions) (Index, bool, error)
7377
}
7478

7579
// EnsureFullTextIndexOptions contains specific options for creating a full text index.
@@ -157,3 +161,19 @@ type EnsureTTLIndexOptions struct {
157161
// Estimates determines if the to-be-created index should maintain selectivity estimates or not.
158162
Estimates *bool
159163
}
164+
165+
// EnsureZKDIndexOptions provides specific options for creating a ZKD index
166+
type EnsureZKDIndexOptions struct {
167+
// If true, then create a unique index.
168+
Unique bool
169+
// InBackground if true will not hold an exclusive collection lock for the entire index creation period (rocksdb only).
170+
InBackground bool
171+
// Name optional user defined name used for hints in AQL queries
172+
Name string
173+
// fieldValueTypes is required and the only allowed value is "double". Future extensions of the index will allow other types.
174+
FieldValueTypes string
175+
176+
// If true, then create a sparse index.
177+
// TODO: The sparse property is not supported yet
178+
// Sparse bool
179+
}

collection_indexes_impl.go

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,28 +28,23 @@ import (
2828
)
2929

3030
type indexData struct {
31-
ID string `json:"id,omitempty"`
32-
Type string `json:"type"`
33-
Fields []string `json:"fields,omitempty"`
34-
Unique *bool `json:"unique,omitempty"`
35-
Deduplicate *bool `json:"deduplicate,omitempty"`
36-
Sparse *bool `json:"sparse,omitempty"`
37-
GeoJSON *bool `json:"geoJson,omitempty"`
38-
InBackground *bool `json:"inBackground,omitempty"`
39-
Estimates *bool `json:"estimates,omitempty"`
40-
MinLength int `json:"minLength,omitempty"`
41-
ExpireAfter int `json:"expireAfter,omitempty"`
42-
Name string `json:"name,omitempty"`
43-
}
44-
45-
type genericIndexData struct {
46-
ID string `json:"id,omitempty"`
47-
Type string `json:"type"`
48-
Name string `json:"name,omitempty"`
31+
ID string `json:"id,omitempty"`
32+
Type string `json:"type"`
33+
Fields []string `json:"fields,omitempty"`
34+
Unique *bool `json:"unique,omitempty"`
35+
Deduplicate *bool `json:"deduplicate,omitempty"`
36+
Sparse *bool `json:"sparse,omitempty"`
37+
GeoJSON *bool `json:"geoJson,omitempty"`
38+
InBackground *bool `json:"inBackground,omitempty"`
39+
Estimates *bool `json:"estimates,omitempty"`
40+
MinLength int `json:"minLength,omitempty"`
41+
ExpireAfter int `json:"expireAfter,omitempty"`
42+
Name string `json:"name,omitempty"`
43+
FieldValueTypes string `json:"fieldValueTypes,omitempty"`
4944
}
5045

5146
type indexListResponse struct {
52-
Indexes []genericIndexData `json:"indexes,omitempty"`
47+
Indexes []indexData `json:"indexes,omitempty"`
5348
}
5449

5550
// Index opens a connection to an existing index within the collection.
@@ -70,7 +65,7 @@ func (c *collection) Index(ctx context.Context, name string) (Index, error) {
7065
if err := resp.ParseBody("", &data); err != nil {
7166
return nil, WithStack(err)
7267
}
73-
idx, err := newIndex(data.ID, data.Type, data.Name, c)
68+
idx, err := newIndex(data, c)
7469
if err != nil {
7570
return nil, WithStack(err)
7671
}
@@ -116,7 +111,7 @@ func (c *collection) Indexes(ctx context.Context) ([]Index, error) {
116111
}
117112
result := make([]Index, 0, len(data.Indexes))
118113
for _, x := range data.Indexes {
119-
idx, err := newIndex(x.ID, x.Type, x.Name, c)
114+
idx, err := newIndex(x, c)
120115
if err != nil {
121116
return nil, WithStack(err)
122117
}
@@ -269,6 +264,29 @@ func (c *collection) EnsureTTLIndex(ctx context.Context, field string, expireAft
269264
return idx, created, nil
270265
}
271266

267+
// EnsureZKDIndex creates a ZKD index in the collection, if it does not already exist.
268+
// Fields is a slice of attribute paths.
269+
// The index is returned, together with a boolean indicating if the index was newly created (true) or pre-existing (false).
270+
func (c *collection) EnsureZKDIndex(ctx context.Context, fields []string, options *EnsureZKDIndexOptions) (Index, bool, error) {
271+
input := indexData{
272+
Type: string(ZKDIndex),
273+
Fields: fields,
274+
// fieldValueTypes is required and the only allowed value is "double". Future extensions of the index will allow other types.
275+
FieldValueTypes: "double",
276+
}
277+
if options != nil {
278+
input.InBackground = &options.InBackground
279+
input.Name = options.Name
280+
input.Unique = &options.Unique
281+
//input.Sparse = &options.Sparse
282+
}
283+
idx, created, err := c.ensureIndex(ctx, input)
284+
if err != nil {
285+
return nil, false, WithStack(err)
286+
}
287+
return idx, created, nil
288+
}
289+
272290
// ensureIndex creates a persistent index in the collection, if it does not already exist.
273291
// Fields is a slice of attribute paths.
274292
// The index is returned, together with a boolean indicating if the index was newly created (true) or pre-existing (false).
@@ -293,7 +311,7 @@ func (c *collection) ensureIndex(ctx context.Context, options indexData) (Index,
293311
if err := resp.ParseBody("", &data); err != nil {
294312
return nil, false, WithStack(err)
295313
}
296-
idx, err := newIndex(data.ID, data.Type, data.Name, c)
314+
idx, err := newIndex(data, c)
297315
if err != nil {
298316
return nil, false, WithStack(err)
299317
}

edge_collection_indexes_impl.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,14 @@ func (c *edgeCollection) EnsureTTLIndex(ctx context.Context, field string, expir
124124
}
125125
return result, created, nil
126126
}
127+
128+
// EnsureZKDIndex creates a ZKD index in the collection, if it does not already exist.
129+
// Fields is a slice of attribute paths.
130+
// The index is returned, together with a boolean indicating if the index was newly created (true) or pre-existing (false).
131+
func (c *edgeCollection) EnsureZKDIndex(ctx context.Context, fields []string, options *EnsureZKDIndexOptions) (Index, bool, error) {
132+
result, created, err := c.rawCollection().EnsureZKDIndex(ctx, fields, options)
133+
if err != nil {
134+
return nil, false, WithStack(err)
135+
}
136+
return result, created, nil
137+
}

index.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const (
3737
GeoIndex = IndexType("geo")
3838
EdgeIndex = IndexType("edge")
3939
TTLIndex = IndexType("ttl")
40+
ZKDIndex = IndexType("zkd")
4041
)
4142

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

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

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

5556
// Type returns the type of the index
@@ -58,4 +59,31 @@ type Index interface {
5859
// Remove removes the entire index.
5960
// If the index does not exist, a NotFoundError is returned.
6061
Remove(ctx context.Context) error
62+
63+
// Fields returns a list of attributes of this index.
64+
Fields() []string
65+
66+
// Unique returns if this index is unique.
67+
Unique() bool
68+
69+
// Deduplicate returns deduplicate setting of this index.
70+
Deduplicate() bool
71+
72+
// Sparse returns if this is a sparse index or not.
73+
Sparse() bool
74+
75+
// GeoJSON returns if geo json was set for this index or not.
76+
GeoJSON() bool
77+
78+
// InBackground if true will not hold an exclusive collection lock for the entire index creation period (rocksdb only).
79+
InBackground() bool
80+
81+
// Estimates determines if the to-be-created index should maintain selectivity estimates or not.
82+
Estimates() bool
83+
84+
// MinLength returns min length for this index if set.
85+
MinLength() int
86+
87+
// ExpireAfter returns an expire after for this index if set.
88+
ExpireAfter() int
6189
}

index_impl.go

Lines changed: 75 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,30 +47,31 @@ func indexStringToType(indexTypeString string) (IndexType, error) {
4747
return EdgeIndex, nil
4848
case string(TTLIndex):
4949
return TTLIndex, nil
50+
case string(ZKDIndex):
51+
return ZKDIndex, nil
5052
default:
5153
return "", WithStack(InvalidArgumentError{Message: "unknown index type"})
5254
}
5355
}
5456

5557
// newIndex creates a new Index implementation.
56-
func newIndex(id string, indexTypeString string, name string, col *collection) (Index, error) {
57-
if id == "" {
58+
func newIndex(data indexData, col *collection) (Index, error) {
59+
if data.ID == "" {
5860
return nil, WithStack(InvalidArgumentError{Message: "id is empty"})
5961
}
60-
parts := strings.Split(id, "/")
62+
parts := strings.Split(data.ID, "/")
6163
if len(parts) != 2 {
6264
return nil, WithStack(InvalidArgumentError{Message: "id must be `collection/name`"})
6365
}
6466
if col == nil {
6567
return nil, WithStack(InvalidArgumentError{Message: "col is nil"})
6668
}
67-
indexType, err := indexStringToType(indexTypeString)
69+
indexType, err := indexStringToType(data.Type)
6870
if err != nil {
6971
return nil, WithStack(err)
7072
}
7173
return &index{
72-
id: id,
73-
name: name,
74+
indexData: data,
7475
indexType: indexType,
7576
col: col,
7677
db: col.db,
@@ -79,8 +80,7 @@ func newIndex(id string, indexTypeString string, name string, col *collection) (
7980
}
8081

8182
type index struct {
82-
id string
83-
name string
83+
indexData
8484
indexType IndexType
8585
db *database
8686
col *collection
@@ -94,29 +94,92 @@ func (i *index) relPath() string {
9494

9595
// Name returns the name of the index.
9696
func (i *index) Name() string {
97-
parts := strings.Split(i.id, "/")
97+
parts := strings.Split(i.indexData.ID, "/")
9898
return parts[1]
9999
}
100100

101101
// ID returns the ID of the index.
102102
func (i *index) ID() string {
103-
return i.id
103+
return i.indexData.ID
104104
}
105105

106106
// UserName returns the user provided name of the index or empty string if non is provided.
107107
func (i *index) UserName() string {
108-
return i.name
108+
return i.indexData.Name
109109
}
110110

111111
// Type returns the type of the index
112112
func (i *index) Type() IndexType {
113113
return i.indexType
114114
}
115115

116+
// Fields returns a list of attributes of this index.
117+
func (i *index) Fields() []string {
118+
return i.indexData.Fields
119+
}
120+
121+
// Unique returns if this index is unique.
122+
func (i *index) Unique() bool {
123+
if i.indexData.Unique == nil {
124+
return false
125+
}
126+
return *i.indexData.Unique
127+
}
128+
129+
// Deduplicate returns deduplicate setting of this index.
130+
func (i *index) Deduplicate() bool {
131+
if i.indexData.Deduplicate == nil {
132+
return false
133+
}
134+
return *i.indexData.Deduplicate
135+
}
136+
137+
// Sparse returns if this is a sparse index or not.
138+
func (i *index) Sparse() bool {
139+
if i.indexData.Sparse == nil {
140+
return false
141+
}
142+
return *i.indexData.Sparse
143+
}
144+
145+
// GeoJSON returns if geo json was set for this index or not.
146+
func (i *index) GeoJSON() bool {
147+
if i.indexData.GeoJSON == nil {
148+
return false
149+
}
150+
return *i.indexData.GeoJSON
151+
}
152+
153+
// InBackground if true will not hold an exclusive collection lock for the entire index creation period (rocksdb only).
154+
func (i *index) InBackground() bool {
155+
if i.indexData.InBackground == nil {
156+
return false
157+
}
158+
return *i.indexData.InBackground
159+
}
160+
161+
// Estimates determines if the to-be-created index should maintain selectivity estimates or not.
162+
func (i *index) Estimates() bool {
163+
if i.indexData.Estimates == nil {
164+
return false
165+
}
166+
return *i.indexData.Estimates
167+
}
168+
169+
// MinLength returns min length for this index if set.
170+
func (i *index) MinLength() int {
171+
return i.indexData.MinLength
172+
}
173+
174+
// ExpireAfter returns an expire after for this index if set.
175+
func (i *index) ExpireAfter() int {
176+
return i.indexData.ExpireAfter
177+
}
178+
116179
// Remove removes the entire index.
117180
// If the index does not exist, a NotFoundError is returned.
118181
func (i *index) Remove(ctx context.Context) error {
119-
req, err := i.conn.NewRequest("DELETE", path.Join(i.relPath(), i.id))
182+
req, err := i.conn.NewRequest("DELETE", path.Join(i.relPath(), i.indexData.ID))
120183
if err != nil {
121184
return WithStack(err)
122185
}

0 commit comments

Comments
 (0)