This project has moved. For the latest updates, please go here.

Returning add/remove triples from savechanges

Feb 18, 2014 at 2:30 PM
Hi,

I hit i point where i need to get the triples that are added/removed by save changes.
In short i try to implement a changeset so only the difference between the default graph and changes pending in context is to be saved in 2 graphs (adds/removals).
It's kind of similar to the solution proposed : here

So i started to implement like this:
I changed "DataObjectStoreBase" save changes
 /// <summary>
        /// Commits all changes. Waits for the operation to complete.
        /// </summary>
        public void SaveChanges(
            bool persistChanges = true,
            IList<VDS.RDF.Triple> addedTriples = null,
            IList<VDS.RDF.Triple> removedTriples = null,
            IList<VDS.RDF.Triple> versionTriples = null)
        {
            if (_savingChanges != null) _savingChanges(this, new EventArgs());
            DoSaveChanges(persistChanges,addedTriples, removedTriples, versionTriples);
        }

        protected abstract void DoSaveChanges(
            bool persistChanges = true,
            IList<VDS.RDF.Triple> addedTriples = null,
            IList<VDS.RDF.Triple> removedTriples = null,
            IList<VDS.RDF.Triple> versionTriples = null);
And then in store implementations i save something like:
.....................................................
foreach (var triple in AddTriples)
            {
                if (exportAddTriples)
                {
                    addedTriples.Add(triple.AsDnrTriple(exportGraph));
                }
            }
            foreach (var triple in DeletePatterns)
            {
                if (exportRemovedTriples)
                {
                    removedTriples.Add(triple.AsDnrTriple(exportGraph));
                }
            }
            foreach (var triple in Preconditions)
            {
                if (exportVersionTriples)
                {
                    versionTriples.Add(triple.AsDnrTriple(exportGraph));
                }
            }

            if (!persistChanges) return;
And inside the B* Triple class I added a method to convert to DNR.Triple (probably code should be in a b*-dnr conversion helper):
public VDS.RDF.Triple AsDnrTriple(IGraph graph)
        {
            ILiteralNode literalNode = null;
            if (IsLiteral)
            {
                if (!String.IsNullOrWhiteSpace(LangCode))
                {
                    literalNode = graph.CreateLiteralNode(Object,LangCode);
                }
                else if (!String.IsNullOrWhiteSpace(DataType))
                {
                    literalNode = graph.CreateLiteralNode(Object, new Uri(DataType));
                }
                else
                {
                    literalNode = graph.CreateLiteralNode(Object);
                }
            }
            return new VDS.RDF.Triple(
                graph.CreateUriNode(new Uri(Subject)),
                graph.CreateUriNode(new Uri(Predicate)),
                IsLiteral ? (INode)literalNode : graph.CreateUriNode(new Uri(Object))
                );
        }
I then started to write a unit test to see how it behaves. Test is in "DotNetRdfStoreTests" and is based on "TestUpdateIntoSeparateGraph" test.
One problem is that the SparqlDataObjectStore has DeletePatterns like below without me deleting anything, so I suppose I have to somehow ignore those.
{http://example.org/bob http://xmlns.com/foaf/0.1/mbox_sha1 http://www.brightstardb.com/.well-known/model/wildcard}
I'm not sure how is better to implement this, and if the approach i took is ok.
maybe you have some suggestions.
Coordinator
Feb 18, 2014 at 6:33 PM
The URI http://www.brightstardb.com/.well-known/model/wildcard is used by BrightstarDB to implement wildcard deletion in a transaction. When you set a property, the code will emit a delete pattern for all values for the mapped RDF property and an insert of the new value (if its not null). For a SPARQL-based store this would need to get converted into something like:
DELETE WHERE { http://example.org/bob http://xmlns.com/foaf/0.1/mbox_sha1 ?v }
This bit of logic is implemented in SparqlUpdateableStore.
Feb 19, 2014 at 4:32 PM
So in order to get the actual triples that are deleted i need to issue construct/select queries using same where clause as in delete.
Coordinator
Feb 20, 2014 at 7:56 AM
If that is what you need then I'm afraid so. Tracking individual triple deletes was not and is not a design consideration for this code.
Coordinator
Feb 20, 2014 at 8:59 AM
Another thought that comes to mind is that if you are in control of the full process perhaps you can track information about deletes yourself, if you know what the old value of the property was before modification, you could make the assumption that the delete would just be for that value. This is not entirely safe - in the case that the graph is holding two or more triples with the same (subject,predicate) pair that get mapped to a single-value property, you have a problem with deterministic mapping of the value; but if your scenario is constrained (e.g. updates are only done via EF), it might work for you.

One way it might work is when the data object layer loads an object you keep a copy of the triples loaded; when you then process a save changes you can match up any wildcard deletes against the original triples to get the the actual triples to be deleted.