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.Pizza or onto.terms.pizza.Pizza.all_subtypes: Queries the class Pizza and all its subclasses (default behavior)

  • onto.terms.pizza.Pizza.any: Queries any entity that satisfies the predicate conditions, regardless of subclass hierarchy

  • onto.terms.pizza.Pizza.only: Queries only the exact class Pizza, 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:

  1. Comparison Operators: Use <, >, <=, >=, and == to filter results based on property values

  2. Logical Operators: Combine conditions with & (AND) and | (OR) for complex filters

  3. Multiple Properties: Query multiple properties simultaneously

  4. Nested Conditions: Build sophisticated queries by nesting logical operators for complex filtering logic

  5. Class Selectors: Control query behavior with .all_subtypes, .any, and .only to 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.