I am struggling creating a correct SPARQL query which will produce the right format so that I can open in it Protege. Our ontology is about cocktails, we want to have all DBPedia cocktails in our database, including the ingredients (dbp:ingredients
) and recipe (dbp:prep
). Getting the cocktail in the database works fine, but the ingredients and recipe not. I have now the following query:
CONSTRUCT {?drink dct:subject ?category.
?drink dbp:prep ?recipe.
?drink dbp:ingredients ?ingredients.
?drink rdf:type owl:NamedIndividual .
?category rdf:type owl:Class.
dct:subject rdf:type owl:ObjectProperty.
dbp:prep rdf:type owl:ObjectProperty.
dbp:ingredient rdf:type owl:Objectproperty.
}
WHERE {
?drink dct:subject ?category.
?drink dbp:prep ?recipe.
?drink dbp:ingredients ?ingredients.}
Since ?ingredients and ?recipe are now not declared, it does not show in the individuals tab in Protege. But when I add this to the CONSTRUCT part of the query:
?recipe rdf:type owl:NamedIndividual.
?ingredients rdf:type owl:NamedIndividual.
I get an error:
Virtuoso RDF01 Error Bad variable value in CONSTRUCT: "*5 cL vodka *10 cL orange juice" (tag 246 box flags 0) is not a valid subject, only object of a triple can be a literal
I think because the prep and ingredients on dbpedia are just a string, no linked data. However, how do I make this work so that it does show in Protege?
It's not possible to have a literal as subject of an RDF triple. Instead, creating a resource for recipe and ingredients + attaching the string values as rdfs:comment
(or maybe rdfs:label
) could be a workaround. It works like this:
CONSTRUCT {
?drink dct:subject ?category.
?drink dbp:prep ?recipe.
?drink dbp:ingredients ?ingredients.
?drink rdf:type owl:NamedIndividual .
?category rdf:type owl:Class.
dct:subject rdf:type owl:ObjectProperty.
dbp:prep rdf:type owl:ObjectProperty.
dbp:ingredients rdf:type owl:Objectproperty.
# add string values as rdfs:comment
?recipe rdfs:comment ?recipe_str .
?ingredients rdfs:comment ?ingredients_str
}
WHERE {
?drink dct:subject ?category.
?drink dbp:prep ?recipe_str.
?drink dbp:ingredients ?ingredients_str.
BIND(URI(CONCAT("http://dbpedia.org/resource/recipe", MD5(STR(?recipe_str)))) as ?recipe)
BIND(URI(CONCAT("http://dbpedia.org/resource/ingredients", MD5(STR(?ingredients_str)))) as ?ingredients)
}
Note, it would somehow fail if the recipe (resp. ingredients) is already a resource. It doesn't hold for dbp:prep
and dbp:ingredients
on DBpedia, but in general, if you're not sure and you have some rdf:Property
which in fact allows for both resources and literals, you need to handle this properly, e.g. by using the IF-ELSE
construct:
BIND(IF(isLiteral(?recipe_str), URI(CONCAT("http://dbpedia.org/resource/recipe", MD5(STR(?recipe_str)))), ?recipe_str) as ?recipe)
BIND(IF(isLiteral(?ingredients_str), URI(CONCAT("http://dbpedia.org/resource/ingredients", MD5(STR(?ingredients_str)))), ?ingredients_str) as ?ingredients)
and you also would have to omit the rdfs:comment
triples then indeed...