Federated Query Guide: Cross-Domain Semantic Analysis¶
Overview¶
This guide explains how to write and execute federated SPARQL queries across the fintech semantic mesh domains (Accounts, Transactions, Risk/Compliance). Federated queries enable unified analysis without centralizing data or semantic responsibility.
What is SPARQL Federation?¶
SPARQL Federation allows you to query multiple RDF datasets (served by different SPARQL endpoints) as if they were a single dataset. The SERVICE {} clause specifies which domain endpoint to query.
Standard SPARQL:
Federated SPARQL (across domains):
SELECT ?customer ?name ?riskScore
WHERE {
SERVICE <http://accounts-domain/sparql> {
?customer a fintech:Customer ;
fintech:customerName ?name .
}
SERVICE <http://risk-domain/sparql> {
?risk fintech:forCustomer ?customer ;
fintech:riskScore ?riskScore .
}
}
In the federated version:
- Customer data comes from Accounts domain
- Risk data comes from Risk/Compliance domain
- The SPARQL engine joins results by matching ?customer across domains
Query Execution Flow¶
1. Local Binding (Individual Domain Queries)¶
When SPARQL encounters a SERVICE clause, it queries that domain's endpoint independently:
Query Plan:
├─ SERVICE <accounts> { Get all customers }
│ └─ Results: [{?customer=<cust_1>, ?name="John"}, ...]
│
├─ SERVICE <risk> { Get risk profiles }
│ └─ Results: [{?risk=<risk_1>, ?customer=<cust_1>, ?riskScore=0.92}, ...]
│
└─ Join on ?customer binding
└─ Final: [{?customer=<cust_1>, ?name="John", ?riskScore=0.92}, ...]
2. Variable Binding Across Services¶
Variables (prefixed with ?) are the join keys. SPARQL binds variables across SERVICE clauses:
WHERE {
SERVICE <http://accounts-domain/sparql> {
?customer a fintech:Customer # Binds ?customer
}
SERVICE <http://transactions-domain/sparql> {
?txn fintech:transactionDebtor ?customer # Uses same ?customer binding
}
}
3. Result Aggregation¶
The SPARQL engine collects results from each service and combines them:
Accounts Domain Results Transactions Domain Results
?customer = <cust_1> + ?txn = <txn_001>
?customer = <cust_2> + ?txn = <txn_002>
+ ?customer = <cust_1> (from transactionDebtor)
?customer = <cust_1>
?customer = <cust_2>
Join on ?customer:
{?customer=<cust_1>, ?txn=<txn_001>}
{?customer=<cust_1>, ?txn=<txn_002>}
{?customer=<cust_2>, ?txn=<txn_003>}
Writing Cross-Domain Queries: Step-by-Step¶
Step 1: Identify Required Data¶
Business Question: "Find customers with failed transactions AND high risk scores"
Data needed: - Customer identity and name → Accounts domain - Failed transactions → Transactions domain - Risk score → Risk/Compliance domain
Step 2: Design the JOIN Strategy¶
Variables are join keys. Plan which variables link domains:
Accounts ←→ Transactions: via ?customer (transactionDebtor)
Accounts ←→ Risk: via ?customer (forCustomer)
Step 3: Write Individual SERVICE Blocks¶
Start simple, add complexity incrementally.
Service 1: Accounts
SERVICE <http://accounts-domain/sparql> {
?customer a fintech:Customer ;
fintech:customerName ?name .
}
Service 2: Transactions
SERVICE <http://transactions-domain/sparql> {
?txn fintech:transactionDebtor ?customer ;
fintech:transactionStatus "failed" .
}
Service 3: Risk
SERVICE <http://risk-domain/sparql> {
?risk fintech:forCustomer ?customer ;
fintech:riskScore ?riskScore .
FILTER (?riskScore > 0.8)
}
Step 4: Combine with Shared Variables¶
Put SERVICE blocks together, using ?customer to join:
SELECT ?customer ?name ?riskScore
WHERE {
SERVICE <http://accounts-domain/sparql> {
?customer a fintech:Customer ;
fintech:customerName ?name .
}
SERVICE <http://transactions-domain/sparql> {
?txn fintech:transactionDebtor ?customer ;
fintech:transactionStatus "failed" .
}
SERVICE <http://risk-domain/sparql> {
?risk fintech:forCustomer ?customer ;
fintech:riskScore ?riskScore .
FILTER (?riskScore > 0.8)
}
}
Step 5: Add Aggregation and Filtering¶
SELECT ?customer ?name ?riskScore (COUNT(DISTINCT ?txn) AS ?failedCount)
WHERE {
# ... SERVICE blocks from Step 4 ...
}
GROUP BY ?customer ?name ?riskScore
HAVING (COUNT(DISTINCT ?txn) >= 1)
ORDER BY DESC(?riskScore)
Case Study: Churn Analysis Query¶
The churn analysis query from churn-analysis-federated.sparql is a complete example:
What it does: 1. Find customers (Accounts domain) 2. Count their failed payments (Transactions domain) 3. Get their risk score (Risk/Compliance domain) 4. Filter to high-risk customers (riskScore > 0.8) 5. Recommend actions based on risk level
Key patterns:
- Cross-domain linking: fintech:transactionDebtor and fintech:forCustomer connect via ?customer
- Filtering: FILTER clause inside SERVICE limits results per domain
- Aggregation: GROUP BY and HAVING combine results across domains
- Binding: Variables like ?txnDate are local to SERVICE blocks; ?customer is shared
Common Patterns¶
Pattern 1: Simple Join¶
Link data from two domains:
SELECT ?customer ?name ?riskScore
WHERE {
SERVICE <http://accounts-domain/sparql> {
?customer fintech:customerName ?name .
}
SERVICE <http://risk-domain/sparql> {
?risk fintech:forCustomer ?customer ;
fintech:riskScore ?riskScore .
}
}
Pattern 2: Multiple Entities in One Domain¶
Query multiple entity types from one domain:
SELECT ?customer ?account ?balance ?txn
WHERE {
SERVICE <http://accounts-domain/sparql> {
?customer a fintech:Customer .
?account fintech:accountOwner ?customer ;
fintech:accountBalance ?balance .
}
SERVICE <http://transactions-domain/sparql> {
?txn fintech:transactionDebtor ?customer .
}
}
Pattern 3: Conditional Binding¶
Use OPTIONAL to include optional data:
SELECT ?customer ?riskScore ?complianceStatus
WHERE {
SERVICE <http://accounts-domain/sparql> {
?customer a fintech:Customer .
}
SERVICE <http://risk-domain/sparql> {
?risk fintech:forCustomer ?customer ;
fintech:riskScore ?riskScore .
}
OPTIONAL {
SERVICE <http://risk-domain/sparql> {
?risk fintech:complianceStatus ?complianceStatus .
}
}
}
Pattern 4: Aggregation Across Domains¶
Count events per entity:
SELECT ?customer (COUNT(DISTINCT ?txn) AS ?txnCount)
WHERE {
SERVICE <http://accounts-domain/sparql> {
?customer a fintech:Customer .
}
SERVICE <http://transactions-domain/sparql> {
?txn fintech:transactionDebtor ?customer .
}
}
GROUP BY ?customer
Performance Considerations¶
Query Optimization¶
-
Filter Early: Push FILTER clauses inside SERVICE blocks to reduce transferred data
# Good: Filter in service SERVICE <http://risk-domain/sparql> { ?risk fintech:forCustomer ?customer ; fintech:riskScore ?riskScore . FILTER (?riskScore > 0.8) # ← Filter at source } # Expensive: Filter after transfer SERVICE <http://risk-domain/sparql> { ?risk fintech:forCustomer ?customer ; fintech:riskScore ?riskScore . } FILTER (?riskScore > 0.8) # ← Filters after all data returned -
Limit Results: Use LIMIT to cap transferred data
-
Select Specific Properties: Only retrieve needed properties
Network Latency¶
Each SERVICE block makes a network request. Minimize round-trips:
- Combine related filters into one SERVICE block (not multiple)
- Avoid querying the same service multiple times for similar data
Result Set Size¶
Large result sets from individual domains multiply when joined:
Service A: 1,000 customers
Service B: 10,000 transactions
Service C: 500 risk profiles
Worst case join: 1,000 × 10,000 × 500 = 5 billion rows
(In practice, WHERE clause filters this dramatically)
Use GROUP BY and HAVING to aggregate early.
Troubleshooting¶
Issue: Query Returns No Results¶
Possible causes: 1. Variable names don't match across SERVICE blocks 2. IRIs don't match (case-sensitive) 3. Data doesn't exist in one or more domains 4. FILTER is too restrictive
Solution:
- Run each SERVICE block independently to verify data exists
- Check IRI formats (e.g., <https://...> vs https://...)
- Loosen FILTER conditions temporarily to verify joins work
Issue: Query Times Out¶
Possible causes: 1. One domain endpoint is slow 2. Query transfers too much data 3. Federation engine can't optimize the query
Solution:
- Check endpoint response time: curl http://endpoint/sparql?query=SELECT%201
- Add LIMIT to reduce transferred data
- Move FILTER clauses inside SERVICE blocks
- Run individual SERVICE queries to identify bottleneck
Issue: Variable Binding Fails¶
Symptom: Variables have no values in results
Cause: Variable binding paths don't exist
# ✗ This binds ?customer from Service 1, but Service 2 uses different binding
SERVICE <http://accounts-domain/sparql> {
?customer a fintech:Customer .
}
SERVICE <http://transactions-domain/sparql> {
?txn fintech:transactionDebtor ?payer . # ← Wrong variable, won't join
}
# ✓ Correct: Use same variable name
SERVICE <http://accounts-domain/sparql> {
?customer a fintech:Customer .
}
SERVICE <http://transactions-domain/sparql> {
?txn fintech:transactionDebtor ?customer . # ← Joins via ?customer
}
Running Queries¶
Via Command Line¶
Via Python API¶
from federated_query_client import FederatedQueryClient
endpoints = {
"accounts": "http://localhost:3030/accounts/sparql",
"transactions": "http://localhost:3031/transactions/sparql",
"risk": "http://localhost:3032/risk_compliance/sparql"
}
client = FederatedQueryClient(endpoints)
# Load and execute query
with open("churn-analysis-federated.sparql") as f:
query = f.read()
results = client.execute_query(query)
print(client.format_results(results))
Via curl¶
curl -X GET \
--data-urlencode "query@churn-analysis-federated.sparql" \
http://localhost:3030/accounts/sparql \
-H "Accept: application/sparql-results+json"