1

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"] ].

Stanislav Kralin
  • 11,070
  • 4
  • 35
  • 58
Alex
  • 21
  • 5
  • See the [tag:blank-nodes] tag info. – Stanislav Kralin Oct 16 '20 at 13:17
  • why not combining both queries? Put the `SELECT` stuff inside the `WHERE` part of a query like `DELETE {?om rr:language 'dhhdhd'} WHERE { ?om rr:language 'dhhdhd' }` – UninformedUser Oct 16 '20 at 14:07
  • Unfortunately I can't combine the queries as there could be other object maps with the same language tag....Thanks for the suggestion though. – Alex Oct 16 '20 at 14:26
  • but how do you know which one you delete with your code? Is it just one randomly? If so, you could do `DELETE {?om rr:language 'dhhdhd'} WHERE { SELECT ?om WHERE {?om rr:language 'dhhdhd' } LIMIT 1}` to pick just one – UninformedUser Oct 16 '20 at 14:42
  • The blank node value and a new value for the language tag will be passed into the function. This example is to illustrate what I hope to achieve. – Alex Oct 16 '20 at 14:53
  • Blank nodes cannot be directly referenced in separate queries, because they really don't have IRIs -- because IRIs are what make nodes "non-blank". You need to use *one* SPARQL query to both discover and update the description of a blank node, because the internal, *temporary* identifier of a blank node remains constant throughout execution of a single query -- but is not re-used for a subsequent query. (This is one of the reasons blank nodes are not good tools for *every* job, though they are for *some* jobs.) – TallTed Oct 16 '20 at 19:24
  • 1
    I have figured out the solution and have updated the question accordingly. Its a hack really, however, it works. The blank nodes in RDFlib are given unique values when loaded into the Graph() class. These stay persistent when querying and can be used to reference values. – Alex Oct 17 '20 at 10:50

2 Answers2

0

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").
                }
            }
Alex
  • 21
  • 5
  • oh, now I understand. Then you should use blank node notation in SPARQL instead of the filter: `_:ub1bL24C24 rr:language "dhhdhd" .` – UninformedUser Oct 17 '20 at 13:05
  • `STR` is not defined on blank nodes BTW :). https://stackoverflow.com/a/55445337/7879193 – Stanislav Kralin Oct 17 '20 at 17:23
  • 1
    This is indeed a hack, and is not likely (never mind guaranteed) to work with any SPARQL processor other than RDFlib. Also, your `FILTER` clause indicates that you already know the relevant blank node's internal identifier — which renders it (temporarily) non-blank anyway — so you can use a much simpler query, `PREFIX rr: DELETE { _:ub1bL24C24 rr:language "dhhdhd" } INSERT { _:ub1bL24C24 rr:language "en-fhfhfh" }` – TallTed Oct 17 '20 at 21:46
  • It worked for the RDFlib processor which is what I'm using. @TallTed that was the original query I tried, however, it didn't work. I have contacted the Github collaborators in hopes of finding out why..... – Alex Oct 20 '20 at 21:36
  • 1
    I noted that this hack will probably not work with any other SPARQL processor because otherwise this solution may be found by others, and considered to be a SPARQL solution -- which it is not; it is an RDFlib solution. I suggest adding a link here to the github issue/thread, so that others can follow along in future. – TallTed Oct 20 '20 at 22:51
0

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")
}
Nicholas Car
  • 1,164
  • 4
  • 7