From 36bf4158d3d8dd156fb19fc391bebb6837395071 Mon Sep 17 00:00:00 2001 From: jwierzbo Date: Tue, 17 Sep 2024 14:20:23 +0200 Subject: [PATCH 1/4] OAS-10042 Support for missing dirty read options (query, transaction apis). Get inbound and outbound edges. --- CHANGELOG.md | 2 + v2/arangodb/database_graph.go | 51 +++++++++++++++- v2/arangodb/database_graph_impl.go | 25 ++++++++ v2/arangodb/database_query.go | 59 ++++++++++++++++++- v2/arangodb/database_query_impl.go | 6 +- v2/arangodb/database_transaction_impl.go | 7 +-- v2/arangodb/database_transaction_opts.go | 49 +++++++++++++-- v2/arangodb/shared.go | 4 +- v2/tests/graph_edge_definitions_edges_test.go | 30 ++++++++++ 9 files changed, 214 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02d967ed..cbad1fb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ## [master](https://github.com/arangodb/go-driver/tree/master) (N/A) - Switch to Go 1.22.5 - Switch to Go 1.22.6 +- [v2] Support for missing dirty read options (query, transaction apis) +- [v2] Get inbound and outbound edges ## [1.6.2](https://github.com/arangodb/go-driver/tree/v1.6.2) (2024-04-02) - Switch to Go 1.20.11 diff --git a/v2/arangodb/database_graph.go b/v2/arangodb/database_graph.go index 78d7aa82..cde0dd07 100644 --- a/v2/arangodb/database_graph.go +++ b/v2/arangodb/database_graph.go @@ -20,7 +20,11 @@ package arangodb -import "context" +import ( + "context" + + "github.com/arangodb/go-driver/v2/connection" +) const ( // SatelliteGraph is a special replication factor for satellite graphs. @@ -28,7 +32,20 @@ const ( SatelliteGraph = -100 ) +type EdgeDirection string + +const ( + // EdgeDirectionIn selects inbound edges + EdgeDirectionIn EdgeDirection = "in" + // EdgeDirectionOut selects outbound edges + EdgeDirectionOut EdgeDirection = "out" +) + type DatabaseGraph interface { + // GetEdges returns inbound and outbound edge documents of a given vertex. + // Requires Edge collection name and vertex ID + GetEdges(ctx context.Context, name, vertex string, options *GetEdgesOptions) ([]EdgeDetails, error) + // Graph opens a connection to an existing graph within the database. // If no graph with given name exists, an NotFoundError is returned. Graph(ctx context.Context, name string, options *GetGraphOptions) (Graph, error) @@ -44,6 +61,22 @@ type DatabaseGraph interface { CreateGraph(ctx context.Context, name string, graph *GraphDefinition, options *CreateGraphOptions) (Graph, error) } +type GetEdgesOptions struct { + // The direction of the edges. Allowed values are "in" and "out". If not set, edges in both directions are returned. + Direction EdgeDirection `json:"direction,omitempty"` + + // Set this to true to allow the Coordinator to ask any shard replica for the data, not only the shard leader. + // This may result in “dirty reads”. + AllowDirtyReads *bool `json:"-"` +} + +type EdgeDetails struct { + DocumentMeta + From string `json:"_from"` + To string `json:"_to"` + Label string `json:"$label"` +} + type GetGraphOptions struct { // SkipExistCheck skips checking if graph exists SkipExistCheck bool `json:"skipExistCheck,omitempty"` @@ -60,3 +93,19 @@ type GraphsResponseReader interface { // Read returns next Graph. If no Graph left, shared.NoMoreDocumentsError returned Read() (Graph, error) } + +func (q *GetEdgesOptions) modifyRequest(r connection.Request) error { + if q == nil { + return nil + } + + if q.AllowDirtyReads != nil { + r.AddHeader("x-arango-allow-dirty-read", boolToString(*q.AllowDirtyReads)) + } + + if q.Direction != "" { + r.AddQuery("direction", string(q.Direction)) + } + + return nil +} diff --git a/v2/arangodb/database_graph_impl.go b/v2/arangodb/database_graph_impl.go index 08cc3709..9c2d4c44 100644 --- a/v2/arangodb/database_graph_impl.go +++ b/v2/arangodb/database_graph_impl.go @@ -44,6 +44,31 @@ type databaseGraph struct { db *database } +func (d *databaseGraph) GetEdges(ctx context.Context, name, vertex string, options *GetEdgesOptions) ([]EdgeDetails, error) { + if name == "" || vertex == "" { + return nil, errors.WithStack(errors.New("edge collection name and vertex must be set")) + } + + urlEndpoint := d.db.url("_api", "edges", url.PathEscape(name)) + + var response struct { + shared.ResponseStruct `json:",inline"` + Edges []EdgeDetails `json:"edges,omitempty"` + } + + resp, err := connection.CallGet(ctx, d.db.connection(), urlEndpoint, &response, append(d.db.modifiers, options.modifyRequest, connection.WithQuery("vertex", vertex))...) + if err != nil { + return nil, errors.WithStack(err) + } + + switch code := resp.Code(); code { + case http.StatusOK: + return response.Edges, nil + default: + return nil, response.AsArangoErrorWithCode(code) + } +} + func (d *databaseGraph) Graph(ctx context.Context, name string, options *GetGraphOptions) (Graph, error) { urlEndpoint := d.db.url("_api", "gharial", url.PathEscape(name)) diff --git a/v2/arangodb/database_query.go b/v2/arangodb/database_query.go index b323fe80..c6729d92 100644 --- a/v2/arangodb/database_query.go +++ b/v2/arangodb/database_query.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2020-2023 ArangoDB GmbH, Cologne, Germany +// Copyright 2020-2024 ArangoDB GmbH, Cologne, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,7 +20,11 @@ package arangodb -import "context" +import ( + "context" + + "github.com/arangodb/go-driver/v2/connection" +) type DatabaseQuery interface { // Query performs an AQL query, returning a cursor used to iterate over the returned documents. @@ -44,17 +48,21 @@ type DatabaseQuery interface { type QuerySubOptions struct { // ShardId query option ShardIds []string `json:"shardIds,omitempty"` + // Profile If set to 1, then the additional query profiling information is returned in the profile sub-attribute // of the extra return attribute, unless the query result is served from the query cache. // If set to 2, the query includes execution stats per query plan node in stats.nodes // sub-attribute of the extra return attribute. // Additionally, the query plan is returned in the extra.plan sub-attribute. Profile uint `json:"profile,omitempty"` + // Optimizer contains options related to the query optimizer. Optimizer QuerySubOptionsOptimizer `json:"optimizer,omitempty"` + // This Enterprise Edition parameter allows to configure how long a DBServer will have time to bring the satellite collections // involved in the query into sync. The default value is 60.0 (seconds). When the max time has been reached the query will be stopped. SatelliteSyncWait float64 `json:"satelliteSyncWait,omitempty"` + // if set to true and the query contains a LIMIT clause, then the result will have an extra attribute with the sub-attributes // stats and fullCount, { ... , "extra": { "stats": { "fullCount": 123 } } }. The fullCount attribute will contain the number // of documents in the result before the last LIMIT in the query was applied. It can be used to count the number of documents @@ -63,50 +71,87 @@ type QuerySubOptions struct { // thus make queries run longer. Note that the fullCount attribute will only be present in the result if the query has a LIMIT clause // and the LIMIT clause is actually used in the query. FullCount bool `json:"fullCount,omitempty"` + // Limits the maximum number of plans that are created by the AQL query optimizer. MaxPlans int `json:"maxPlans,omitempty"` + // Specify true and the query will be executed in a streaming fashion. The query result is not stored on // the server, but calculated on the fly. Beware: long-running queries will need to hold the collection // locks for as long as the query cursor exists. When set to false a query will be executed right away in // its entirety. Stream bool `json:"stream,omitempty"` + // MaxRuntime specify the timeout which can be used to kill a query on the server after the specified // amount in time. The timeout value is specified in seconds. A value of 0 means no timeout will be enforced. MaxRuntime float64 `json:"maxRuntime,omitempty"` + // FillBlockCache if is set to true or not specified, this will make the query store the data it reads via the RocksDB storage engine in the RocksDB block cache. // This is usually the desired behavior. The option can be set to false for queries that are known to either read a lot of data which would thrash the block cache, // or for queries that read data which are known to be outside of the hot set. By setting the option to false, data read by the query will not make it into // the RocksDB block cache if not already in there, thus leaving more room for the actual hot set. FillBlockCache bool `json:"fillBlockCache,omitempty"` + // AllowRetry If set to `true`, ArangoDB will store cursor results in such a way // that batch reads can be retried in the case of a communication error. AllowRetry bool `json:"allowRetry,omitempty"` } type QueryOptions struct { - // indicates whether the number of documents in the result set should be returned in the "count" attribute of the result. + // Set this to true to allow the Coordinator to ask any shard replica for the data, not only the shard leader. + // This may result in “dirty reads”. + // This option is ignored if this operation is part of a DatabaseTransaction (TransactionID option). + // The header set when creating the transaction decides about dirty reads for the entire transaction, + // not the individual read operations. + AllowDirtyReads *bool `json:"-"` + + // To make this operation a part of a Stream Transaction, set this header to the transaction ID returned by the + // DatabaseTransaction.BeginTransaction() method. + TransactionID string `json:"-"` + + // Indicates whether the number of documents in the result set should be returned in the "count" attribute of the result. // Calculating the "count" attribute might have a performance impact for some queries in the future so this option is // turned off by default, and "count" is only returned when requested. Count bool `json:"count,omitempty"` + // maximum number of result documents to be transferred from the server to the client in one roundtrip. // If this attribute is not set, a server-controlled default value will be used. A batchSize value of 0 is disallowed. BatchSize int `json:"batchSize,omitempty"` + // flag to determine whether the AQL query cache shall be used. If set to false, then any query cache lookup // will be skipped for the query. If set to true, it will lead to the query cache being checked for the query // if the query cache mode is either on or demand. Cache bool `json:"cache,omitempty"` + // the maximum number of memory (measured in bytes) that the query is allowed to use. If set, then the query will fail // with error "resource limit exceeded" in case it allocates too much memory. A value of 0 indicates that there is no memory limit. MemoryLimit int64 `json:"memoryLimit,omitempty"` + // The time-to-live for the cursor (in seconds). The cursor will be removed on the server automatically after the specified // amount of time. This is useful to ensure garbage collection of cursors that are not fully fetched by clients. // If not set, a server-defined value will be used. TTL float64 `json:"ttl,omitempty"` + // key/value pairs representing the bind parameters. BindVars map[string]interface{} `json:"bindVars,omitempty"` Options QuerySubOptions `json:"options,omitempty"` } +func (q *QueryOptions) modifyRequest(r connection.Request) error { + if q == nil { + return nil + } + + if q.AllowDirtyReads != nil { + r.AddHeader("x-arango-allow-dirty-read", boolToString(*q.AllowDirtyReads)) + } + + if q.TransactionID != "" { + r.AddHeader("x-arango-trx-id", q.TransactionID) + } + + return nil +} + // QuerySubOptionsOptimizer describes optimization's settings for AQL queries. type QuerySubOptionsOptimizer struct { // A list of to-be-included or to-be-excluded optimizer rules can be put into this attribute, @@ -158,14 +203,19 @@ type ExplainQueryResultExecutionVariable struct { type ExplainQueryResultPlan struct { // Execution nodes of the plan. NodesRaw []ExplainQueryResultExecutionNodeRaw `json:"nodes,omitempty"` + // List of rules the optimizer applied Rules []string `json:"rules,omitempty"` + // List of collections used in the query Collections []ExplainQueryResultExecutionCollection `json:"collections,omitempty"` + // List of variables used in the query (note: this may contain internal variables created by the optimizer) Variables []ExplainQueryResultExecutionVariable `json:"variables,omitempty"` + // The total estimated cost for the plan. If there are multiple plans, the optimizer will choose the plan with the lowest total cost EstimatedCost float64 `json:"estimatedCost,omitempty"` + // The estimated number of results. EstimatedNrItems int `json:"estimatedNrItems,omitempty"` } @@ -181,10 +231,13 @@ type ExplainQueryResultExecutionStats struct { type ExplainQueryResult struct { Plan ExplainQueryResultPlan `json:"plan,omitempty"` Plans []ExplainQueryResultPlan `json:"plans,omitempty"` + // List of warnings that occurred during optimization or execution plan creation Warnings []string `json:"warnings,omitempty"` + // Info about optimizer statistics Stats ExplainQueryResultExecutionStats `json:"stats,omitempty"` + // Cacheable states whether the query results can be cached on the server if the query result cache were used. // This attribute is not present when allPlans is set to true. Cacheable *bool `json:"cacheable,omitempty"` diff --git a/v2/arangodb/database_query_impl.go b/v2/arangodb/database_query_impl.go index 333145c1..0386c1bd 100644 --- a/v2/arangodb/database_query_impl.go +++ b/v2/arangodb/database_query_impl.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2020 ArangoDB GmbH, Cologne, Germany +// Copyright 2020-2024 ArangoDB GmbH, Cologne, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -17,8 +17,6 @@ // // Copyright holder is ArangoDB GmbH, Cologne, Germany // -// Author Adam Janikowski -// package arangodb @@ -64,7 +62,7 @@ func (d databaseQuery) getCursor(ctx context.Context, query string, opts *QueryO cursorData `json:",inline"` } - resp, err := connection.CallPost(ctx, d.db.connection(), url, &response, &req, d.db.modifiers...) + resp, err := connection.CallPost(ctx, d.db.connection(), url, &response, &req, append(d.db.modifiers, opts.modifyRequest)...) if err != nil { return nil, err } diff --git a/v2/arangodb/database_transaction_impl.go b/v2/arangodb/database_transaction_impl.go index 62af9021..c32fe788 100644 --- a/v2/arangodb/database_transaction_impl.go +++ b/v2/arangodb/database_transaction_impl.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2020-2023 ArangoDB GmbH, Cologne, Germany +// Copyright 2020-2024 ArangoDB GmbH, Cologne, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -24,10 +24,9 @@ import ( "context" "net/http" - "github.com/arangodb/go-driver/v2/arangodb/shared" - "github.com/pkg/errors" + "github.com/arangodb/go-driver/v2/arangodb/shared" "github.com/arangodb/go-driver/v2/connection" ) @@ -138,7 +137,7 @@ func (d databaseTransaction) BeginTransaction(ctx context.Context, cols Transact } `json:"result"` }{} - resp, err := connection.CallPost(ctx, d.db.connection(), url, &output, &input, d.db.modifiers...) + resp, err := connection.CallPost(ctx, d.db.connection(), url, &output, &input, append(d.db.modifiers, opts.modifyRequest)...) if err != nil { return nil, errors.WithStack(err) } diff --git a/v2/arangodb/database_transaction_opts.go b/v2/arangodb/database_transaction_opts.go index d27a21bb..02a3dd91 100644 --- a/v2/arangodb/database_transaction_opts.go +++ b/v2/arangodb/database_transaction_opts.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2017-2023 ArangoDB GmbH, Cologne, Germany +// Copyright 2017-2024 ArangoDB GmbH, Cologne, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -21,16 +21,53 @@ package arangodb import ( + "github.com/arangodb/go-driver/v2/connection" "time" ) // BeginTransactionOptions provides options for BeginTransaction call type BeginTransactionOptions struct { - WaitForSync bool `json:"waitForSync,omitempty"` - AllowImplicit bool `json:"allowImplicit,omitempty"` + // Set this to true to allow the Coordinator to ask any shard replica for the data, not only the shard leader. + // This may result in “dirty reads”. + AllowDirtyReads *bool `json:"-"` + + // Allow reading from undeclared collections. + AllowImplicit bool `json:"allowImplicit,omitempty"` + + // An optional numeric value that can be used to set a timeout in seconds for waiting on collection locks. + // This option is only meaningful when using exclusive locks. If not specified, a default value will be used. + // Setting lockTimeout to 0 will make ArangoDB not time out waiting for a lock. + LockTimeout float64 `json:"lockTimeout,omitempty"` + + // Transaction size limit in bytes. + MaxTransactionSize uint64 `json:"maxTransactionSize,omitempty"` + + // Whether to disable fast locking for write operations. + // + // Skipping the fast lock round can be faster overall if there are many concurrent Stream Transactions queued that + // all try to lock the same collection exclusively. It avoids deadlocking and retrying which can occur with the fast + // locking by guaranteeing a deterministic locking order at the expense of each actual locking operation taking longer. + // + // Fast locking should not be skipped for read-only Stream Transactions because it degrades performance if there + // are no concurrent transactions that use exclusive locks on the same collection. + SkipFastLockRound bool `json:"skipFastLockRound,omitempty"` + + // An optional boolean flag that, if set, will force the transaction to write all data to disk before returning. + WaitForSync bool `json:"waitForSync,omitempty"` + LockTimeoutDuration time.Duration `json:"-"` - LockTimeout float64 `json:"lockTimeout,omitempty"` - MaxTransactionSize uint64 `json:"maxTransactionSize,omitempty"` +} + +func (q *BeginTransactionOptions) modifyRequest(r connection.Request) error { + if q == nil { + return nil + } + + if q.AllowDirtyReads != nil { + r.AddHeader("x-arango-allow-dirty-read", boolToString(*q.AllowDirtyReads)) + } + + return nil } func (b *BeginTransactionOptions) set() *BeginTransactionOptions { @@ -45,8 +82,10 @@ func (b *BeginTransactionOptions) set() *BeginTransactionOptions { type TransactionCollections struct { // Collections that the transaction reads from. Read []string `json:"read,omitempty"` + // Collections that the transaction writes to. Write []string `json:"write,omitempty"` + // Collections that the transaction writes exclusively to. Exclusive []string `json:"exclusive,omitempty"` } diff --git a/v2/arangodb/shared.go b/v2/arangodb/shared.go index ee5057e0..2dbd7b3f 100644 --- a/v2/arangodb/shared.go +++ b/v2/arangodb/shared.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2023 ArangoDB GmbH, Cologne, Germany +// Copyright 2023-2024 ArangoDB GmbH, Cologne, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -57,7 +57,7 @@ type PrimarySort struct { Cache *bool `json:"cache,omitempty"` } -// PrimarySortEntry field to sort the index by and the direction +// PrimarySortEntry field to sort the index by and the direction type PrimarySortEntry struct { // Field An attribute path. The . character denotes sub-attributes. Field string `json:"field,required"` diff --git a/v2/tests/graph_edge_definitions_edges_test.go b/v2/tests/graph_edge_definitions_edges_test.go index b952283b..ac132b71 100644 --- a/v2/tests/graph_edge_definitions_edges_test.go +++ b/v2/tests/graph_edge_definitions_edges_test.go @@ -70,6 +70,17 @@ func Test_EdgeSimple(t *testing.T) { }) + t.Run("Get Edge documents", func(t *testing.T) { + edges, err := db.GetEdges(ctx, edgeColName, string(fromVertex.ID), nil) + require.NoError(t, err) + require.Len(t, edges, 1) + + docRead := edges[0] + require.Equal(t, docKey, docRead.Key) + require.Equal(t, doc.From, docRead.From) + require.Equal(t, doc.To, docRead.To) + }) + t.Run("Update Edge", func(t *testing.T) { opts := arangodb.EdgeUpdateOptions{ WaitForSync: newBool(true), @@ -168,6 +179,25 @@ func Test_EdgeExtended(t *testing.T) { docKey = createEdgeResp.Key }) + t.Run("Get Edge documents", func(t *testing.T) { + opts := arangodb.GetEdgesOptions{ + Direction: arangodb.EdgeDirectionOut, + } + edges, err := db.GetEdges(ctx, edgeColName, string(fromVertex.ID), &opts) + require.NoError(t, err) + require.Len(t, edges, 1) + + docRead := edges[0] + require.Equal(t, docKey, docRead.Key) + require.Equal(t, doc.From, docRead.From) + require.Equal(t, doc.To, docRead.To) + + opts.Direction = arangodb.EdgeDirectionIn + edges, err = db.GetEdges(ctx, edgeColName, string(fromVertex.ID), &opts) + require.NoError(t, err) + require.Len(t, edges, 0) + }) + t.Run("Update Edge", func(t *testing.T) { var oldObject RouteEdge opts := arangodb.EdgeUpdateOptions{ From 237eb3842ada533afd06493e4fff8e8785c42cfa Mon Sep 17 00:00:00 2001 From: jwierzbo Date: Tue, 17 Sep 2024 14:25:40 +0200 Subject: [PATCH 2/4] fmt --- v2/arangodb/database_transaction_opts.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/v2/arangodb/database_transaction_opts.go b/v2/arangodb/database_transaction_opts.go index 02a3dd91..2cce5c56 100644 --- a/v2/arangodb/database_transaction_opts.go +++ b/v2/arangodb/database_transaction_opts.go @@ -21,8 +21,9 @@ package arangodb import ( - "github.com/arangodb/go-driver/v2/connection" "time" + + "github.com/arangodb/go-driver/v2/connection" ) // BeginTransactionOptions provides options for BeginTransaction call From b77740fdda8f7c710f1e90827dce248c15762384 Mon Sep 17 00:00:00 2001 From: jwierzbo Date: Tue, 17 Sep 2024 16:04:04 +0200 Subject: [PATCH 3/4] Use constants --- v2/arangodb/collection_documents_create.go | 26 +++++------ v2/arangodb/collection_documents_delete.go | 14 +++--- v2/arangodb/collection_documents_read.go | 12 ++--- v2/arangodb/collection_documents_replace.go | 18 ++++---- v2/arangodb/collection_documents_update.go | 22 ++++----- v2/arangodb/database_graph.go | 4 +- v2/arangodb/database_query.go | 4 +- v2/arangodb/database_transaction_opts.go | 8 ++-- .../graph_edge_definitions_edges_impl.go | 46 +++++++++---------- .../graph_vertex_collections_vertices_impl.go | 46 +++++++++---------- v2/arangodb/shared.go | 24 +++++++++- 11 files changed, 122 insertions(+), 102 deletions(-) diff --git a/v2/arangodb/collection_documents_create.go b/v2/arangodb/collection_documents_create.go index 0fdd2893..efc4c47c 100644 --- a/v2/arangodb/collection_documents_create.go +++ b/v2/arangodb/collection_documents_create.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2020-2023 ArangoDB GmbH, Cologne, Germany +// Copyright 2020-2024 ArangoDB GmbH, Cologne, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -171,51 +171,51 @@ func (c *CollectionDocumentCreateOptions) modifyRequest(r connection.Request) er } if c.WithWaitForSync != nil { - r.AddQuery("waitForSync", boolToString(*c.WithWaitForSync)) + r.AddQuery(QueryWaitForSync, boolToString(*c.WithWaitForSync)) } if c.Overwrite != nil { - r.AddQuery("overwrite", boolToString(*c.Overwrite)) + r.AddQuery(QueryOverwrite, boolToString(*c.Overwrite)) } if c.OverwriteMode != nil { - r.AddQuery("overwriteMode", c.OverwriteMode.String()) + r.AddQuery(QueryOverwriteMode, c.OverwriteMode.String()) } if c.Silent != nil { - r.AddQuery("silent", boolToString(*c.Silent)) + r.AddQuery(QuerySilent, boolToString(*c.Silent)) } if c.NewObject != nil { - r.AddQuery("returnNew", "true") + r.AddQuery(QueryReturnNew, "true") } if c.OldObject != nil { - r.AddQuery("returnOld", "true") + r.AddQuery(QueryReturnOld, "true") } if c.RefillIndexCaches != nil { - r.AddQuery("refillIndexCaches", boolToString(*c.RefillIndexCaches)) + r.AddQuery(QueryRefillIndexCaches, boolToString(*c.RefillIndexCaches)) } if c.KeepNull != nil { - r.AddQuery("keepNull", boolToString(*c.KeepNull)) + r.AddQuery(QueryKeepNull, boolToString(*c.KeepNull)) } if c.MergeObjects != nil { - r.AddQuery("mergeObjects", boolToString(*c.MergeObjects)) + r.AddQuery(QueryMergeObjects, boolToString(*c.MergeObjects)) } if c.IgnoreRevs != nil { - r.AddQuery("ignoreRevs", boolToString(*c.IgnoreRevs)) + r.AddQuery(QueryIgnoreRevs, boolToString(*c.IgnoreRevs)) } if c.IsRestore != nil { - r.AddQuery("isRestore", boolToString(*c.IsRestore)) + r.AddQuery(QueryIsRestore, boolToString(*c.IsRestore)) } if c.VersionAttribute != "" { - r.AddQuery("versionAttribute", c.VersionAttribute) + r.AddQuery(QueryVersionAttribute, c.VersionAttribute) } return nil diff --git a/v2/arangodb/collection_documents_delete.go b/v2/arangodb/collection_documents_delete.go index b2896b1c..3e7f8293 100644 --- a/v2/arangodb/collection_documents_delete.go +++ b/v2/arangodb/collection_documents_delete.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2023 ArangoDB GmbH, Cologne, Germany +// Copyright 2023-2024 ArangoDB GmbH, Cologne, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -95,27 +95,27 @@ func (c *CollectionDocumentDeleteOptions) modifyRequest(r connection.Request) er } if c.IfMatch != "" { - r.AddHeader("If-Match", c.IfMatch) + r.AddHeader(HeaderIfMatch, c.IfMatch) } if c.IgnoreRevs != nil { - r.AddQuery("ignoreRevs", boolToString(*c.IgnoreRevs)) + r.AddQuery(QueryIgnoreRevs, boolToString(*c.IgnoreRevs)) } if c.WithWaitForSync != nil { - r.AddQuery("waitForSync", boolToString(*c.WithWaitForSync)) + r.AddQuery(QueryWaitForSync, boolToString(*c.WithWaitForSync)) } if c.OldObject != nil { - r.AddQuery("returnOld", "true") + r.AddQuery(QueryReturnOld, "true") } if c.Silent != nil { - r.AddQuery("silent", boolToString(*c.Silent)) + r.AddQuery(QuerySilent, boolToString(*c.Silent)) } if c.RefillIndexCaches != nil { - r.AddQuery("refillIndexCaches", boolToString(*c.RefillIndexCaches)) + r.AddQuery(QueryRefillIndexCaches, boolToString(*c.RefillIndexCaches)) } return nil diff --git a/v2/arangodb/collection_documents_read.go b/v2/arangodb/collection_documents_read.go index 581c296d..ae664de0 100644 --- a/v2/arangodb/collection_documents_read.go +++ b/v2/arangodb/collection_documents_read.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2020-2023 ArangoDB GmbH, Cologne, Germany +// Copyright 2020-2024 ArangoDB GmbH, Cologne, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -99,23 +99,23 @@ func (c *CollectionDocumentReadOptions) modifyRequest(r connection.Request) erro } if c.IfMatch != "" { - r.AddHeader("If-Match", c.IfMatch) + r.AddHeader(HeaderIfMatch, c.IfMatch) } if c.IfNoneMatch != "" { - r.AddHeader("If-None-Match", c.IfNoneMatch) + r.AddHeader(HeaderIfNoneMatch, c.IfNoneMatch) } if c.IgnoreRevs != nil { - r.AddQuery("ignoreRevs", boolToString(*c.IgnoreRevs)) + r.AddQuery(QueryIgnoreRevs, boolToString(*c.IgnoreRevs)) } if c.AllowDirtyReads != nil { - r.AddHeader("x-arango-allow-dirty-read", boolToString(*c.AllowDirtyReads)) + r.AddHeader(HeaderDirtyReads, boolToString(*c.AllowDirtyReads)) } if c.TransactionID != "" { - r.AddHeader("x-arango-trx-id", c.TransactionID) + r.AddHeader(HeaderTransaction, c.TransactionID) } return nil diff --git a/v2/arangodb/collection_documents_replace.go b/v2/arangodb/collection_documents_replace.go index 317a2b1a..9cce18e6 100644 --- a/v2/arangodb/collection_documents_replace.go +++ b/v2/arangodb/collection_documents_replace.go @@ -111,39 +111,39 @@ func (c *CollectionDocumentReplaceOptions) modifyRequest(r connection.Request) e } if c.IfMatch != "" { - r.AddHeader("If-Match", c.IfMatch) + r.AddHeader(HeaderIfMatch, c.IfMatch) } if c.WithWaitForSync != nil { - r.AddQuery("waitForSync", boolToString(*c.WithWaitForSync)) + r.AddQuery(QueryWaitForSync, boolToString(*c.WithWaitForSync)) } if c.Silent != nil { - r.AddQuery("silent", boolToString(*c.Silent)) + r.AddQuery(QuerySilent, boolToString(*c.Silent)) } if c.NewObject != nil { - r.AddQuery("returnNew", "true") + r.AddQuery(QueryReturnNew, "true") } if c.OldObject != nil { - r.AddQuery("returnOld", "true") + r.AddQuery(QueryReturnOld, "true") } if c.RefillIndexCaches != nil { - r.AddQuery("refillIndexCaches", boolToString(*c.RefillIndexCaches)) + r.AddQuery(QueryRefillIndexCaches, boolToString(*c.RefillIndexCaches)) } if c.IgnoreRevs != nil { - r.AddQuery("ignoreRevs", boolToString(*c.IgnoreRevs)) + r.AddQuery(QueryIgnoreRevs, boolToString(*c.IgnoreRevs)) } if c.IsRestore != nil { - r.AddQuery("isRestore", boolToString(*c.IsRestore)) + r.AddQuery(QueryIsRestore, boolToString(*c.IsRestore)) } if c.VersionAttribute != "" { - r.AddQuery("versionAttribute", c.VersionAttribute) + r.AddQuery(QueryVersionAttribute, c.VersionAttribute) } return nil diff --git a/v2/arangodb/collection_documents_update.go b/v2/arangodb/collection_documents_update.go index 8f00c9dd..cefdb6cd 100644 --- a/v2/arangodb/collection_documents_update.go +++ b/v2/arangodb/collection_documents_update.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2020-2023 ArangoDB GmbH, Cologne, Germany +// Copyright 2020-2024 ArangoDB GmbH, Cologne, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -119,43 +119,43 @@ func (c *CollectionDocumentUpdateOptions) modifyRequest(r connection.Request) er } if c.IfMatch != "" { - r.AddHeader("If-Match", c.IfMatch) + r.AddHeader(HeaderIfMatch, c.IfMatch) } if c.WithWaitForSync != nil { - r.AddQuery("waitForSync", boolToString(*c.WithWaitForSync)) + r.AddQuery(QueryWaitForSync, boolToString(*c.WithWaitForSync)) } if c.Silent != nil { - r.AddQuery("silent", boolToString(*c.Silent)) + r.AddQuery(QuerySilent, boolToString(*c.Silent)) } if c.NewObject != nil { - r.AddQuery("returnNew", "true") + r.AddQuery(QueryReturnNew, "true") } if c.OldObject != nil { - r.AddQuery("returnOld", "true") + r.AddQuery(QueryReturnOld, "true") } if c.RefillIndexCaches != nil { - r.AddQuery("refillIndexCaches", boolToString(*c.RefillIndexCaches)) + r.AddQuery(QueryRefillIndexCaches, boolToString(*c.RefillIndexCaches)) } if c.KeepNull != nil { - r.AddQuery("keepNull", boolToString(*c.KeepNull)) + r.AddQuery(QueryKeepNull, boolToString(*c.KeepNull)) } if c.MergeObjects != nil { - r.AddQuery("mergeObjects", boolToString(*c.MergeObjects)) + r.AddQuery(QueryMergeObjects, boolToString(*c.MergeObjects)) } if c.IgnoreRevs != nil { - r.AddQuery("ignoreRevs", boolToString(*c.IgnoreRevs)) + r.AddQuery(QueryIgnoreRevs, boolToString(*c.IgnoreRevs)) } if c.VersionAttribute != "" { - r.AddQuery("versionAttribute", c.VersionAttribute) + r.AddQuery(QueryVersionAttribute, c.VersionAttribute) } return nil diff --git a/v2/arangodb/database_graph.go b/v2/arangodb/database_graph.go index cde0dd07..2ab08a58 100644 --- a/v2/arangodb/database_graph.go +++ b/v2/arangodb/database_graph.go @@ -100,11 +100,11 @@ func (q *GetEdgesOptions) modifyRequest(r connection.Request) error { } if q.AllowDirtyReads != nil { - r.AddHeader("x-arango-allow-dirty-read", boolToString(*q.AllowDirtyReads)) + r.AddHeader(HeaderDirtyReads, boolToString(*q.AllowDirtyReads)) } if q.Direction != "" { - r.AddQuery("direction", string(q.Direction)) + r.AddQuery(QueryDirection, string(q.Direction)) } return nil diff --git a/v2/arangodb/database_query.go b/v2/arangodb/database_query.go index c6729d92..43aee84a 100644 --- a/v2/arangodb/database_query.go +++ b/v2/arangodb/database_query.go @@ -142,11 +142,11 @@ func (q *QueryOptions) modifyRequest(r connection.Request) error { } if q.AllowDirtyReads != nil { - r.AddHeader("x-arango-allow-dirty-read", boolToString(*q.AllowDirtyReads)) + r.AddHeader(HeaderDirtyReads, boolToString(*q.AllowDirtyReads)) } if q.TransactionID != "" { - r.AddHeader("x-arango-trx-id", q.TransactionID) + r.AddHeader(HeaderTransaction, q.TransactionID) } return nil diff --git a/v2/arangodb/database_transaction_opts.go b/v2/arangodb/database_transaction_opts.go index 2cce5c56..c6cda662 100644 --- a/v2/arangodb/database_transaction_opts.go +++ b/v2/arangodb/database_transaction_opts.go @@ -59,13 +59,13 @@ type BeginTransactionOptions struct { LockTimeoutDuration time.Duration `json:"-"` } -func (q *BeginTransactionOptions) modifyRequest(r connection.Request) error { - if q == nil { +func (b *BeginTransactionOptions) modifyRequest(r connection.Request) error { + if b == nil { return nil } - if q.AllowDirtyReads != nil { - r.AddHeader("x-arango-allow-dirty-read", boolToString(*q.AllowDirtyReads)) + if b.AllowDirtyReads != nil { + r.AddHeader(HeaderDirtyReads, boolToString(*b.AllowDirtyReads)) } return nil diff --git a/v2/arangodb/graph_edge_definitions_edges_impl.go b/v2/arangodb/graph_edge_definitions_edges_impl.go index 8e4a370a..bcf1ec4f 100644 --- a/v2/arangodb/graph_edge_definitions_edges_impl.go +++ b/v2/arangodb/graph_edge_definitions_edges_impl.go @@ -84,19 +84,19 @@ func (g *GetEdgeOptions) modifyRequest(r connection.Request) error { } if g.Rev != "" { - r.AddQuery("rev", g.Rev) + r.AddQuery(QueryRev, g.Rev) } if g.IfMatch != "" { - r.AddHeader("If-Match", g.IfMatch) + r.AddHeader(HeaderIfMatch, g.IfMatch) } if g.IfNoneMatch != "" { - r.AddHeader("If-None-Match", g.IfNoneMatch) + r.AddHeader(HeaderIfNoneMatch, g.IfNoneMatch) } if g.TransactionID != "" { - r.AddHeader("x-arango-trx-id", g.TransactionID) + r.AddHeader(HeaderTransaction, g.TransactionID) } return nil @@ -142,15 +142,15 @@ func (c *CreateEdgeOptions) modifyRequest(r connection.Request) error { } if c.WaitForSync != nil { - r.AddQuery("waitForSync", boolToString(*c.WaitForSync)) + r.AddQuery(QueryWaitForSync, boolToString(*c.WaitForSync)) } if c.NewObject != nil { - r.AddQuery("returnNew", "true") + r.AddQuery(QueryReturnNew, "true") } if c.TransactionID != "" { - r.AddHeader("x-arango-trx-id", c.TransactionID) + r.AddHeader(HeaderTransaction, c.TransactionID) } return nil @@ -199,27 +199,27 @@ func (v *EdgeUpdateOptions) modifyRequest(r connection.Request) error { } if v.WaitForSync != nil { - r.AddQuery("waitForSync", boolToString(*v.WaitForSync)) + r.AddQuery(QueryWaitForSync, boolToString(*v.WaitForSync)) } if v.NewObject != nil { - r.AddQuery("returnNew", "true") + r.AddQuery(QueryReturnNew, "true") } if v.OldObject != nil { - r.AddQuery("returnOld", "true") + r.AddQuery(QueryReturnOld, "true") } if v.KeepNull != nil { - r.AddQuery("keepNull", boolToString(*v.KeepNull)) + r.AddQuery(QueryKeepNull, boolToString(*v.KeepNull)) } if v.IfMatch != "" { - r.AddHeader("If-Match", v.IfMatch) + r.AddHeader(HeaderIfMatch, v.IfMatch) } if v.TransactionID != "" { - r.AddHeader("x-arango-trx-id", v.TransactionID) + r.AddHeader(HeaderTransaction, v.TransactionID) } return nil @@ -269,27 +269,27 @@ func (v *EdgeReplaceOptions) modifyRequest(r connection.Request) error { } if v.WaitForSync != nil { - r.AddQuery("waitForSync", boolToString(*v.WaitForSync)) + r.AddQuery(QueryWaitForSync, boolToString(*v.WaitForSync)) } if v.NewObject != nil { - r.AddQuery("returnNew", "true") + r.AddQuery(QueryReturnNew, "true") } if v.OldObject != nil { - r.AddQuery("returnOld", "true") + r.AddQuery(QueryReturnOld, "true") } if v.KeepNull != nil { - r.AddQuery("keepNull", boolToString(*v.KeepNull)) + r.AddQuery(QueryKeepNull, boolToString(*v.KeepNull)) } if v.IfMatch != "" { - r.AddHeader("If-Match", v.IfMatch) + r.AddHeader(HeaderIfMatch, v.IfMatch) } if v.TransactionID != "" { - r.AddHeader("x-arango-trx-id", v.TransactionID) + r.AddHeader(HeaderTransaction, v.TransactionID) } return nil @@ -322,19 +322,19 @@ func (c *DeleteEdgeOptions) modifyRequest(r connection.Request) error { } if c.WaitForSync != nil { - r.AddQuery("waitForSync", boolToString(*c.WaitForSync)) + r.AddQuery(QueryWaitForSync, boolToString(*c.WaitForSync)) } if c.OldObject != nil { - r.AddQuery("returnOld", "true") + r.AddQuery(QueryReturnOld, "true") } if c.IfMatch != "" { - r.AddHeader("If-Match", c.IfMatch) + r.AddHeader(HeaderIfMatch, c.IfMatch) } if c.TransactionID != "" { - r.AddHeader("x-arango-trx-id", c.TransactionID) + r.AddHeader(HeaderTransaction, c.TransactionID) } return nil diff --git a/v2/arangodb/graph_vertex_collections_vertices_impl.go b/v2/arangodb/graph_vertex_collections_vertices_impl.go index 32788d43..e823f890 100644 --- a/v2/arangodb/graph_vertex_collections_vertices_impl.go +++ b/v2/arangodb/graph_vertex_collections_vertices_impl.go @@ -84,19 +84,19 @@ func (g *GetVertexOptions) modifyRequest(r connection.Request) error { } if g.Rev != "" { - r.AddQuery("rev", g.Rev) + r.AddQuery(QueryRev, g.Rev) } if g.IfMatch != "" { - r.AddHeader("If-Match", g.IfMatch) + r.AddHeader(HeaderIfMatch, g.IfMatch) } if g.IfNoneMatch != "" { - r.AddHeader("If-None-Match", g.IfNoneMatch) + r.AddHeader(HeaderIfNoneMatch, g.IfNoneMatch) } if g.TransactionID != "" { - r.AddHeader("x-arango-trx-id", g.TransactionID) + r.AddHeader(HeaderTransaction, g.TransactionID) } return nil @@ -142,15 +142,15 @@ func (c *CreateVertexOptions) modifyRequest(r connection.Request) error { } if c.WaitForSync != nil { - r.AddQuery("waitForSync", boolToString(*c.WaitForSync)) + r.AddQuery(QueryWaitForSync, boolToString(*c.WaitForSync)) } if c.NewObject != nil { - r.AddQuery("returnNew", "true") + r.AddQuery(QueryReturnNew, "true") } if c.TransactionID != "" { - r.AddHeader("x-arango-trx-id", c.TransactionID) + r.AddHeader(HeaderTransaction, c.TransactionID) } return nil @@ -199,27 +199,27 @@ func (v *VertexUpdateOptions) modifyRequest(r connection.Request) error { } if v.WaitForSync != nil { - r.AddQuery("waitForSync", boolToString(*v.WaitForSync)) + r.AddQuery(QueryWaitForSync, boolToString(*v.WaitForSync)) } if v.NewObject != nil { - r.AddQuery("returnNew", "true") + r.AddQuery(QueryReturnNew, "true") } if v.OldObject != nil { - r.AddQuery("returnOld", "true") + r.AddQuery(QueryReturnOld, "true") } if v.KeepNull != nil { - r.AddQuery("keepNull", boolToString(*v.KeepNull)) + r.AddQuery(QueryKeepNull, boolToString(*v.KeepNull)) } if v.IfMatch != "" { - r.AddHeader("If-Match", v.IfMatch) + r.AddHeader(HeaderIfMatch, v.IfMatch) } if v.TransactionID != "" { - r.AddHeader("x-arango-trx-id", v.TransactionID) + r.AddHeader(HeaderTransaction, v.TransactionID) } return nil @@ -269,27 +269,27 @@ func (v *VertexReplaceOptions) modifyRequest(r connection.Request) error { } if v.WaitForSync != nil { - r.AddQuery("waitForSync", boolToString(*v.WaitForSync)) + r.AddQuery(QueryWaitForSync, boolToString(*v.WaitForSync)) } if v.NewObject != nil { - r.AddQuery("returnNew", "true") + r.AddQuery(QueryReturnNew, "true") } if v.OldObject != nil { - r.AddQuery("returnOld", "true") + r.AddQuery(QueryReturnOld, "true") } if v.KeepNull != nil { - r.AddQuery("keepNull", boolToString(*v.KeepNull)) + r.AddQuery(QueryKeepNull, boolToString(*v.KeepNull)) } if v.IfMatch != "" { - r.AddHeader("If-Match", v.IfMatch) + r.AddHeader(HeaderIfMatch, v.IfMatch) } if v.TransactionID != "" { - r.AddHeader("x-arango-trx-id", v.TransactionID) + r.AddHeader(HeaderTransaction, v.TransactionID) } return nil @@ -322,19 +322,19 @@ func (c *DeleteVertexOptions) modifyRequest(r connection.Request) error { } if c.WaitForSync != nil { - r.AddQuery("waitForSync", boolToString(*c.WaitForSync)) + r.AddQuery(QueryWaitForSync, boolToString(*c.WaitForSync)) } if c.OldObject != nil { - r.AddQuery("returnOld", "true") + r.AddQuery(QueryReturnOld, "true") } if c.IfMatch != "" { - r.AddHeader("If-Match", c.IfMatch) + r.AddHeader(HeaderIfMatch, c.IfMatch) } if c.TransactionID != "" { - r.AddHeader("x-arango-trx-id", c.TransactionID) + r.AddHeader(HeaderTransaction, c.TransactionID) } return nil diff --git a/v2/arangodb/shared.go b/v2/arangodb/shared.go index 2dbd7b3f..7ccedf85 100644 --- a/v2/arangodb/shared.go +++ b/v2/arangodb/shared.go @@ -17,11 +17,31 @@ // // Copyright holder is ArangoDB GmbH, Cologne, Germany // -// Author Jakub Wierzbowski -// package arangodb +const ( + HeaderDirtyReads = "x-arango-allow-dirty-read" + HeaderTransaction = "x-arango-trx-id" + HeaderIfMatch = "If-Match" + HeaderIfNoneMatch = "If-None-Match" + + QueryRev = "rev" + QueryIgnoreRevs = "ignoreRevs" + QueryWaitForSync = "waitForSync" + QueryReturnNew = "returnNew" + QueryReturnOld = "returnOld" + QueryKeepNull = "keepNull" + QueryDirection = "direction" + QuerySilent = "silent" + QueryRefillIndexCaches = "refillIndexCaches" + QueryMergeObjects = "mergeObjects" + QueryOverwrite = "overwrite" + QueryOverwriteMode = "overwriteMode" + QueryVersionAttribute = "versionAttribute" + QueryIsRestore = "isRestore" +) + // PrimarySortCompression Defines how to compress the primary sort data (introduced in v3.7.1) type PrimarySortCompression string From 7b07727650659efc6c35ec3e1eca74940263e587 Mon Sep 17 00:00:00 2001 From: jwierzbo Date: Wed, 18 Sep 2024 09:55:26 +0200 Subject: [PATCH 4/4] Update CHANGELOG.md --- CHANGELOG.md | 2 -- v2/CHANGELOG.md | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cbad1fb6..02d967ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,6 @@ ## [master](https://github.com/arangodb/go-driver/tree/master) (N/A) - Switch to Go 1.22.5 - Switch to Go 1.22.6 -- [v2] Support for missing dirty read options (query, transaction apis) -- [v2] Get inbound and outbound edges ## [1.6.2](https://github.com/arangodb/go-driver/tree/v1.6.2) (2024-04-02) - Switch to Go 1.20.11 diff --git a/v2/CHANGELOG.md b/v2/CHANGELOG.md index 36a91783..6cc6a8b4 100644 --- a/v2/CHANGELOG.md +++ b/v2/CHANGELOG.md @@ -3,6 +3,9 @@ ## [master](https://github.com/arangodb/go-driver/tree/master) (N/A) - Improve backup tests stability - CheckAvailability function for the specific member +- Switch to Go 1.22.6 +- Support for missing dirty read options (query, transaction apis) +- Get inbound and outbound edges ## [2.1.0](https://github.com/arangodb/go-driver/tree/v2.1.0) (2024-04-02) - Switch to Go 1.21.5