/// <inheritdoc/> public async Task <ArangoStructure> GetStructureAsync(ArangoHandle db, CancellationToken cancellationToken = default) { var snapshot = new ArangoStructure(); var collectionsInfos = await _arango.Collection.ListAsync(db); var graphs = await _arango.Graph.ListAsync(db); var analyzers = await _arango.Analyzer.ListAsync(db); var viewInfos = await _arango.View.ListAsync(db); var functions = await _arango.Function.ListAsync(db); foreach (var cinfo in collectionsInfos) { var collection = await _arango.Collection.GetAsync(db, cinfo.Name); var indices = await _arango.Index.ListAsync(db, collection.Name); foreach (var idx in indices) { idx.Id = null; } var c = new ArangoCollectionIndices { Collection = collection, Indices = indices.ToList() }; snapshot.Collections.Add(c); } var analyzerToPatch = analyzers.Where(x => x.Name.Contains("::")).ToList(); foreach (var a in analyzerToPatch) { var idx = a.Name.IndexOf("::", StringComparison.Ordinal); a.Name = a.Name.Substring(idx + 2); } snapshot.Analyzers = analyzerToPatch; foreach (var g in graphs) { if (g.ExtensionData?.ContainsKey("_key") == true) { g.ExtensionData.Remove("_key"); } if (g.ExtensionData?.ContainsKey("_id") == true) { g.ExtensionData.Remove("_id"); } if (g.ExtensionData?.ContainsKey("_rev") == true) { g.ExtensionData.Remove("_rev"); } if (g.ExtensionData != null) { g.Options ??= new ArangoGraphOptions(); // Normalize graph options for standalone / cluster if (g.ExtensionData?.ContainsKey("numberOfShards") == true) { g.Options.NumberOfShards = (int)(long)g.ExtensionData["numberOfShards"]; g.ExtensionData.Remove("numberOfShards"); if (g.Options.NumberOfShards == 1) { g.Options.NumberOfShards = null; } } if (g.ExtensionData?.ContainsKey("replicationFactor") == true) { g.Options.ReplicationFactor = g.ExtensionData["replicationFactor"]; g.ExtensionData.Remove("replicationFactor"); if (g.Options.ReplicationFactor is long r1 && r1 == 1) { g.Options.ReplicationFactor = null; } if (g.Options.ReplicationFactor is int r2 && r2 == 1) { g.Options.ReplicationFactor = null; } } if (g.ExtensionData?.ContainsKey("minReplicationFactor") == true) { g.Options.WriteConcern = (int)(long)g.ExtensionData["minReplicationFactor"]; g.ExtensionData.Remove("minReplicationFactor"); if (g.Options.WriteConcern == 1) { g.Options.WriteConcern = null; } } if (g.ExtensionData?.ContainsKey("smartGraphAttribute") == true) { g.Options.SmartGraphAttribute = (string)g.ExtensionData["smartGraphAttribute"]; g.ExtensionData.Remove("smartGraphAttribute"); } } } snapshot.Graphs = graphs.ToList(); foreach (var viewinfo in viewInfos) { var v = await _arango.View.GetPropertiesAsync(db, viewinfo.Name); if (v.ExtensionData?.ContainsKey("id") == true) { v.ExtensionData.Remove("id"); } if (v.ExtensionData?.ContainsKey("globallyUniqueId") == true) { v.ExtensionData.Remove("globallyUniqueId"); } if (v.ExtensionData?.ContainsKey("error") == true) { v.ExtensionData.Remove("error"); } if (v.ExtensionData?.ContainsKey("code") == true) { v.ExtensionData.Remove("code"); } if (v.PrimarySort != null) { foreach (var sort in v.PrimarySort) { sort.Direction = (bool)sort.ExtensionData["asc"] ? ArangoSortDirection.Asc : ArangoSortDirection.Desc; sort.ExtensionData.Remove("asc"); } } snapshot.Views.Add(v); } snapshot.Functions = functions.ToList(); return(snapshot); }
/// <inheritdoc/> public async Task ApplyStructureAsync(ArangoHandle db, ArangoStructure update, ArangoMigrationOptions options = null) { options ??= new ArangoMigrationOptions(); var current = await GetStructureAsync(db); foreach (var targetCollection in update.Collections ?? new List <ArangoCollectionIndices>()) { var currentCollection = current.Collections.SingleOrDefault(x => x.Collection.Name == targetCollection.Collection.Name); if (currentCollection == null) { options.Notify?.Invoke(new ArangoMigrationNotification { Object = ArangoMigrationObject.Collection, State = ArangoMigrationState.Create, Name = targetCollection.Collection.Name }); if (!options.DryRun) { await _arango.Collection.CreateAsync(db, targetCollection.Collection); } foreach (var idx in targetCollection.Indices) { options.Notify?.Invoke(new ArangoMigrationNotification { Object = ArangoMigrationObject.Index, State = ArangoMigrationState.Create, Name = idx.Name }); if (!options.DryRun) { await _arango.Index.CreateAsync(db, targetCollection.Collection.Name, idx); } } } else { // No collection updates supported options.Notify?.Invoke(new ArangoMigrationNotification { Object = ArangoMigrationObject.Collection, State = ArangoMigrationState.Identical, Name = targetCollection.Collection.Name }); foreach (var targetIndex in targetCollection.Indices) { var currentIndex = currentCollection.Indices.SingleOrDefault(x => x.Name == targetIndex.Name); if (currentIndex == null) { options.Notify?.Invoke(new ArangoMigrationNotification { Object = ArangoMigrationObject.Index, State = ArangoMigrationState.Create, Name = targetIndex.Name }); if (!options.DryRun) { await _arango.Index.CreateAsync(db, targetCollection.Collection.Name, targetIndex); } } else { if (!targetIndex.Deduplicate.HasValue) { targetIndex.Deduplicate = true; } if (!targetIndex.Sparse.HasValue) { targetIndex.Sparse = false; } if (!targetIndex.Unique.HasValue) { targetIndex.Unique = false; } if (!Compare(currentIndex, targetIndex)) { options.Notify?.Invoke(new ArangoMigrationNotification { Object = ArangoMigrationObject.Index, State = ArangoMigrationState.Update, Name = targetIndex.Name }); if (!options.DryRun) { await _arango.Index.DropAsync(db, targetCollection.Collection.Name + "/" + targetIndex.Name); await _arango.Index.CreateAsync(db, targetCollection.Collection.Name, targetIndex); } } else { options.Notify?.Invoke(new ArangoMigrationNotification { Object = ArangoMigrationObject.Index, State = ArangoMigrationState.Identical, Name = targetIndex.Name }); } } } } } foreach (var targetGraph in update.Graphs ?? new List <ArangoGraph>()) { var currentGraph = current.Graphs.SingleOrDefault(x => x.Name == targetGraph.Name); if (currentGraph == null) { options.Notify?.Invoke(new ArangoMigrationNotification { Object = ArangoMigrationObject.Graph, State = ArangoMigrationState.Create, Name = targetGraph.Name }); if (!options.DryRun) { await _arango.Graph.CreateAsync(db, targetGraph); } } else if (!Compare(currentGraph, targetGraph)) { options.Notify?.Invoke(new ArangoMigrationNotification { Object = ArangoMigrationObject.Graph, State = ArangoMigrationState.Update, Name = targetGraph.Name }); if (!options.DryRun) { await _arango.Graph.DropAsync(db, targetGraph.Name); await _arango.Graph.CreateAsync(db, targetGraph); } } else { options.Notify?.Invoke(new ArangoMigrationNotification { Object = ArangoMigrationObject.Graph, State = ArangoMigrationState.Identical, Name = targetGraph.Name }); } } foreach (var targetAnalyzer in update.Analyzers ?? new List <ArangoAnalyzer>()) { var currentAnalyzer = current.Analyzers.SingleOrDefault(x => x.Name == targetAnalyzer.Name); if (currentAnalyzer == null) { options.Notify?.Invoke(new ArangoMigrationNotification { Object = ArangoMigrationObject.Analyzer, State = ArangoMigrationState.Create, Name = targetAnalyzer.Name }); if (!options.DryRun) { await _arango.Analyzer.CreateAsync(db, targetAnalyzer); } } else if (!Compare(currentAnalyzer, targetAnalyzer)) { options.Notify?.Invoke(new ArangoMigrationNotification { Object = ArangoMigrationObject.Analyzer, State = ArangoMigrationState.Update, Name = targetAnalyzer.Name }); if (!options.DryRun) { await _arango.Analyzer.DeleteAsync(db, currentAnalyzer.Name, true); await _arango.Analyzer.CreateAsync(db, currentAnalyzer); } } else { options.Notify?.Invoke(new ArangoMigrationNotification { Object = ArangoMigrationObject.Analyzer, State = ArangoMigrationState.Identical, Name = targetAnalyzer.Name }); } } foreach (var targetView in update.Views ?? new List <ArangoView>()) { var currentView = current.Views.SingleOrDefault(x => x.Name == targetView.Name); if (currentView == null) { options.Notify?.Invoke(new ArangoMigrationNotification { Object = ArangoMigrationObject.View, State = ArangoMigrationState.Create, Name = targetView.Name }); if (!options.DryRun) { await _arango.View.CreateAsync(db, targetView); } } else if (!Compare(currentView, targetView)) { options.Notify?.Invoke(new ArangoMigrationNotification { Object = ArangoMigrationObject.View, State = ArangoMigrationState.Update, Name = targetView.Name }); if (!options.DryRun) { await _arango.View.DropAsync(db, targetView.Name); await _arango.View.CreateAsync(db, targetView); } } else { options.Notify?.Invoke(new ArangoMigrationNotification { Object = ArangoMigrationObject.View, State = ArangoMigrationState.Identical, Name = targetView.Name }); } } foreach (var targetFunction in update.Functions ?? new List <ArangoFunctionDefinition>()) { var currentFunction = current.Functions.SingleOrDefault(x => x.Name == targetFunction.Name); if (currentFunction == null) { options.Notify?.Invoke(new ArangoMigrationNotification { Object = ArangoMigrationObject.Function, State = ArangoMigrationState.Create, Name = targetFunction.Name }); if (!options.DryRun) { await _arango.Function.CreateAsync(db, targetFunction); } } else if (!Compare(currentFunction, targetFunction)) { options.Notify?.Invoke(new ArangoMigrationNotification { Object = ArangoMigrationObject.Function, State = ArangoMigrationState.Update, Name = targetFunction.Name }); if (!options.DryRun) { await _arango.Function.RemoveAsync(db, targetFunction.Name); await _arango.Function.CreateAsync(db, targetFunction); } } else { options.Notify?.Invoke(new ArangoMigrationNotification { Object = ArangoMigrationObject.Function, State = ArangoMigrationState.Identical, Name = targetFunction.Name }); } } // drop if (options.DropExcess) { foreach (var currentView in current.Views .Where(x => update.Views.All(y => y.Name != x.Name))) { options.Notify?.Invoke(new ArangoMigrationNotification { Object = ArangoMigrationObject.View, State = ArangoMigrationState.Delete, Name = currentView.Name }); if (!options.DryRun) { await _arango.View.DropAsync(db, currentView.Name); } } foreach (var currentAnalyzer in current.Analyzers .Where(x => update.Analyzers.All(y => y.Name != x.Name))) { options.Notify?.Invoke(new ArangoMigrationNotification { Object = ArangoMigrationObject.Analyzer, State = ArangoMigrationState.Delete, Name = currentAnalyzer.Name }); if (!options.DryRun) { await _arango.Analyzer.DeleteAsync(db, currentAnalyzer.Name); } } foreach (var currentGraph in current.Graphs .Where(x => update.Graphs.All(y => y.Name != x.Name))) { options.Notify?.Invoke(new ArangoMigrationNotification { Object = ArangoMigrationObject.Graph, State = ArangoMigrationState.Delete, Name = currentGraph.Name }); if (!options.DryRun) { await _arango.Graph.DropAsync(db, currentGraph.Name); } } foreach (var currentCollection in current.Collections .Where(x => update.Collections.All(y => y.Collection.Name != x.Collection.Name))) { options.Notify?.Invoke(new ArangoMigrationNotification { Object = ArangoMigrationObject.Collection, State = ArangoMigrationState.Delete, Name = currentCollection.Collection.Name }); if (!options.DryRun) { await _arango.Collection.DropAsync(db, currentCollection.Collection.Name); } } foreach (var currentFunction in current.Functions .Where(x => update.Functions.All(y => y.Name != x.Name))) { options.Notify?.Invoke(new ArangoMigrationNotification { Object = ArangoMigrationObject.Function, State = ArangoMigrationState.Delete, Name = currentFunction.Name }); if (!options.DryRun) { await _arango.Function.RemoveAsync(db, currentFunction.Name); } } } }