Skip to content

Commit

Permalink
[Feature] [3.7] Extend graph parameters (#275)
Browse files Browse the repository at this point in the history
  • Loading branch information
ajanikow authored Aug 10, 2020
1 parent 13b09cd commit 6e51632
Show file tree
Hide file tree
Showing 8 changed files with 271 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- Use internal coordinator communication for cursors if specified coordinator was not found on endpoint list
- Add support for Overwrite Mode (ArangoDB 3.7)
- Add support for Schema Collection options (ArangoDB 3.7)
- Add support for Disjoint and Satellite Graphs options (ArangoDB 3.7)

## [1.0.0](https://github.com/arangodb/go-driver/tree/1.0.0) (N/A)
- Enable proper CHANGELOG and versioning
6 changes: 6 additions & 0 deletions database_graphs.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ package driver

import "context"

const (
SatelliteGraph = -100
)

// DatabaseGraphs provides access to all graphs in a single database.
type DatabaseGraphs interface {
// Graph opens a connection to an existing graph within the database.
Expand Down Expand Up @@ -64,6 +68,8 @@ type CreateGraphOptions struct {
// WriteConcern is the number of min replication factor that is used for every collection within this graph.
// Cannot be modified later.
WriteConcern int
// IsDisjoint set isDisjoint flag for Graph. Required ArangoDB 3.7+
IsDisjoint bool
}

// EdgeDefinition contains all information needed to define a single edge in a graph.
Expand Down
68 changes: 61 additions & 7 deletions database_graphs_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ package driver

import (
"context"
"encoding/json"
"path"

"github.com/pkg/errors"
)

// Graph opens a connection to an existing graph within the database.
Expand All @@ -42,7 +45,11 @@ func (d *database) Graph(ctx context.Context, name string) (Graph, error) {
if err := resp.CheckStatus(200); err != nil {
return nil, WithStack(err)
}
g, err := newGraph(name, d)
var data getGraphResponse
if err := resp.ParseBody("", &data); err != nil {
return nil, WithStack(err)
}
g, err := newGraph(data.Graph, d)
if err != nil {
return nil, WithStack(err)
}
Expand Down Expand Up @@ -70,7 +77,7 @@ func (d *database) GraphExists(ctx context.Context, name string) (bool, error) {
}

type getGraphsResponse struct {
Graphs []DocumentMeta `json:"graphs,omitempty"`
Graphs []graphDefinition `json:"graphs,omitempty"`
}

// Graphs returns a list of all graphs in the database.
Expand All @@ -92,7 +99,7 @@ func (d *database) Graphs(ctx context.Context) ([]Graph, error) {
}
result := make([]Graph, 0, len(data.Graphs))
for _, info := range data.Graphs {
g, err := newGraph(info.Key, d)
g, err := newGraph(info, d)
if err != nil {
return nil, WithStack(err)
}
Expand All @@ -109,6 +116,40 @@ type createGraphOptions struct {
Options *createGraphAdditionalOptions `json:"options,omitempty"`
}

type graphReplicationFactor int

func (g graphReplicationFactor) MarshalJSON() ([]byte, error) {
switch g {
case SatelliteGraph:
return json.Marshal(replicationFactorSatelliteString)
default:
return json.Marshal(int(g))
}
}

func (g *graphReplicationFactor) UnmarshalJSON(data []byte) error {
var d int

if err := json.Unmarshal(data, &d); err == nil {
*g = graphReplicationFactor(d)
return nil
}

var s string

if err := json.Unmarshal(data, &s); err != nil {
return err
}

switch s {
case replicationFactorSatelliteString:
*g = graphReplicationFactor(SatelliteGraph)
return nil
default:
return errors.Errorf("Unsupported type %s", s)
}
}

type createGraphAdditionalOptions struct {
// SmartGraphAttribute is the attribute name that is used to smartly shard the vertices of a graph.
// Every vertex in this Graph has to have this attribute.
Expand All @@ -119,10 +160,12 @@ type createGraphAdditionalOptions struct {
NumberOfShards int `json:"numberOfShards,omitempty"`
// ReplicationFactor is the number of replication factor that is used for every collection within this graph.
// Cannot be modified later.
ReplicationFactor int `json:"replicationFactor,omitempty"`
ReplicationFactor graphReplicationFactor `json:"replicationFactor,omitempty"`
// WriteConcern is the number of min replication factor that is used for every collection within this graph.
// Cannot be modified later.
WriteConcern int `json:"writeConcern,omitempty"`
// IsDisjoint set isDisjoint flag for Graph. Required ArangoDB 3.7+
IsDisjoint bool `json:"isDisjoint,omitempty"`
}

// CreateGraph creates a new graph with given name and options, and opens a connection to it.
Expand All @@ -135,12 +178,19 @@ func (d *database) CreateGraph(ctx context.Context, name string, options *Create
input.OrphanVertexCollections = options.OrphanVertexCollections
input.EdgeDefinitions = options.EdgeDefinitions
input.IsSmart = options.IsSmart
if options.SmartGraphAttribute != "" || options.NumberOfShards != 0 {
if options.ReplicationFactor == SatelliteGraph {
input.Options = &createGraphAdditionalOptions{
SmartGraphAttribute: options.SmartGraphAttribute,
ReplicationFactor: graphReplicationFactor(options.ReplicationFactor),
IsDisjoint: options.IsDisjoint,
}
} else if options.SmartGraphAttribute != "" || options.NumberOfShards != 0 {
input.Options = &createGraphAdditionalOptions{
SmartGraphAttribute: options.SmartGraphAttribute,
NumberOfShards: options.NumberOfShards,
ReplicationFactor: options.ReplicationFactor,
ReplicationFactor: graphReplicationFactor(options.ReplicationFactor),
WriteConcern: options.WriteConcern,
IsDisjoint: options.IsDisjoint,
}
}
}
Expand All @@ -158,7 +208,11 @@ func (d *database) CreateGraph(ctx context.Context, name string, options *Create
if err := resp.CheckStatus(201, 202); err != nil {
return nil, WithStack(err)
}
g, err := newGraph(name, d)
var data getGraphResponse
if err := resp.ParseBody("", &data); err != nil {
return nil, WithStack(err)
}
g, err := newGraph(data.Graph, d)
if err != nil {
return nil, WithStack(err)
}
Expand Down
9 changes: 9 additions & 0 deletions graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ type Graph interface {
// If the graph does not exist, a NotFoundError is returned.
Remove(ctx context.Context) error

// IsSmart returns true of smart is smart. In case of Community Edition it is always false
IsSmart() bool

// IsSatellite returns true of smart is satellite. In case of Community Edition it is always false
IsSatellite() bool

// IsDisjoint return information if graph have isDisjoint flag set to true
IsDisjoint() bool

// Edge collection functions
GraphEdgeCollections

Expand Down
13 changes: 10 additions & 3 deletions graph_edge_collections_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,17 @@ import (
"path"
)

type graphDefinition struct {
Name string `json:"name"`
IsSmart bool `json:"isSmart"`
IsSatellite bool `json:"isSatellite"`
IsDisjoint bool `json:"isDisjoint,omitempty"`

EdgeDefinitions []EdgeDefinition `json:"edgeDefinitions,omitempty"`
}

type getGraphResponse struct {
Graph struct {
EdgeDefinitions []EdgeDefinition `json:"edgeDefinitions,omitempty"`
} `json:"graph"`
Graph graphDefinition `json:"graph"`
}

// EdgeCollection opens a connection to an existing edge-collection within the graph.
Expand Down
32 changes: 22 additions & 10 deletions graph_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,35 +28,47 @@ import (
)

// newGraph creates a new Graph implementation.
func newGraph(name string, db *database) (Graph, error) {
if name == "" {
func newGraph(input graphDefinition, db *database) (Graph, error) {
if input.Name == "" {
return nil, WithStack(InvalidArgumentError{Message: "name is empty"})
}
if db == nil {
return nil, WithStack(InvalidArgumentError{Message: "db is nil"})
}
return &graph{
name: name,
db: db,
conn: db.conn,
input: input,
db: db,
conn: db.conn,
}, nil
}

type graph struct {
name string
db *database
conn Connection
input graphDefinition
db *database
conn Connection
}

func (g *graph) IsSmart() bool {
return g.input.IsSmart
}

func (g *graph) IsDisjoint() bool {
return g.input.IsDisjoint
}

func (g *graph) IsSatellite() bool {
return g.input.IsSatellite
}

// relPath creates the relative path to this graph (`_db/<db-name>/_api/gharial/<graph-name>`)
func (g *graph) relPath() string {
escapedName := pathEscape(g.name)
escapedName := pathEscape(g.Name())
return path.Join(g.db.relPath(), "_api", "gharial", escapedName)
}

// Name returns the name of the graph.
func (g *graph) Name() string {
return g.name
return g.input.Name
}

// Remove removes the entire graph.
Expand Down
133 changes: 133 additions & 0 deletions test/graph_creation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,136 @@ func Test_Graph_AdvancedCreate_Defaults(t *testing.T) {
}
})
}

func TestGraphCreation(t *testing.T) {
// Arrange
ctx := context.Background()

c := createClientFromEnv(t, true)
EnsureVersion(t, ctx, c).MinimumVersion("3.7.0").Cluster().Enterprise()

t.Run("Satellite", func(t *testing.T) {
db := ensureDatabase(ctx, c, databaseName("graph", "create", "defaults"), nil, t)

// Create
graphID := db.Name() + "_graph"

options, collections := newGraphOpts(db)

options.ReplicationFactor = driver.SatelliteGraph

g, err := db.CreateGraph(ctx, graphID, &options)
require.NoError(t, err)

// Wait for collections to be created
waitForCollections(t, db, collections)

require.True(t, g.IsSatellite())
})

t.Run("Satellite - list", func(t *testing.T) {
db := ensureDatabase(ctx, c, databaseName("graph", "create", "defaults"), nil, t)

// Create
graphID := db.Name() + "_graph"

options, collections := newGraphOpts(db)

options.ReplicationFactor = driver.SatelliteGraph

g, err := db.CreateGraph(ctx, graphID, &options)
require.NoError(t, err)

// Wait for collections to be created
waitForCollections(t, db, collections)

graphs, err := db.Graphs(ctx)
require.NoError(t, err)
require.Len(t, graphs, 1)

require.Equal(t, g.Name(), graphs[0].Name())
require.True(t, graphs[0].IsSatellite())
})

t.Run("Standard", func(t *testing.T) {
db := ensureDatabase(ctx, c, databaseName("graph", "create", "defaults"), nil, t)

// Create
graphID := db.Name() + "_graph"

options, collections := newGraphOpts(db)

g, err := db.CreateGraph(ctx, graphID, &options)
require.NoError(t, err)

// Wait for collections to be created
waitForCollections(t, db, collections)

require.False(t, g.IsSatellite())
})

t.Run("Standard - list", func(t *testing.T) {
db := ensureDatabase(ctx, c, databaseName("graph", "create", "defaults"), nil, t)

// Create
graphID := db.Name() + "_graph"

options, collections := newGraphOpts(db)

g, err := db.CreateGraph(ctx, graphID, &options)
require.NoError(t, err)

// Wait for collections to be created
waitForCollections(t, db, collections)

graphs, err := db.Graphs(ctx)
require.NoError(t, err)
require.Len(t, graphs, 1)

require.Equal(t, g.Name(), graphs[0].Name())
require.False(t, graphs[0].IsSatellite())
})

t.Run("Disjoint", func(t *testing.T) {
db := ensureDatabase(ctx, c, databaseName("graph", "create", "defaults"), nil, t)

// Create
graphID := db.Name() + "_graph"

options, collections := newGraphOpts(db)

options.IsDisjoint = true

g, err := db.CreateGraph(ctx, graphID, &options)
require.NoError(t, err)

// Wait for collections to be created
waitForCollections(t, db, collections)

require.True(t, g.IsDisjoint())
})

t.Run("Disjoint - list", func(t *testing.T) {
db := ensureDatabase(ctx, c, databaseName("graph", "create", "defaults"), nil, t)

// Create
graphID := db.Name() + "_graph"

options, collections := newGraphOpts(db)

options.IsDisjoint = true

g, err := db.CreateGraph(ctx, graphID, &options)
require.NoError(t, err)

// Wait for collections to be created
waitForCollections(t, db, collections)

graphs, err := db.Graphs(ctx)
require.NoError(t, err)
require.Len(t, graphs, 1)

require.Equal(t, g.Name(), graphs[0].Name())
require.True(t, graphs[0].IsDisjoint())
})
}
Loading

0 comments on commit 6e51632

Please sign in to comment.