public void GraphMatchTrivial1() { Graph g = new Graph(); g.LoadFromFile("turtle11-unofficial/test-13.ttl"); Graph h = new Graph(); h.LoadFromFile("turtle11-unofficial/test-13.out", new NTriplesParser()); GraphDiffReport report = g.Difference(h); if (!report.AreEqual) { TestTools.ShowDifferences(report); } Assert.IsTrue(report.AreEqual); }
public void GraphDiffRemovedGroundTriples() { Graph g = new Graph(); Graph h = new Graph(); g.LoadFromFile("resources\\InferenceTest.ttl"); h.LoadFromFile("resources\\InferenceTest.ttl"); //Remove Triples about Ford Fiestas from 2nd Graph h.Retract(h.GetTriplesWithSubject(new Uri("http://example.org/vehicles/FordFiesta")).ToList()); GraphDiffReport report = g.Difference(h); TestTools.ShowDifferences(report); Assert.False(report.AreEqual, "Graphs should not have been reported as equal"); Assert.True(report.RemovedTriples.Any(), "Difference should have reported some Removed Triples"); }
private void MergeMapping(GraphDiffReport report, Dictionary <INode, INode> mapping) { //This first run through ensures the mappings don't conflict in which case it is an invalid mapping foreach (KeyValuePair <INode, INode> kvp in mapping) { if (report.Mapping.ContainsKey(kvp.Key)) { if (!report.Mapping[kvp.Key].Equals(kvp.Value)) { throw new RdfException("Error in GraphDiff - " + kvp.Key.ToString() + " is already mapped to " + report.Mapping[kvp.Key].ToString() + " so cannot be remapped to " + kvp.Value.ToString()); } } } //The second run through does the actual merge foreach (KeyValuePair <INode, INode> kvp in mapping) { report.Mapping.Add(kvp.Key, kvp.Value); } }
/// <summary> /// Converts a <see cref="GraphDiffReport">diff</see> to an equivalent <see cref="ModifyCommand">SPARQL Update INSERT/DELETE command</see>. /// </summary> /// <param name="diff">The <see cref="GraphDiffReport">diff</see> to convert.</param> /// <param name="graphUri">Optional <see cref="Uri">URI</see> of the affected graph.</param> /// <param name="prefixes">Optional <see cref="INamespaceMapper">mapper</see> used to resolve prefixes.</param> /// <returns>A <see cref="ModifyCommand">SPARQL Update INSERT/DELETE command</see> that represents the <see cref="GraphDiffReport">diff</see>.</returns> public static ModifyCommand AsUpdate(this GraphDiffReport diff, Uri graphUri = null, INamespaceMapper prefixes = null) { var delete = new GraphPatternBuilder(); var insert = new GraphPatternBuilder(); var where = new GraphPatternBuilder(); // Removed ground triples are added as is to both delete and where clauses foreach (var t in diff.RemovedTriples) { delete.AddTriplePattern(t); where.AddTriplePattern(t); } foreach (var g in diff.RemovedMSGs) { // Blank nodes in removed non-ground triples are converted to variables and added to both delete and where clauses foreach (var t in g.Triples) { delete.AddVariablePattern(t); where.AddVariablePattern(t); } // An ISBLANK filter is added for each blank node in removed non-ground triples foreach (var n in g.BlankNodes()) { where.AddBlankNodeFilter(n); } } // Added triples (ground or not) are added as is to the insert clause foreach (var t in diff.AllAddedTriples()) { insert.AddTriplePattern(t); } return(new ModifyCommand( delete.BuildGraphPattern(prefixes), insert.BuildGraphPattern(prefixes), where.BuildGraphPattern(prefixes), graphUri)); }
/// <summary> /// Converts a <see cref="GraphDiffReport">diff</see> to an equivalent <see cref="ModifyCommand">SPARQL Update query</see> /// </summary> /// <param name="diff">The <see cref="GraphDiffReport">diff</see> to convert</param> /// <returns>A <see cref="ModifyCommand">SPARQL Update query</see> that represents the <see cref="GraphDiffReport">diff</see></returns> internal static ModifyCommand AsUpdate(this GraphDiffReport diff) { var delete = new GraphPatternBuilder(); var insert = new GraphPatternBuilder(); var where = new GraphPatternBuilder(); // Groud removed triples are added as is to both delete and where clauses foreach (var t in diff.RemovedTriples) { delete.AddTriplePattern(t); where.AddTriplePattern(t); } foreach (var g in diff.RemovedMSGs) { // Blank nodes in non-ground removed triples are converted to variables and added to both delete and where clauses foreach (var t in g.Triples) { delete.AddVariablePattern(t); where.AddVariablePattern(t); } // An ISBLANK filter is added for each blank node in non-ground removed triples foreach (var n in g.BlankNodes()) { where.AddBlankNodeFilter(n); } } // Added triples (ground or not) are added as is to the insert clause foreach (var t in diff.AllAddedTriples()) { insert.AddTriplePattern(t); } return(new ModifyCommand( delete.BuildGraphPattern(), insert.BuildGraphPattern(), where.BuildGraphPattern())); }
public void GraphDiffRemovedMSG() { Graph g = new Graph(); Graph h = new Graph(); g.LoadFromFile("resources\\InferenceTest.ttl"); h.LoadFromFile("resources\\InferenceTest.ttl"); //Remove MSG from 2nd Graph INode toRemove = h.Nodes.BlankNodes().FirstOrDefault(); Skip.If(toRemove == null, "No MSGs in test graph"); h.Retract(h.GetTriplesWithSubject(toRemove).ToList()); GraphDiffReport report = g.Difference(h); TestTools.ShowDifferences(report); Assert.False(report.AreEqual, "Graphs should not have been reported as equal"); Assert.True(report.RemovedMSGs.Any(), "Difference should have reported some Removed MSGs"); }
public void GraphDiffAddedMSG() { Graph g = new Graph(); Graph h = new Graph(); g.LoadFromFile("resources\\InferenceTest.ttl"); h.LoadFromFile("resources\\InferenceTest.ttl"); //Add additional Triple to 2nd Graph INode blank = h.CreateBlankNode(); IUriNode subClass = h.CreateUriNode("rdfs:subClassOf"); IUriNode vehicle = h.CreateUriNode("eg:Vehicle"); h.Assert(new Triple(blank, subClass, vehicle)); GraphDiffReport report = g.Difference(h); TestTools.ShowDifferences(report); Assert.False(report.AreEqual, "Graphs should not have been reported as equal"); Assert.True(report.AddedMSGs.Any(), "Difference should have reported some Added MSGs"); }
public void GraphDiffAddedGroundTriples() { Graph g = new Graph(); Graph h = new Graph(); FileLoader.Load(g, "InferenceTest.ttl"); FileLoader.Load(h, "InferenceTest.ttl"); //Add additional Triple to 2nd Graph IUriNode spaceVehicle = h.CreateUriNode("eg:SpaceVehicle"); IUriNode subClass = h.CreateUriNode("rdfs:subClassOf"); IUriNode vehicle = h.CreateUriNode("eg:Vehicle"); h.Assert(new Triple(spaceVehicle, subClass, vehicle)); GraphDiffReport report = g.Difference(h); TestTools.ShowDifferences(report); Assert.IsFalse(report.AreEqual, "Graphs should not have been reported as equal"); Assert.IsTrue(report.AddedTriples.Any(), "Difference should have reported some Added Triples"); }
public void GraphMatchTrivial2() { Graph g = new Graph(); IBlankNode a = g.CreateBlankNode("b1"); IBlankNode b = g.CreateBlankNode("b2"); IBlankNode c = g.CreateBlankNode("b3"); INode pred = g.CreateUriNode(UriFactory.Create("http://predicate")); g.Assert(a, pred, g.CreateLiteralNode("A")); g.Assert(a, pred, b); g.Assert(b, pred, g.CreateLiteralNode("B")); g.Assert(b, pred, c); g.Assert(c, pred, g.CreateLiteralNode("C")); g.Assert(c, pred, a); Graph h = new Graph(); IBlankNode a2 = h.CreateBlankNode("b4"); IBlankNode b2 = h.CreateBlankNode("b5"); IBlankNode c2 = h.CreateBlankNode("b3"); INode pred2 = h.CreateUriNode(UriFactory.Create("http://predicate")); h.Assert(a2, pred2, h.CreateLiteralNode("A")); h.Assert(a2, pred2, b2); h.Assert(b2, pred2, h.CreateLiteralNode("B")); h.Assert(b2, pred2, c2); h.Assert(c2, pred2, h.CreateLiteralNode("C")); h.Assert(c2, pred2, a2); GraphDiffReport report = g.Difference(h); if (!report.AreEqual) { TestTools.ShowDifferences(report); } Assert.IsTrue(report.AreEqual); }
public void GraphDiffRemovedMSG() { Graph g = new Graph(); Graph h = new Graph(); FileLoader.Load(g, "InferenceTest.ttl"); FileLoader.Load(h, "InferenceTest.ttl"); //Remove MSG from 2nd Graph INode toRemove = h.Nodes.BlankNodes().FirstOrDefault(); if (toRemove == null) { Assert.Inconclusive("No MSGs in test graph"); } h.Retract(h.GetTriplesWithSubject(toRemove).ToList()); GraphDiffReport report = g.Difference(h); TestTools.ShowDifferences(report); Assert.IsFalse(report.AreEqual, "Graphs should not have been reported as equal"); Assert.IsTrue(report.RemovedMSGs.Any(), "Difference should have reported some Removed MSGs"); }
private static IEnumerable <Triple> AllAddedTriples(this GraphDiffReport diff) { return(diff.AddedMSGs.SelectMany(msg => msg.Triples).Union(diff.AddedTriples)); }
/// <summary> /// Calculates the Difference between the two Graphs i.e. the changes required to get from the 1st Graph to the 2nd Graph /// </summary> /// <param name="a">First Graph</param> /// <param name="b">Second Graph</param> /// <returns></returns> public GraphDiffReport Difference(IGraph a, IGraph b) { GraphDiffReport report = new GraphDiffReport(); //Firstly check for Graph Equality Dictionary <INode, INode> equalityMapping = new Dictionary <INode, INode>(); if (a.Equals(b, out equalityMapping)) { //If Graphs are equal set AreEqual to true, assign the mapping and return report.AreEqual = true; if (equalityMapping != null) { report.Mapping = equalityMapping; } return(report); } //Next check for changes in Ground Triples //Iterate over the Ground Triples in the 1st Graph to find those that have been removed in the 2nd foreach (Triple t in a.Triples.Where(t => t.IsGroundTriple)) { if (!b.Triples.Contains(t)) { report.AddRemovedTriple(t); } } //Iterate over the Ground Triples in the 2nd Graph to find those that have been added in the 2nd foreach (Triple t in b.Triples.Where(t => t.IsGroundTriple)) { if (!a.Triples.Contains(t)) { report.AddAddedTriple(t); } } //Do we need to compute MSGs? //If all Triples are Ground Triples then this step gets skipped which saves on computation if (a.Triples.Any(t => !t.IsGroundTriple) || b.Triples.Any(t => !t.IsGroundTriple)) { //Some non-ground Triples so start computing MSGs //First build 2 HashSets of the non-ground Triples from the Graphs foreach (Triple t in a.Triples.Where(t => !t.IsGroundTriple)) { this._lhsUnassigned.Add(t); } foreach (Triple t in b.Triples.Where(t => !t.IsGroundTriple)) { this._rhsUnassigned.Add(t); } //Then compute all the MSGs this.ComputeMSGs(a, this._lhsUnassigned, this._lhsMSGs); this.ComputeMSGs(b, this._rhsUnassigned, this._rhsMSGs); //Sort MSGs by size - this is just so we start checking MSG equality from smallest MSGs first for efficiency GraphSizeComparer comparer = new GraphSizeComparer(); this._lhsMSGs.Sort(comparer); this._rhsMSGs.Sort(comparer); //Now start trying to match MSG foreach (IGraph msg in this._lhsMSGs) { //Get Candidate MSGs from RHS i.e. those of equal size List <IGraph> candidates = (from g in this._rhsMSGs where g.Triples.Count == msg.Triples.Count select g).ToList(); if (candidates.Count == 0) { //No Candidate Matches so this MSG is not present in the 2nd Graph so add to report as a Removed MSG report.AddRemovedMSG(msg); } else { //Do any of the candidates match? bool hasMatch = false; foreach (IGraph candidate in candidates) { Dictionary <INode, INode> tempMapping = new Dictionary <INode, INode>(); if (msg.Equals(candidate, out tempMapping)) { //This MSG has a Match in the 2nd Graph so add the Mapping information hasMatch = true; try { this.MergeMapping(report, tempMapping); } catch (RdfException) { //If the Mapping cannot be merged it is a bad mapping and we try other candidates hasMatch = false; continue; } //Remove the matched MSG from the RHS MSGs so we cannot match another LHS MSG to it later //We use ReferenceEquals for this remove to avoid potentially costly Graph Equality calculations this._rhsMSGs.RemoveAll(g => ReferenceEquals(g, candidate)); } } //No match was found so the MSG is removed from the 2nd Graph if (!hasMatch) { report.AddRemovedMSG(msg); } } } //If we are left with any MSGs in the RHS then these are added MSG foreach (IGraph msg in this._rhsMSGs) { report.AddAddedMSG(msg); } } return(report); }
public static void ShowDifferences(GraphDiffReport report, String lhsName, String rhsName) { NTriplesFormatter formatter = new NTriplesFormatter(); lhsName = String.IsNullOrEmpty(lhsName) ? "1st Graph" : lhsName; rhsName = String.IsNullOrEmpty(rhsName) ? "2nd Graph" : rhsName; if (report.AreEqual) { Console.WriteLine("Graphs are Equal"); Console.WriteLine(); Console.WriteLine("Blank Node Mapping between Graphs:"); foreach (KeyValuePair <INode, INode> kvp in report.Mapping) { Console.WriteLine(kvp.Key.ToString(formatter) + " => " + kvp.Value.ToString(formatter)); } } else { Console.WriteLine("Graphs are non-equal"); Console.WriteLine(); Console.WriteLine("Triples added to " + lhsName + " to give " + rhsName + ":"); foreach (Triple t in report.AddedTriples) { Console.WriteLine(t.ToString(formatter)); } Console.WriteLine(); Console.WriteLine("Triples removed from " + lhsName + " to give " + rhsName + ":"); foreach (Triple t in report.RemovedTriples) { Console.WriteLine(t.ToString(formatter)); } Console.WriteLine(); Console.WriteLine("Blank Node Mapping between Graphs:"); foreach (KeyValuePair <INode, INode> kvp in report.Mapping) { Console.WriteLine(kvp.Key.ToString(formatter) + " => " + kvp.Value.ToString(formatter)); } Console.WriteLine(); if (report.AddedMSGs.Any()) { Console.WriteLine("MSGs added to " + lhsName + " to give " + rhsName + ":"); foreach (IGraph msg in report.AddedMSGs) { Console.WriteLine(msg.Triples.Count + " Triple(s):"); foreach (Triple t in msg.Triples) { Console.WriteLine(t.ToString(formatter)); } Console.WriteLine(); } Console.WriteLine(); } if (report.RemovedMSGs.Any()) { Console.WriteLine("MSGs removed from " + lhsName + " to give " + rhsName + ":"); foreach (IGraph msg in report.RemovedMSGs) { Console.WriteLine(msg.Triples.Count + " Triple(s):"); foreach (Triple t in msg.Triples) { Console.WriteLine(t.ToString(formatter)); } Console.WriteLine(); } } } }
public static void ShowDifferences(GraphDiffReport report) { ShowDifferences(report, "1st Graph", "2nd Graph"); }