Skip to content

Commit 761827c

Browse files
authored
GT-149 Support for computed values fields (#415)
* GT-149 Support for computed values fields * Update comments
1 parent 19f1f26 commit 761827c

6 files changed

+138
-1
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## [master](https://github.com/arangodb/go-driver/tree/master) (N/A)
44
- Add `hex` property to analyzer's properties
5+
- Add support for `computedValues`
56

67
## [1.3.3](https://github.com/arangodb/go-driver/tree/v1.3.3) (2022-07-27)
78
- Fix `lastValue` field type

collection.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,10 @@ type CollectionProperties struct {
110110
JournalSize int64 `json:"journalSize,omitempty"`
111111
// CacheEnabled set cacheEnabled option in collection properties
112112
CacheEnabled bool `json:"cacheEnabled,omitempty"`
113-
KeyOptions struct {
113+
// ComputedValues let configure collections to generate document attributes when documents are created or modified, using an AQL expression
114+
ComputedValues []ComputedValue `json:"computedValues,omitempty"`
115+
// KeyOptions
116+
KeyOptions struct {
114117
// Type specifies the type of the key generator. The currently available generators are traditional and autoincrement.
115118
Type KeyGeneratorType `json:"type,omitempty"`
116119
// AllowUserKeys; if set to true, then it is allowed to supply own key values in the _key attribute of a document.
@@ -203,6 +206,8 @@ type SetCollectionPropertiesOptions struct {
203206
CacheEnabled *bool `json:"cacheEnabled,omitempty"`
204207
// Schema for collection validation
205208
Schema *CollectionSchemaOptions `json:"schema,omitempty"`
209+
// ComputedValues let configure collections to generate document attributes when documents are created or modified, using an AQL expression
210+
ComputedValues []ComputedValue `json:"computedValues,omitempty"`
206211
}
207212

208213
// CollectionStatus indicates the status of a collection.

collection_impl.go

+2
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ type setCollectionPropertiesOptionsInternal struct {
308308
JournalSize int64 `json:"journalSize,omitempty"`
309309
ReplicationFactor replicationFactor `json:"replicationFactor,omitempty"`
310310
CacheEnabled *bool `json:"cacheEnabled,omitempty"`
311+
ComputedValues []ComputedValue `json:"computedValues,omitempty"`
311312
// Deprecated: use 'WriteConcern' instead
312313
MinReplicationFactor int `json:"minReplicationFactor,omitempty"`
313314
// Available from 3.6 arangod version.
@@ -320,6 +321,7 @@ func (p *SetCollectionPropertiesOptions) asInternal() setCollectionPropertiesOpt
320321
WaitForSync: p.WaitForSync,
321322
JournalSize: p.JournalSize,
322323
CacheEnabled: p.CacheEnabled,
324+
ComputedValues: p.ComputedValues,
323325
ReplicationFactor: replicationFactor(p.ReplicationFactor),
324326
MinReplicationFactor: p.MinReplicationFactor,
325327
WriteConcern: p.WriteConcern,

database_collections.go

+30
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ type DatabaseCollections interface {
4545
type CreateCollectionOptions struct {
4646
// CacheEnabled set cacheEnabled option in collection properties
4747
CacheEnabled *bool `json:"cacheEnabled,omitempty"`
48+
// ComputedValues let configure collections to generate document attributes when documents are created or modified, using an AQL expression
49+
ComputedValues []ComputedValue `json:"computedValues,omitempty"`
4850
// This field is used for internal purposes only. DO NOT USE.
4951
DistributeShardsLike string `json:"distributeShardsLike,omitempty"`
5052
// DoCompact checks if the collection will be compacted (default is true)
@@ -142,6 +144,34 @@ const (
142144
CollectionTypeEdge = CollectionType(3)
143145
)
144146

147+
type ComputedValue struct {
148+
// The name of the target attribute. Can only be a top-level attribute, but you
149+
// may return a nested object. Cannot be `_key`, `_id`, `_rev`, `_from`, `_to`,
150+
// or a shard key attribute.
151+
Name string `json:"name"`
152+
// An AQL `RETURN` operation with an expression that computes the desired value.
153+
Expression string `json:"expression"`
154+
// An array of strings to define on which write operations the value shall be
155+
// computed. The possible values are `"insert"`, `"update"`, and `"replace"`.
156+
// The default is `["insert", "update", "replace"]`.
157+
ComputeOn []ComputeOn `json:"computeOn"`
158+
// Whether the computed value shall take precedence over a user-provided or existing attribute.
159+
Overwrite bool `json:"overwrite"`
160+
// Whether to let the write operation fail if the expression produces a warning. The default is false.
161+
FailOnWarning *bool `json:"failOnWarning,omitempty"`
162+
// Whether the result of the expression shall be stored if it evaluates to `null`.
163+
// This can be used to skip the value computation if any pre-conditions are not met.
164+
KeepNull *bool `json:"keepNull,omitempty"`
165+
}
166+
167+
type ComputeOn string
168+
169+
const (
170+
ComputeOnInsert ComputeOn = "insert"
171+
ComputeOnUpdate ComputeOn = "update"
172+
ComputeOnReplace ComputeOn = "replace"
173+
)
174+
145175
// CollectionKeyOptions specifies ways for creating keys of a collection.
146176
type CollectionKeyOptions struct {
147177
// If set to true, then it is allowed to supply own key values in the _key attribute of a document.

database_collections_impl.go

+2
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ func (d *database) Collections(ctx context.Context) ([]Collection, error) {
103103

104104
type createCollectionOptionsInternal struct {
105105
CacheEnabled *bool `json:"cacheEnabled,omitempty"`
106+
ComputedValues []ComputedValue `json:"computedValues,omitempty"`
106107
DistributeShardsLike string `json:"distributeShardsLike,omitempty"`
107108
DoCompact *bool `json:"doCompact,omitempty"`
108109
IndexBuckets int `json:"indexBuckets,omitempty"`
@@ -163,6 +164,7 @@ func (d *database) CreateCollection(ctx context.Context, name string, options *C
163164

164165
func (p *createCollectionOptionsInternal) fromExternal(i *CreateCollectionOptions) {
165166
p.CacheEnabled = i.CacheEnabled
167+
p.ComputedValues = i.ComputedValues
166168
p.DistributeShardsLike = i.DistributeShardsLike
167169
p.DoCompact = i.DoCompact
168170
p.IndexBuckets = i.IndexBuckets

test/collection_test.go

+97
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,103 @@ func TestCollection_CacheEnabled(t *testing.T) {
168168
})
169169
}
170170

171+
// TestCollection_ComputedValues
172+
func TestCollection_ComputedValues(t *testing.T) {
173+
c := createClientFromEnv(t, true)
174+
skipBelowVersion(c, "3.10", t)
175+
db := ensureDatabase(nil, c, "collection_test_computed_values", nil, t)
176+
177+
t.Run("Create with ComputedValues", func(t *testing.T) {
178+
name := "test_users_computed_values"
179+
180+
// Add an attribute with the creation timestamp to new documents
181+
computedValue := driver.ComputedValue{
182+
Name: "createdAt",
183+
Expression: "RETURN DATE_NOW()",
184+
Overwrite: true,
185+
ComputeOn: []driver.ComputeOn{driver.ComputeOnInsert},
186+
}
187+
188+
_, err := db.CreateCollection(nil, name, &driver.CreateCollectionOptions{
189+
ComputedValues: []driver.ComputedValue{computedValue},
190+
})
191+
require.NoError(t, err)
192+
193+
// Collection must exist now
194+
col, err := db.Collection(nil, name)
195+
require.NoError(t, err)
196+
197+
prop, err := col.Properties(nil)
198+
require.NoError(t, err)
199+
200+
// Check if the computed value is in the list of computed values
201+
require.Len(t, prop.ComputedValues, 1)
202+
require.Equal(t, computedValue.Name, prop.ComputedValues[0].Name)
203+
require.Equal(t, computedValue.Expression, prop.ComputedValues[0].Expression)
204+
205+
// Create a document
206+
doc := UserDoc{Name: fmt.Sprintf("Jakub")}
207+
meta, err := col.CreateDocument(nil, doc)
208+
if err != nil {
209+
t.Fatalf("Failed to create document: %s", describe(err))
210+
}
211+
212+
// Read document
213+
var readDoc map[string]interface{}
214+
if _, err := col.ReadDocument(nil, meta.Key, &readDoc); err != nil {
215+
t.Fatalf("Failed to read document '%s': %s", meta.Key, describe(err))
216+
}
217+
218+
require.Equal(t, doc.Name, readDoc["name"])
219+
220+
// Verify that the computed value is set
221+
createdAtValue, createdAtIsPresent := readDoc["createdAt"]
222+
require.True(t, createdAtIsPresent)
223+
224+
// Verify that the computed value is a valid date
225+
tm := time.Unix(int64(createdAtValue.(float64)), 0)
226+
require.True(t, tm.After(time.Now().Add(-time.Second)))
227+
})
228+
229+
t.Run("Update to ComputedValues", func(t *testing.T) {
230+
name := "test_update_computed_values"
231+
232+
// Add an attribute with the creation timestamp to new documents
233+
computedValue := driver.ComputedValue{
234+
Name: "createdAt",
235+
Expression: "RETURN DATE_NOW()",
236+
Overwrite: true,
237+
ComputeOn: []driver.ComputeOn{driver.ComputeOnInsert},
238+
}
239+
240+
_, err := db.CreateCollection(nil, name, nil)
241+
require.NoError(t, err)
242+
243+
// Collection must exist now
244+
col, err := db.Collection(nil, name)
245+
require.NoError(t, err)
246+
247+
prop, err := col.Properties(nil)
248+
require.NoError(t, err)
249+
250+
require.Len(t, prop.ComputedValues, 0)
251+
252+
err = col.SetProperties(nil, driver.SetCollectionPropertiesOptions{
253+
ComputedValues: []driver.ComputedValue{computedValue},
254+
})
255+
require.NoError(t, err)
256+
257+
// Check if the computed value is in the list of computed values
258+
col, err = db.Collection(nil, name)
259+
require.NoError(t, err)
260+
261+
prop, err = col.Properties(nil)
262+
require.NoError(t, err)
263+
264+
require.Len(t, prop.ComputedValues, 1)
265+
})
266+
}
267+
171268
// TestCreateSatelliteCollection create a satellite collection
172269
func TestCreateSatelliteCollection(t *testing.T) {
173270
skipNoEnterprise(t)

0 commit comments

Comments
 (0)