vemonet / rdflib-endpoint

💫 Deploy SPARQL endpoints from RDFLib Graphs to serve RDF files, machine learning models, or any other logic implemented in Python
https://pypi.org/project/rdflib-endpoint
MIT License
72 stars 18 forks source link

SPARQL query containing 'coalesce' returns no result on rdflib-endpoint.SparqlEndpoint() #4

Closed byte-for-byte closed 3 months ago

byte-for-byte commented 1 year ago

Hello!

My setup:

I define a graph, g = Graph(), then I g.parse() a number of .ttl files and create the endpoint with SparqlEndpoint(graph=g). Then I use uvicorn.run(app, ...) to expose the endpoint on my local machine.

I can successfully run this simple sparql statement to query for keywords:

PREFIX dcat: <http://www.w3.org/ns/dcat#>
SELECT 
?keyword
WHERE { ?subj dcat:keyword ?keyword }

However, as soon as I add a coalesce statement, the query does not return results any more:

PREFIX dcat: <http://www.w3.org/ns/dcat#>
SELECT 
?keyword
(coalesce(?keyword, "xyz") as ?foo) 
WHERE { ?subj dcat:keyword ?keyword }

I tried something similar on wikidata which has no problems:

SELECT ?item ?itemLabel 
(coalesce(?itemLabel, 2) as ?foo)
WHERE 
{
  ?item wdt:P31 wd:Q146. # Must be of a cat
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en". } 
}

The server debug output for the keywords query looks ok to me.

INFO:     127.0.0.1:43942 - "GET /sparql?format=json&query=%0APREFIX+dcat%3A+%3Chttp%3A%2F%2Fwww.w3.org%2Fns%2Fdcat%23%3E%0ASELECT+%0A%3Fkeyword%0A%28coalesce%28%3Fkeyword%2C+%22xyz%22%29+as+%3Ffoo%29+%0AWHERE+%7B+%3Fsubj+dcat%3Akeyword+%3Fkeyword+%7D%0ALIMIT+10%0A HTTP/1.1" 200 OK

Putting COALESCE in the WHERE clause does not solve the problem:

PREFIX dcat: <http://www.w3.org/ns/dcat#>
SELECT
?keyword
?foo
WHERE {
?subj dcat:keyword ?keyword
BIND(coalesce(?keyword, 2) as ?foo)
}

Am I missing something?

Thanks in advance

vemonet commented 1 year ago

Hi @byte-for-byte , thanks for the clear report

I have never used coalesce() with rdflib-endpoint so I never met this issue.

To execute the SPARQL queries we use the RDFLib SPARQL execution engine (implemented in RDFLib), so I would say the problem might come from RDFLib not implementing the coalesce() function (whereas Blazegraph, the triplestore used by Wikidata, implements it)

You might want to dig a bit in rdflib documentation, code and issues to figure out if RDFLib implements coalesce(). Maybe try using COALESCE() in full capital letters (RDFLib query engine is maybe case sensitive for functions)

Otherwise, a potential solution could be to enable users to use a different query engine than the one implemented in RDFLib. This could be possible with oxigraph, a triplestore written in rust with bindings to python and RDFLib: https://github.com/oxigraph/oxrdflib (but I am not 100% sure this will work)

I don't have much time to work on this right now, but this is a feature I would be interested to add. So if you are feeling hacky feel free to try it yourself and send a pull request if it works!

vemonet commented 1 year ago

@byte-for-byte I gave a try to COALESCE() and get the same issue with the default RDFLib query engine

I added a fix to make it work when using oxrdflib (oxigraph) as backend engine. And COALESCE() works with oxigraph! But the custom functions don't work (I think oxrdflib doesn't support the custom evaluations plugin), so that would work if you don't need custom functions

Install the required dependency:

pip install "rdflib-endpoint[oxigraph] >=0.2.5"

Create your RDFLib graph using the Oxigraph store (it needs to be capitalized, it's case sensitive):

from rdflib import Graph
from rdflib_endpoint import SparqlEndpoint

g = Graph(store="Oxigraph")

# Add your triples in the graph

app = SparqlEndpoint(
    graph=g,
    ...
)
byte-for-byte commented 1 year ago

Hi @vemonet, I very much appreciate your quick response and looking into this already. Using Oxigraph might be a solution for me, I'll check this out! (Although I am not so happy with introducing the rust dependency.)

Well, thanks again and happy holidays! :)

vemonet commented 1 year ago

@byte-for-byte the magic with rust bindings is that you don't see any rust dependency, you just install a pip package (oxrdflib in this case) and call python objects/functions like you would do usually

You never see the rust "executable" that is actually used in the back, and it is pre-compiled in a way that it is just an executable without need for any dependency (really cool way to build super efficient software, yet user-friendly that can be called from python)

And the Oxigraph team made a great job to make oxigraph compatible as a "RDFLib store" with oxrdflib so you can use it like you use RDFLib usually