Skip to content

Commit 2f09ee9

Browse files
authored
[Feature] Hybrid smartgraphs (#355)
1 parent 601a6a0 commit 2f09ee9

File tree

7 files changed

+196
-20
lines changed

7 files changed

+196
-20
lines changed

database_graphs.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ type CreateGraphOptions struct {
7171
// IsDisjoint set isDisjoint flag for Graph. Required ArangoDB 3.7+
7272
IsDisjoint bool
7373
// Satellites contains an array of collection names that will be used to create SatelliteCollections for a Hybrid (Disjoint) SmartGraph (Enterprise Edition only)
74-
Satellites []string `json:"satellites"`
74+
Satellites []string `json:"satellites,omitempty"`
7575
}
7676

7777
// EdgeDefinition contains all information needed to define a single edge in a graph.

database_graphs_impl.go

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,12 @@ type createGraphAdditionalOptions struct {
167167
// IsDisjoint set isDisjoint flag for Graph. Required ArangoDB 3.7+
168168
IsDisjoint bool `json:"isDisjoint,omitempty"`
169169
// Satellites contains an array of collection names that will be used to create SatelliteCollections for a Hybrid (Disjoint) SmartGraph (Enterprise Edition only)
170-
Satellites []string `json:"satellites"`
170+
Satellites []string `json:"satellites,omitempty"`
171171
}
172172

173173
// CreateGraph creates a new graph with given name and options, and opens a connection to it.
174174
// If a graph with given name already exists within the database, a DuplicateError is returned.
175+
// todo test
175176
func (d *database) CreateGraph(ctx context.Context, name string, options *CreateGraphOptions) (Graph, error) {
176177
input := createGraphOptions{
177178
Name: name,
@@ -180,22 +181,13 @@ func (d *database) CreateGraph(ctx context.Context, name string, options *Create
180181
input.OrphanVertexCollections = options.OrphanVertexCollections
181182
input.EdgeDefinitions = options.EdgeDefinitions
182183
input.IsSmart = options.IsSmart
183-
if options.ReplicationFactor == SatelliteGraph {
184-
input.Options = &createGraphAdditionalOptions{
185-
SmartGraphAttribute: options.SmartGraphAttribute,
186-
ReplicationFactor: graphReplicationFactor(options.ReplicationFactor),
187-
IsDisjoint: options.IsDisjoint,
188-
Satellites: options.Satellites,
189-
}
190-
} else if options.SmartGraphAttribute != "" || options.NumberOfShards != 0 {
191-
input.Options = &createGraphAdditionalOptions{
192-
SmartGraphAttribute: options.SmartGraphAttribute,
193-
NumberOfShards: options.NumberOfShards,
194-
ReplicationFactor: graphReplicationFactor(options.ReplicationFactor),
195-
WriteConcern: options.WriteConcern,
196-
IsDisjoint: options.IsDisjoint,
197-
Satellites: options.Satellites,
198-
}
184+
input.Options = &createGraphAdditionalOptions{
185+
SmartGraphAttribute: options.SmartGraphAttribute,
186+
NumberOfShards: options.NumberOfShards,
187+
ReplicationFactor: graphReplicationFactor(options.ReplicationFactor),
188+
WriteConcern: options.WriteConcern,
189+
IsDisjoint: options.IsDisjoint,
190+
Satellites: options.Satellites,
199191
}
200192
}
201193
req, err := d.conn.NewRequest("POST", path.Join(d.relPath(), "_api/gharial"))

graph_edge_collections.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,5 @@ type VertexConstraints struct {
6262
// CreateEdgeCollectionOptions contains optional parameters for creating a new edge collection
6363
type CreateEdgeCollectionOptions struct {
6464
// Satellites contains an array of collection names that will be used to create SatelliteCollections for a Hybrid (Disjoint) SmartGraph (Enterprise Edition only)
65-
Satellites []string `json:"satellites"`
65+
Satellites []string `json:"satellites,omitempty"`
6666
}

graph_vertex_collections.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,5 @@ type GraphVertexCollections interface {
4949
// CreateVertexCollectionOptions contains optional parameters for creating a new vertex collection
5050
type CreateVertexCollectionOptions struct {
5151
// Satellites contains an array of collection names that will be used to create SatelliteCollections for a Hybrid (Disjoint) SmartGraph (Enterprise Edition only)
52-
Satellites []string `json:"satellites"`
52+
Satellites []string `json:"satellites,omitempty"`
5353
}

test/edge_collection_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,80 @@ func TestCreateEdgeCollection(t *testing.T) {
109109
}
110110
}
111111

112+
// TestCreateSatelliteEdgeCollection creates a graph and then adds an Satellite edge collection in it
113+
func TestCreateSatelliteEdgeCollection(t *testing.T) {
114+
ctx := context.Background()
115+
116+
c := createClientFromEnv(t, true)
117+
EnsureVersion(t, ctx, c).CheckVersion(MinimumVersion("3.9.0")).Cluster().Enterprise()
118+
119+
db := ensureDatabase(nil, c, "edge_collection_test", nil, t)
120+
121+
name := "test_create_sat_edge_collection"
122+
options := driver.CreateGraphOptions{
123+
IsSmart: true,
124+
SmartGraphAttribute: "test",
125+
}
126+
g, err := db.CreateGraph(ctx, name, &options)
127+
if err != nil {
128+
t.Fatalf("Failed to create graph '%s': %s", name, describe(err))
129+
}
130+
131+
// List edge collections, must be empty
132+
if list, _, err := g.EdgeCollections(nil); err != nil {
133+
t.Errorf("EdgeCollections failed: %s", describe(err))
134+
} else if len(list) > 0 {
135+
t.Errorf("EdgeCollections return %d edge collections, expected 0", len(list))
136+
}
137+
138+
// Now create an edge collection
139+
colName := "create_sat_edge_collection"
140+
col1Name := "sat_edge_collection1"
141+
col2Name := "sat_edge_collection2"
142+
143+
opt := driver.CreateEdgeCollectionOptions{Satellites: []string{col1Name}}
144+
if ec, err := g.CreateEdgeCollectionWithOptions(nil, colName, driver.VertexConstraints{From: []string{col1Name}, To: []string{col2Name}}, opt); err != nil {
145+
t.Errorf("CreateEdgeCollection failed: %s", describe(err))
146+
} else if ec.Name() != colName {
147+
t.Errorf("Invalid name, expected '%s', got '%s'", colName, ec.Name())
148+
}
149+
150+
assertCollection(nil, db, colName, t)
151+
assertCollection(nil, db, col1Name, t)
152+
assertCollection(nil, db, col2Name, t)
153+
154+
if list, constraints, err := g.EdgeCollections(nil); err != nil {
155+
t.Errorf("EdgeCollections failed: %s", describe(err))
156+
} else {
157+
if len(list) != 1 {
158+
t.Errorf("EdgeCollections return %d edge collections, expected 1", len(list))
159+
} else if list[0].Name() != colName {
160+
t.Errorf("Invalid list[0].name, expected '%s', got '%s'", colName, list[0].Name())
161+
}
162+
if len(constraints) != 1 {
163+
t.Errorf("EdgeCollections return %d constraints, expected 1", len(constraints))
164+
} else {
165+
if strings.Join(constraints[0].From, ",") != col1Name {
166+
t.Errorf("Invalid constraints[0].From, expected ['%s'], got %q", col1Name, constraints[0].From)
167+
}
168+
if strings.Join(constraints[0].To, ",") != col2Name {
169+
t.Errorf("Invalid constraints[0].From, expected ['%s'], got %q", col2Name, constraints[0].To)
170+
}
171+
172+
prop, err := list[0].Properties(ctx)
173+
if err != nil {
174+
t.Errorf("VertexCollections Properties failed: %s", describe(err))
175+
}
176+
if !prop.IsSatellite() {
177+
t.Errorf("Collection %s is not satellite", colName)
178+
}
179+
}
180+
}
181+
182+
// revert
183+
g.Remove(ctx)
184+
}
185+
112186
// TestRemoveEdgeCollection creates a graph and then adds an edge collection in it and then removes the edge collection.
113187
func TestRemoveEdgeCollection(t *testing.T) {
114188
c := createClientFromEnv(t, true)

test/graph_creation_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,3 +315,56 @@ func TestGraphCreation(t *testing.T) {
315315
require.True(t, graphs[0].IsDisjoint())
316316
})
317317
}
318+
319+
func TestHybridSmartGraphCreation(t *testing.T) {
320+
ctx := context.Background()
321+
322+
c := createClientFromEnv(t, true)
323+
EnsureVersion(t, ctx, c).CheckVersion(MinimumVersion("3.9.0")).Cluster().Enterprise()
324+
325+
db := ensureDatabase(ctx, c, databaseName("graph", "create", "hybrid"), nil, t)
326+
327+
name := db.Name() + "_test_create_hybrid_graph"
328+
colName := db.Name() + "_create_hybrid_edge_col"
329+
col1Name := db.Name() + "_sat_edge_col"
330+
col2Name := db.Name() + "_non_sat_edge_col"
331+
332+
options := driver.CreateGraphOptions{
333+
IsSmart: true,
334+
SmartGraphAttribute: "test",
335+
ReplicationFactor: 2,
336+
NumberOfShards: 2,
337+
Satellites: []string{colName, col1Name},
338+
EdgeDefinitions: []driver.EdgeDefinition{{
339+
Collection: colName,
340+
From: []string{col1Name},
341+
To: []string{col2Name},
342+
}},
343+
}
344+
g, err := db.CreateGraph(ctx, name, &options)
345+
if err != nil {
346+
t.Fatalf("Failed to create graph '%s': %s", name, describe(err))
347+
}
348+
349+
graphs, err := db.Graphs(ctx)
350+
require.NoError(t, err)
351+
require.Len(t, graphs, 1)
352+
353+
require.Equal(t, g.Name(), graphs[0].Name())
354+
require.True(t, graphs[0].IsSmart())
355+
356+
for _, collName := range []string{colName, col1Name, col2Name} {
357+
collection, err := db.Collection(ctx, collName)
358+
require.NoError(t, err)
359+
360+
prop, err := collection.Properties(ctx)
361+
require.NoError(t, err)
362+
363+
if collName == col2Name {
364+
require.Equalf(t, 2, prop.ReplicationFactor, "ReplicationFactor mismatch for %s", collName)
365+
require.Equalf(t, 2, prop.NumberOfShards, "NumberOfShards mismatch for %s", collName)
366+
} else {
367+
require.True(t, prop.IsSatellite())
368+
}
369+
}
370+
}

test/vertex_collection_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,63 @@ func TestCreateVertexCollection(t *testing.T) {
9292
}
9393
}
9494

95+
// TestCreateSatelliteVertexCollection creates a graph and then adds a Satellite vertex collection in it
96+
func TestCreateSatelliteVertexCollection(t *testing.T) {
97+
ctx := context.Background()
98+
99+
c := createClientFromEnv(t, true)
100+
EnsureVersion(t, ctx, c).CheckVersion(MinimumVersion("3.9.0")).Cluster().Enterprise()
101+
102+
db := ensureDatabase(ctx, c, "vertex_collection_test", nil, t)
103+
104+
name := "test_create_satellite_vertex_collection"
105+
options := driver.CreateGraphOptions{
106+
IsSmart: true,
107+
SmartGraphAttribute: "key",
108+
}
109+
g, err := db.CreateGraph(ctx, name, &options)
110+
if err != nil {
111+
t.Fatalf("Failed to create graph '%s': %s", name, describe(err))
112+
}
113+
114+
// List vertex collections, must be empty
115+
if list, err := g.VertexCollections(ctx); err != nil {
116+
t.Errorf("VertexCollections failed: %s", describe(err))
117+
} else if len(list) > 0 {
118+
t.Errorf("VertexCollections return %d vertex collections, expected 0", len(list))
119+
}
120+
121+
satelliteName := "vertex-sat-test"
122+
opt := driver.CreateVertexCollectionOptions{Satellites: []string{satelliteName}}
123+
124+
// Now create a vertex collection
125+
if vc, err := g.CreateVertexCollectionWithOptions(ctx, satelliteName, opt); err != nil {
126+
t.Errorf("CreateVertexCollection failed: %s", describe(err))
127+
} else if vc.Name() != satelliteName {
128+
t.Errorf("Invalid name, expected 'vertex-sat-test', got '%s'", vc.Name())
129+
}
130+
131+
// List vertex collections, must be contain 'person'
132+
if list, err := g.VertexCollections(ctx); err != nil {
133+
t.Errorf("VertexCollections failed: %s", describe(err))
134+
} else if len(list) != 1 {
135+
t.Errorf("VertexCollections return %d vertex collections, expected 1", len(list))
136+
} else if list[0].Name() != satelliteName {
137+
t.Errorf("Invalid list[0].name, expected 'vertex-sat-test', got '%s'", list[0].Name())
138+
} else {
139+
prop, err := list[0].Properties(ctx)
140+
if err != nil {
141+
t.Errorf("VertexCollections Properties failed: %s", describe(err))
142+
}
143+
if !prop.IsSatellite() {
144+
t.Errorf("Collection %s is not satellite", satelliteName)
145+
}
146+
}
147+
148+
// revert
149+
g.Remove(ctx)
150+
}
151+
95152
// TestRemoveVertexCollection creates a graph and then adds an vertex collection in it and then removes the vertex collection.
96153
func TestRemoveVertexCollection(t *testing.T) {
97154
c := createClientFromEnv(t, true)

0 commit comments

Comments
 (0)