Advanced Query Building with tools4RDF#
This notebook builds on the previous tutorial by introducing more powerful query-building features, including comparison operators and logical composition. These capabilities allow you to construct sophisticated SPARQL queries using intuitive Python syntax.
Setup#
Once again, we’ll use the Pizza ontology and a pre-loaded RDF knowledge graph.
from tools4rdf.network.network import OntologyNetwork
from rdflib import Graph
onto = OntologyNetwork("pizza.owl")
g = Graph()
g.parse("pizza_kg.ttl", format="ttl")
<Graph identifier=N1ad04bacb4dd4f63ad25d3be980d723e (<class 'rdflib.graph.Graph'>)>
Comparison Operators#
tools4RDF supports all standard comparison operators: <, >, <=, >=, and ==. These operators enable you to add conditional filters to your SPARQL queries in a Pythonic way.
Baseline Query#
Let’s start with a simple query to find all Food items and their prices:
q = onto.query(g, onto.terms.pizza.Food.any, onto.terms.pizza.hasPrice)
q
| Food | hasPricevalue | |
|---|---|---|
| 0 | American:245aa2f1 | 10.0 |
| 1 | AmericanHot:81efd537 | 11.5 |
| 2 | Pizza:bec7e5f2 | 11.5 |
| 3 | IceCream:b992bbd1 | 7.0 |
| 4 | Margherita:3cfeae45 | 8.99 |
| 5 | Margherita:e424656a | 9.5 |
| 6 | Mushroom:b6b736c9 | 11.0 |
| 7 | Pizza:71307a5a | 11.0 |
| 8 | Pizza:f9f81adc | 12.99 |
Less Than Operator#
Now let’s filter the results to show only Food items with a price less than 10:
q = onto.query(g, onto.terms.pizza.Food.any, onto.terms.pizza.hasPrice < 10)
q
| Food | hasPricevalue | |
|---|---|---|
| 0 | IceCream:b992bbd1 | 7.0 |
| 1 | Margherita:3cfeae45 | 8.99 |
| 2 | Margherita:e424656a | 9.5 |
Equality Operator#
The other comparison operators work in the same way. For example, let’s find Food items with a price equal to 10:
q = onto.query(g, onto.terms.pizza.Food.any, onto.terms.pizza.hasPrice == 10)
q
| Food | hasPricevalue | |
|---|---|---|
| 0 | American:245aa2f1 | 10.0 |
Logical Operators#
tools4RDF supports logical operators & (AND) and | (OR) to combine multiple conditions. This allows you to build complex filters by composing simple comparison operations.
AND Operator#
For example, let’s find Food items with a price between 10 and 11 (inclusive):
q = onto.query(g, onto.terms.pizza.Food.any,
((onto.terms.pizza.hasPrice >= 10) & (onto.terms.pizza.hasPrice <= 11)))
q
| Food | hasPricevalue | |
|---|---|---|
| 0 | American:245aa2f1 | 10.0 |
| 1 | Mushroom:b6b736c9 | 11.0 |
| 2 | Pizza:71307a5a | 11.0 |
OR Operator#
The OR operator (|) allows you to match items that satisfy at least one of the specified conditions. Let’s find Food items with a price less than 9 OR greater than 11:
q = onto.query(g, onto.terms.pizza.Food.any,
((onto.terms.pizza.hasPrice < 9) | (onto.terms.pizza.hasPrice > 11)))
q
| Food | hasPricevalue | |
|---|---|---|
| 0 | AmericanHot:81efd537 | 11.5 |
| 1 | Pizza:bec7e5f2 | 11.5 |
| 2 | IceCream:b992bbd1 | 7.0 |
| 3 | Margherita:3cfeae45 | 8.99 |
| 4 | Pizza:f9f81adc | 12.99 |
Combining Multiple Properties and Conditions#
One of the most powerful features of tools4RDF is the ability to combine queries across multiple properties with different conditions. This enables you to express complex queries that would otherwise require intricate SPARQL syntax.
Querying Multiple Properties#
You can pass multiple destination terms to the query function. For example, let’s find Pizza items and both their prices and calorific content values:
q = onto.query(g, onto.terms.pizza.Pizza,
[onto.terms.pizza.hasPrice,
onto.terms.pizza.hasCalorificContentValue,]
)
q
| Pizza | hasPricevalue | hasCalorificContentValuevalue | |
|---|---|---|---|
| 0 | American:245aa2f1 | 10.0 | 1292.0 |
| 1 | AmericanHot:81efd537 | 11.5 | 1321.0 |
| 2 | Margherita:3cfeae45 | 8.99 | 848.0 |
| 3 | Margherita:e424656a | 9.5 | 898.0 |
| 4 | Mushroom:b6b736c9 | 11.0 | 820.0 |
| 5 | Pizza:71307a5a | 11.0 | 838.0 |
| 6 | Pizza:bec7e5f2 | 11.5 | 960.0 |
| 7 | Pizza:f9f81adc | 12.99 | 824.0 |
Combining Conditions on Multiple Properties#
Now let’s apply different conditions to multiple properties. For example, find Pizza items with a price less than or equal to 10 AND calorific content greater than 1000:
q = onto.query(g, onto.terms.pizza.Pizza,
((onto.terms.pizza.hasPrice <= 10) & (onto.terms.pizza.hasCalorificContentValue > 1000))
)
q
| Pizza | hasPricevalue | hasCalorificContentValuevalue | |
|---|---|---|---|
| 0 | American:245aa2f1 | 10.0 | 1292.0 |
Complex Nested Conditions#
You can create sophisticated queries by nesting logical operators. For example, let’s find Pizza items in the “sweet spot” with a price between 10-11 AND calorific content greater than 1000:
condition = ((onto.terms.pizza.hasPrice >= 10) & (onto.terms.pizza.hasPrice <= 11) & (onto.terms.pizza.hasCalorificContentValue > 1000))
q = onto.query(g, onto.terms.pizza.Pizza,
(condition)
)
q
| Pizza | hasPricevalue | hasCalorificContentValuevalue | |
|---|---|---|---|
| 0 | American:245aa2f1 | 10.0 | 1292.0 |
We can of course check how the actual SPARQL query looks like:
condition = ((onto.terms.pizza.hasPrice >= 10) & (onto.terms.pizza.hasPrice <= 11) & (onto.terms.pizza.hasCalorificContentValue > 1000))
q = onto.create_query(onto.terms.pizza.Pizza,
(condition)
)
print(q)
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX pizza: <https://example.org/pizza#>
SELECT DISTINCT ?Pizza ?hasPricevalue ?hasCalorificContentValuevalue
WHERE {
?Pizza pizza:hasPrice ?hasPricevalue .
?Pizza pizza:hasCalorificContentValue ?hasCalorificContentValuevalue .
{ ?Pizza rdf:type pizza:Pizza . }
UNION
{ ?Pizza rdf:type pizza:NamedPizza . }
UNION
{ ?Pizza rdf:type pizza:American . }
UNION
{ ?Pizza rdf:type pizza:AmericanHot . }
UNION
{ ?Pizza rdf:type pizza:Cajun . }
UNION
{ ?Pizza rdf:type pizza:Capricciosa . }
UNION
{ ?Pizza rdf:type pizza:Caprina . }
UNION
{ ?Pizza rdf:type pizza:Fiorentina . }
UNION
{ ?Pizza rdf:type pizza:FourSeasons . }
UNION
{ ?Pizza rdf:type pizza:FruttiDiMare . }
UNION
{ ?Pizza rdf:type pizza:Giardiniera . }
UNION
{ ?Pizza rdf:type pizza:LaReine . }
UNION
{ ?Pizza rdf:type pizza:Margherita . }
UNION
{ ?Pizza rdf:type pizza:Mushroom . }
UNION
{ ?Pizza rdf:type pizza:Napoletana . }
UNION
{ ?Pizza rdf:type pizza:Parmense . }
UNION
{ ?Pizza rdf:type pizza:PolloAdAstra . }
UNION
{ ?Pizza rdf:type pizza:PrinceCarlo . }
UNION
{ ?Pizza rdf:type pizza:QuattroFormaggi . }
UNION
{ ?Pizza rdf:type pizza:Rosa . }
UNION
{ ?Pizza rdf:type pizza:Siciliana . }
UNION
{ ?Pizza rdf:type pizza:SloppyGiuseppe . }
UNION
{ ?Pizza rdf:type pizza:Soho . }
UNION
{ ?Pizza rdf:type pizza:UnclosedPizza . }
UNION
{ ?Pizza rdf:type pizza:Veneziana . }
FILTER (((?hasPricevalue>="10"^^xsd:float)&&(?hasPricevalue<="11"^^xsd:float))&&(?hasCalorificContentValuevalue>"1000"^^xsd:float))
}
Working with Specific Class Selectors#
Remember from the previous notebook that you can control how classes and their hierarchies are queried. tools4RDF supports four different class selector patterns:
onto.terms.pizza.Pizzaoronto.terms.pizza.Pizza.all_subtypes: Queries the classPizzaand all its subclasses (default behavior)onto.terms.pizza.Pizza.any: Queries any entity that satisfies the predicate conditions, regardless of subclass hierarchyonto.terms.pizza.Pizza.only: Queries only the exact classPizza, excluding subclasses
These selectors become particularly useful when combined with comparison operators and filters.
Summary#
In this notebook, we explored advanced query-building capabilities in tools4RDF:
Comparison Operators: Use
<,>,<=,>=, and==to filter results based on property valuesLogical Operators: Combine conditions with
&(AND) and|(OR) for complex filtersMultiple Properties: Query multiple properties simultaneously
Nested Conditions: Build sophisticated queries by nesting logical operators for complex filtering logic
Class Selectors: Control query behavior with
.all_subtypes,.any, and.onlyto precisely target classes and hierarchies
These features allow you to construct complex SPARQL queries using intuitive Python syntax, making knowledge graph querying more accessible without requiring deep SPARQL expertise.