RDFlib Blank node update query

649 views Asked by At

I am trying to update the object of a triple with a blank node as its subject using RDFlib. I firstly select the blank node in the first function and insert this blank node into the update query in the second function, however, this doesn't provide me with the required output. I can't use the add() method or initBindings as I need to save the SPARQL query executed for the user.

Sample data

@prefix rr: <http://www.w3.org/ns/r2rml#> .
[ rr:objectMap [ rr:column "age" ;
                 rr:language "dhhdhd"] ].

Code

mapping_graph = Graph().parse("valid_mapping.ttl",format="ttl")

# find the blank node for the update query 
def find_om_IRI():
    query = """SELECT ?om
                WHERE {
                  ?om rr:language 'dhhdhd' .
                }
           """
    qres = mapping_graph.query(query)
    for row in qres:
        return row[0]
# insert blank node as subject to update query
def change_language_tag():
    om_IRI = find_om_IRI()
    update_query = """
        PREFIX rr: <http://www.w3.org/ns/r2rml#>
        DELETE DATA{
            _:%s  rr:language 'dhhdhd' .
        }
        """ % (om_IRI)
    processUpdate(mapping_graph, update_query)
    print(update_query)
    print(mapping_graph.serialize(format="ttl").decode("utf-8"))
    return update_query


change_language_tag()

This however returns the following output. Leaving the graph unchanged.

@prefix rr: <http://www.w3.org/ns/r2rml#> .
[ rr:objectMap [ rr:column "age" ;
                 rr:language "dhhdhd"] ].

2

There are 2 answers

5
Alex On

If you filter based on the blank node value. This is the final query I came up with.

            PREFIX rr: <http://www.w3.org/ns/r2rml#> 
            DELETE { ?om rr:language "dhhdhd" } 
            INSERT { ?om rr:language "en-fhfhfh" } 
            WHERE { 
            SELECT ?om
            WHERE {
                  ?om rr:language "dhhdhd" .
                  FILTER(str(?om) = "ub1bL24C24").
                }
            }
0
Nicholas Car On

Indeed, as commenter @TallTed says "Blank nodes cannot be directly referenced in separate queries". You are trying to do something with BNs for which the are expressly not defined, that is persist their absolute identity, e.g. across separate queries. You should take the approach of relative identification (locate the BN with reference to identified, URI, nodes) or single SPARQL queries. So this question is an RDF/SPARQL question, not an RDFlib question.

You said: "I can't combine the queries as there could be other object maps with the same language tag". So if you cannot deterministically refer to a node due to its lack of distinctness, you will have to change your data, but I suspect you can - see the suggestion at the end.

Then you said "I have figured out the solution and have updated the question accordingly. Its a hack really..." Yes, don't do this! You should have a solution that's not dependent on some quirks of RDFlib! RDF and Semantic Web in general is all about universally defined and standard data and querying, so don't rely on a particular toolkit for a data question like this. Use RDFlib only as an implementation but one that should be replicable in another language. I personally model all my RDFlib triple adding/deleting/selecting code as standard SPARQL queries first so that my RDFlib code is then just a standard function equivalent.

In your own answer you said "If you filter based on the blank node value...", also don't do this either!

My suggestion is to change your underlying data to include representations of things - named nodes etc - that you can use to fix on for querying. If you cannot distinguish between things that you want to change without resorting to hacks, then you have a data modelling problem that needs solving. I do think you can distinguishes object maps though.

In your data, you must be able to fix on the particular object map for which you are changing the language. Is the object map unique per column and is the column uniquely identified by its rr:column value? If so:

SELECT ?lang
WHERE {
  ?om rr:column ?col .  ?om rr:language ?lang .
  FILTER (?col = "age")
}

This will get you the object map for the column "age" so, to change it:

DELETE {
  ?om rr:language ?lang .
}
INSERT {
  ?om rr:language "new-language" .
}
WHERE {
  ?om rr:column ?col .  ?om rr:language ?lang .

  FILTER (?col = "age")
}