From 26ade9a3291d89090815dc358b73936c16231ae1 Mon Sep 17 00:00:00 2001 From: jwierzbo Date: Tue, 18 Jan 2022 15:08:28 +0100 Subject: [PATCH 1/2] Drivers 3.9: Hybrid SmartGraphs - optional satellite parameter --- database_graphs.go | 2 +- database_graphs_impl.go | 26 +++++++++----------------- graph_edge_collections.go | 2 +- graph_vertex_collections.go | 2 +- 4 files changed, 12 insertions(+), 20 deletions(-) diff --git a/database_graphs.go b/database_graphs.go index 9b82cbca..2fa2fd11 100644 --- a/database_graphs.go +++ b/database_graphs.go @@ -71,7 +71,7 @@ type CreateGraphOptions struct { // IsDisjoint set isDisjoint flag for Graph. Required ArangoDB 3.7+ IsDisjoint bool // Satellites contains an array of collection names that will be used to create SatelliteCollections for a Hybrid (Disjoint) SmartGraph (Enterprise Edition only) - Satellites []string `json:"satellites"` + Satellites []string `json:"satellites,omitempty"` } // EdgeDefinition contains all information needed to define a single edge in a graph. diff --git a/database_graphs_impl.go b/database_graphs_impl.go index 7e4e7152..72f52c17 100644 --- a/database_graphs_impl.go +++ b/database_graphs_impl.go @@ -167,11 +167,12 @@ type createGraphAdditionalOptions struct { // IsDisjoint set isDisjoint flag for Graph. Required ArangoDB 3.7+ IsDisjoint bool `json:"isDisjoint,omitempty"` // Satellites contains an array of collection names that will be used to create SatelliteCollections for a Hybrid (Disjoint) SmartGraph (Enterprise Edition only) - Satellites []string `json:"satellites"` + Satellites []string `json:"satellites,omitempty"` } // CreateGraph creates a new graph with given name and options, and opens a connection to it. // If a graph with given name already exists within the database, a DuplicateError is returned. +// todo test func (d *database) CreateGraph(ctx context.Context, name string, options *CreateGraphOptions) (Graph, error) { input := createGraphOptions{ Name: name, @@ -180,22 +181,13 @@ 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.ReplicationFactor == SatelliteGraph { - input.Options = &createGraphAdditionalOptions{ - SmartGraphAttribute: options.SmartGraphAttribute, - ReplicationFactor: graphReplicationFactor(options.ReplicationFactor), - IsDisjoint: options.IsDisjoint, - Satellites: options.Satellites, - } - } else if options.SmartGraphAttribute != "" || options.NumberOfShards != 0 { - input.Options = &createGraphAdditionalOptions{ - SmartGraphAttribute: options.SmartGraphAttribute, - NumberOfShards: options.NumberOfShards, - ReplicationFactor: graphReplicationFactor(options.ReplicationFactor), - WriteConcern: options.WriteConcern, - IsDisjoint: options.IsDisjoint, - Satellites: options.Satellites, - } + input.Options = &createGraphAdditionalOptions{ + SmartGraphAttribute: options.SmartGraphAttribute, + NumberOfShards: options.NumberOfShards, + ReplicationFactor: graphReplicationFactor(options.ReplicationFactor), + WriteConcern: options.WriteConcern, + IsDisjoint: options.IsDisjoint, + Satellites: options.Satellites, } } req, err := d.conn.NewRequest("POST", path.Join(d.relPath(), "_api/gharial")) diff --git a/graph_edge_collections.go b/graph_edge_collections.go index afa44430..928fd135 100644 --- a/graph_edge_collections.go +++ b/graph_edge_collections.go @@ -62,5 +62,5 @@ type VertexConstraints struct { // CreateEdgeCollectionOptions contains optional parameters for creating a new edge collection type CreateEdgeCollectionOptions struct { // Satellites contains an array of collection names that will be used to create SatelliteCollections for a Hybrid (Disjoint) SmartGraph (Enterprise Edition only) - Satellites []string `json:"satellites"` + Satellites []string `json:"satellites,omitempty"` } diff --git a/graph_vertex_collections.go b/graph_vertex_collections.go index 2bf52f4c..94a98720 100644 --- a/graph_vertex_collections.go +++ b/graph_vertex_collections.go @@ -49,5 +49,5 @@ type GraphVertexCollections interface { // CreateVertexCollectionOptions contains optional parameters for creating a new vertex collection type CreateVertexCollectionOptions struct { // Satellites contains an array of collection names that will be used to create SatelliteCollections for a Hybrid (Disjoint) SmartGraph (Enterprise Edition only) - Satellites []string `json:"satellites"` + Satellites []string `json:"satellites,omitempty"` } From 009cc07a7dbf592f93905290cf5e3fb7c8fd8909 Mon Sep 17 00:00:00 2001 From: jwierzbo Date: Tue, 18 Jan 2022 15:59:06 +0100 Subject: [PATCH 2/2] Tests --- test/edge_collection_test.go | 74 ++++++++++++++++++++++++++++++++++ test/graph_creation_test.go | 53 ++++++++++++++++++++++++ test/vertex_collection_test.go | 57 ++++++++++++++++++++++++++ 3 files changed, 184 insertions(+) diff --git a/test/edge_collection_test.go b/test/edge_collection_test.go index 96056975..cb71c511 100644 --- a/test/edge_collection_test.go +++ b/test/edge_collection_test.go @@ -109,6 +109,80 @@ func TestCreateEdgeCollection(t *testing.T) { } } +// TestCreateSatelliteEdgeCollection creates a graph and then adds an Satellite edge collection in it +func TestCreateSatelliteEdgeCollection(t *testing.T) { + ctx := context.Background() + + c := createClientFromEnv(t, true) + EnsureVersion(t, ctx, c).CheckVersion(MinimumVersion("3.9.0")).Cluster().Enterprise() + + db := ensureDatabase(nil, c, "edge_collection_test", nil, t) + + name := "test_create_sat_edge_collection" + options := driver.CreateGraphOptions{ + IsSmart: true, + SmartGraphAttribute: "test", + } + g, err := db.CreateGraph(ctx, name, &options) + if err != nil { + t.Fatalf("Failed to create graph '%s': %s", name, describe(err)) + } + + // List edge collections, must be empty + if list, _, err := g.EdgeCollections(nil); err != nil { + t.Errorf("EdgeCollections failed: %s", describe(err)) + } else if len(list) > 0 { + t.Errorf("EdgeCollections return %d edge collections, expected 0", len(list)) + } + + // Now create an edge collection + colName := "create_sat_edge_collection" + col1Name := "sat_edge_collection1" + col2Name := "sat_edge_collection2" + + opt := driver.CreateEdgeCollectionOptions{Satellites: []string{col1Name}} + if ec, err := g.CreateEdgeCollectionWithOptions(nil, colName, driver.VertexConstraints{From: []string{col1Name}, To: []string{col2Name}}, opt); err != nil { + t.Errorf("CreateEdgeCollection failed: %s", describe(err)) + } else if ec.Name() != colName { + t.Errorf("Invalid name, expected '%s', got '%s'", colName, ec.Name()) + } + + assertCollection(nil, db, colName, t) + assertCollection(nil, db, col1Name, t) + assertCollection(nil, db, col2Name, t) + + if list, constraints, err := g.EdgeCollections(nil); err != nil { + t.Errorf("EdgeCollections failed: %s", describe(err)) + } else { + if len(list) != 1 { + t.Errorf("EdgeCollections return %d edge collections, expected 1", len(list)) + } else if list[0].Name() != colName { + t.Errorf("Invalid list[0].name, expected '%s', got '%s'", colName, list[0].Name()) + } + if len(constraints) != 1 { + t.Errorf("EdgeCollections return %d constraints, expected 1", len(constraints)) + } else { + if strings.Join(constraints[0].From, ",") != col1Name { + t.Errorf("Invalid constraints[0].From, expected ['%s'], got %q", col1Name, constraints[0].From) + } + if strings.Join(constraints[0].To, ",") != col2Name { + t.Errorf("Invalid constraints[0].From, expected ['%s'], got %q", col2Name, constraints[0].To) + } + + prop, err := list[0].Properties(ctx) + if err != nil { + t.Errorf("VertexCollections Properties failed: %s", describe(err)) + } + if !prop.IsSatellite() { + t.Errorf("Collection %s is not satellite", colName) + } + } + } + + // revert + g.Remove(ctx) +} + // TestRemoveEdgeCollection creates a graph and then adds an edge collection in it and then removes the edge collection. func TestRemoveEdgeCollection(t *testing.T) { c := createClientFromEnv(t, true) diff --git a/test/graph_creation_test.go b/test/graph_creation_test.go index d3e2c1c8..64f2f0c0 100644 --- a/test/graph_creation_test.go +++ b/test/graph_creation_test.go @@ -315,3 +315,56 @@ func TestGraphCreation(t *testing.T) { require.True(t, graphs[0].IsDisjoint()) }) } + +func TestHybridSmartGraphCreation(t *testing.T) { + ctx := context.Background() + + c := createClientFromEnv(t, true) + EnsureVersion(t, ctx, c).CheckVersion(MinimumVersion("3.9.0")).Cluster().Enterprise() + + db := ensureDatabase(ctx, c, databaseName("graph", "create", "hybrid"), nil, t) + + name := db.Name() + "_test_create_hybrid_graph" + colName := db.Name() + "_create_hybrid_edge_col" + col1Name := db.Name() + "_sat_edge_col" + col2Name := db.Name() + "_non_sat_edge_col" + + options := driver.CreateGraphOptions{ + IsSmart: true, + SmartGraphAttribute: "test", + ReplicationFactor: 2, + NumberOfShards: 2, + Satellites: []string{colName, col1Name}, + EdgeDefinitions: []driver.EdgeDefinition{{ + Collection: colName, + From: []string{col1Name}, + To: []string{col2Name}, + }}, + } + g, err := db.CreateGraph(ctx, name, &options) + if err != nil { + t.Fatalf("Failed to create graph '%s': %s", name, describe(err)) + } + + 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].IsSmart()) + + for _, collName := range []string{colName, col1Name, col2Name} { + collection, err := db.Collection(ctx, collName) + require.NoError(t, err) + + prop, err := collection.Properties(ctx) + require.NoError(t, err) + + if collName == col2Name { + require.Equalf(t, 2, prop.ReplicationFactor, "ReplicationFactor mismatch for %s", collName) + require.Equalf(t, 2, prop.NumberOfShards, "NumberOfShards mismatch for %s", collName) + } else { + require.True(t, prop.IsSatellite()) + } + } +} diff --git a/test/vertex_collection_test.go b/test/vertex_collection_test.go index 2287cd43..037616b4 100644 --- a/test/vertex_collection_test.go +++ b/test/vertex_collection_test.go @@ -92,6 +92,63 @@ func TestCreateVertexCollection(t *testing.T) { } } +// TestCreateSatelliteVertexCollection creates a graph and then adds a Satellite vertex collection in it +func TestCreateSatelliteVertexCollection(t *testing.T) { + ctx := context.Background() + + c := createClientFromEnv(t, true) + EnsureVersion(t, ctx, c).CheckVersion(MinimumVersion("3.9.0")).Cluster().Enterprise() + + db := ensureDatabase(ctx, c, "vertex_collection_test", nil, t) + + name := "test_create_satellite_vertex_collection" + options := driver.CreateGraphOptions{ + IsSmart: true, + SmartGraphAttribute: "key", + } + g, err := db.CreateGraph(ctx, name, &options) + if err != nil { + t.Fatalf("Failed to create graph '%s': %s", name, describe(err)) + } + + // List vertex collections, must be empty + if list, err := g.VertexCollections(ctx); err != nil { + t.Errorf("VertexCollections failed: %s", describe(err)) + } else if len(list) > 0 { + t.Errorf("VertexCollections return %d vertex collections, expected 0", len(list)) + } + + satelliteName := "vertex-sat-test" + opt := driver.CreateVertexCollectionOptions{Satellites: []string{satelliteName}} + + // Now create a vertex collection + if vc, err := g.CreateVertexCollectionWithOptions(ctx, satelliteName, opt); err != nil { + t.Errorf("CreateVertexCollection failed: %s", describe(err)) + } else if vc.Name() != satelliteName { + t.Errorf("Invalid name, expected 'vertex-sat-test', got '%s'", vc.Name()) + } + + // List vertex collections, must be contain 'person' + if list, err := g.VertexCollections(ctx); err != nil { + t.Errorf("VertexCollections failed: %s", describe(err)) + } else if len(list) != 1 { + t.Errorf("VertexCollections return %d vertex collections, expected 1", len(list)) + } else if list[0].Name() != satelliteName { + t.Errorf("Invalid list[0].name, expected 'vertex-sat-test', got '%s'", list[0].Name()) + } else { + prop, err := list[0].Properties(ctx) + if err != nil { + t.Errorf("VertexCollections Properties failed: %s", describe(err)) + } + if !prop.IsSatellite() { + t.Errorf("Collection %s is not satellite", satelliteName) + } + } + + // revert + g.Remove(ctx) +} + // TestRemoveVertexCollection creates a graph and then adds an vertex collection in it and then removes the vertex collection. func TestRemoveVertexCollection(t *testing.T) { c := createClientFromEnv(t, true)