SPARQL Generation
Use this skill to generate SPARQL queries from resolved Wikidata entities, properties, and verified graph paths. The skill constructs syntactically correct queries using proper Wikidata modeling patterns.
Files
scripts/sparql_generator.py: standalone script for generating and validating SPARQL queries
references/sparql-1.1-query-features.md: SPARQL 1.1 language reference for query forms, property paths, aggregation, subqueries, VALUES, BIND, negation, and solution modifiers
references/wikidata-data-model.md: Wikidata RDF/modeling reference for wdt: vs p:/ps:, qualifiers, references, ranks, normalized values, datatype handling, and statement/value nodes
When To Use This Skill
Use this skill when:
- You have resolved entity IDs (QIDs) and property IDs (PIDs) from wikidata-search
- You have verified graph paths from graph-exploration
- You need to assemble a SPARQL query with proper triple patterns
- You want to validate SPARQL syntax before execution
- You need to apply common Wikidata query patterns (subclass traversal, label service, qualifiers)
Reference Usage
Consult the bundled references whenever the query requires details beyond the core patterns in this file:
- Use
references/sparql-1.1-query-features.md when deciding which SPARQL construct to use or how to structure it correctly. Typical cases: OPTIONAL vs UNION, FILTER NOT EXISTS vs MINUS, property-path syntax, VALUES, BIND, aggregates with GROUP BY/HAVING, subqueries, ORDER BY with LIMIT, and variable-scope questions.
- Use
references/wikidata-data-model.md when deciding which Wikidata RDF layer to query. Typical cases: choosing wdt: vs p:/ps:, accessing qualifiers with pq:, references with pr:, ranks with wikibase:rank, normalized quantities via psn:/wikibase:quantityAmount, datatype-specific handling, and understanding truthy versus full statement semantics.
- Prefer this
SKILL.md for task-specific generation rules and project-specific guardrails; use the reference documents to resolve syntax/modeling uncertainty, not to replace the workflow here.
Requirements
Install skill dependencies from the workspace root with uv sync.
The script uses rdflib for SPARQL parsing/validation (optional) and basic Python for query construction. No API keys required.
Environment Variables
None required.
Safety Rules
- Generate only read-only queries (SELECT, ASK, CONSTRUCT, DESCRIBE).
- Always include a LIMIT clause during iterative development (remove only for final verified queries if appropriate).
- Never generate DELETE, INSERT, or UPDATE operations.
- Use verified IDs only — never guess QIDs or PIDs.
Script Modes
1. Validate SPARQL Syntax (validate)
Check that a SPARQL query is syntactically valid:
uv run python .agents/skills/sparql-generation/scripts/sparql_generator.py \
--mode validate \
--sparql "SELECT ?x WHERE { ?x wdt:P31 wd:Q5 . } LIMIT 10"
2. Generate from Template (generate)
Generate a SPARQL query from structured inputs:
uv run python .agents/skills/sparql-generation/scripts/sparql_generator.py \
--mode generate \
--question "What is the capital of France?" \
--entities '{"France": "Q142"}' \
--properties '{"capital": "P36"}' \
--pattern "direct-lookup"
3. Apply Pattern (pattern)
Apply a named query pattern with entity/property substitution:
uv run python .agents/skills/sparql-generation/scripts/sparql_generator.py \
--mode pattern \
--pattern "type-filter" \
--entities '{"type": "Q6256", "constraint_property": "P30", "constraint_value": "Q18"}' \
--properties '{"output_property": "P36"}'
Script Usage
Arguments
Required:
--mode / -m: Operation mode (validate, generate, pattern)
Mode-specific:
--sparql: SPARQL query string (required for validate)
--question: Natural-language question (for generate mode context)
--entities: JSON object mapping entity names to QIDs
--properties: JSON object mapping property names to PIDs
--paths: JSON array of verified graph paths from exploration
--pattern: Named pattern to use (direct-lookup, reverse-lookup, type-filter, aggregation, qualifier, subclass, date-filter, top-k)
Optional:
--include-labels: Include the Wikidata label service (default: true)
--limit: Add a LIMIT clause (default: none for final, 20 for exploration)
--output-file: Write result JSON to a file instead of stdout
Return Shape
Validation Result
{
"success": true,
"mode": "validate",
"sparql": "SELECT ?x WHERE { ?x wdt:P31 wd:Q5 . } LIMIT 10",
"valid": true,
"errors": [],
"warnings": ["No label service included — results will show URIs instead of labels"],
"error": null
}
Generation Result
{
"success": true,
"mode": "generate",
"question": "What is the capital of France?",
"sparql": "SELECT ?capital ?capitalLabel WHERE {\n wd:Q142 wdt:P36 ?capital .\n OPTIONAL { ?capital rdfs:label ?capitalLabel FILTER(LANG(?capitalLabel) = \"en\") }\n}",
"assumptions": [
"France = Q142 (country)",
"capital = P36 (capital property)",
"Direct property lookup (wdt:) — no qualifiers needed"
],
"pattern_used": "direct-lookup",
"error": null
}
Query Patterns Reference
1. Direct Property Lookup
# "What is the X of Y?"
SELECT ?value ?valueLabel WHERE {
wd:Q_ENTITY wdt:P_PROPERTY ?value .
OPTIONAL { ?value rdfs:label ?valueLabel FILTER(LANG(?valueLabel) = "en") }
}
2. Reverse Lookup
# "What entities have property X pointing to Y?"
SELECT ?entity ?entityLabel WHERE {
?entity wdt:P_PROPERTY wd:Q_TARGET .
OPTIONAL { ?entity rdfs:label ?entityLabel FILTER(LANG(?entityLabel) = "en") }
}
3. Type Filter
# "Which entities of type T satisfy condition C?"
SELECT ?entity ?entityLabel WHERE {
?entity wdt:P31 wd:Q_TYPE ;
wdt:P_FILTER ?filterValue .
FILTER(?filterValue > threshold)
OPTIONAL { ?entity rdfs:label ?entityLabel FILTER(LANG(?entityLabel) = "en") }
}
4. Subclass Traversal
# "All entities that are instances of T or any subclass of T"
SELECT ?entity ?entityLabel WHERE {
?entity wdt:P31/wdt:P279* wd:Q_TYPE .
OPTIONAL { ?entity rdfs:label ?entityLabel FILTER(LANG(?entityLabel) = "en") }
}
5. Aggregation (COUNT, AVG, etc.)
# "How many entities of type T?"
SELECT (COUNT(?entity) AS ?count) WHERE {
?entity wdt:P31 wd:Q_TYPE .
}
6. Top-K / Ordering
# "Top N entities by property value"
SELECT ?entity ?entityLabel ?value WHERE {
?entity wdt:P31 wd:Q_TYPE ;
wdt:P_MEASURE ?value .
OPTIONAL { ?entity rdfs:label ?entityLabel FILTER(LANG(?entityLabel) = "en") }
}
ORDER BY DESC(?value)
LIMIT N
7. Date Filters
# "Entities where date property is after/before a date"
SELECT ?entity ?entityLabel ?date WHERE {
?entity wdt:P31 wd:Q_TYPE ;
wdt:P_DATE ?date .
FILTER(?date >= "2000-01-01T00:00:00Z"^^xsd:dateTime)
OPTIONAL { ?entity rdfs:label ?entityLabel FILTER(LANG(?entityLabel) = "en") }
}
Important: Always use the full ISO 8601 format with time component (T00:00:00Z) for xsd:dateTime comparisons. Bare date strings (e.g., "2000-01-01"^^xsd:dateTime) fail on some endpoints.
8. Qualifier Access
# "What is the value of property P with qualifier Q?"
SELECT ?value ?valueLabel ?qualifier WHERE {
wd:Q_ENTITY p:P_PROPERTY ?stmt .
?stmt ps:P_PROPERTY ?value ;
pq:P_QUALIFIER ?qualifier .
OPTIONAL { ?value rdfs:label ?valueLabel FILTER(LANG(?valueLabel) = "en") }
}
9. OPTIONAL for Non-Required Fields
# "List entities with property X, include Y if available"
SELECT ?entity ?entityLabel ?x ?y WHERE {
?entity wdt:P31 wd:Q_TYPE ;
wdt:P_X ?x .
OPTIONAL { ?entity wdt:P_Y ?y . }
OPTIONAL { ?entity rdfs:label ?entityLabel FILTER(LANG(?entityLabel) = "en") }
}
10. VALUES for Known Entity Sets
# "Information about specific entities"
SELECT ?entity ?entityLabel ?value WHERE {
VALUES ?entity { wd:Q1 wd:Q2 wd:Q3 }
?entity wdt:P_PROPERTY ?value .
OPTIONAL { ?entity rdfs:label ?entityLabel FILTER(LANG(?entityLabel) = "en") }
}
Generation Rules
Before finalizing a query, cross-check any non-trivial syntax against references/sparql-1.1-query-features.md and any statement-level or datatype-specific Wikidata modeling against references/wikidata-data-model.md.
Use only verified IDs: Never guess QIDs or PIDs. All IDs must come from wikidata-search or graph-exploration.
Correct triple pattern direction: Verified in graph exploration. wd:Q wdt:P ?obj vs ?subj wdt:P wd:Q.
Include labels using rdfs:label: Use OPTIONAL { ?x rdfs:label ?xLabel FILTER(LANG(?xLabel) = "en") } for human-readable labels. NOTE: SERVICE wikibase:label is NOT available on the custom endpoint — it causes HTTP 500 errors. Always use rdfs:label OPTIONAL pattern instead. Omit labels entirely for count/aggregate-only queries.
Use VALUES for known entities from the question: When the question explicitly names a small set of entities (e.g., "information about France, Germany, and Italy"), use VALUES ?entity { wd:Q1 wd:Q2 wd:Q3 }. However, NEVER use VALUES to inject entities discovered from world knowledge that the endpoint didn't return — if a declarative query returns fewer results than expected, those ARE the results.
Use OPTIONAL sparingly: Only for genuinely optional fields that may not exist on all entities.
Avoid Cartesian products: Ensure all triple patterns share variables or are properly constrained.
Use DISTINCT when needed: Especially after JOINs that could produce duplicates.
Bound exploration queries: Always include LIMIT during iterative development.
Use p:/ps:/pq: for qualifiers: Switch from wdt: when qualifier access is needed.
Filter placement: Place FILTERs close to the triple patterns they constrain.
Use normalized values for quantities: When retrieving numeric quantity values (distance, mass, area, duration, speed, etc.), ALWAYS use the normalized value path p:P.../psn:P.../wikibase:quantityAmount instead of wdt:P.... This returns SI-normalized values (meters, kg, m², seconds) which are consistent regardless of how the data was entered. See "Quantity Property Patterns" below.
Use full statement model for temporal/historical data: When the question asks about historical extremes ("highest ever", "all-time record") or needs to count ALL statements including past ones (e.g., "how many spouses total"), use p:P.../ps:P... to access all statements. The wdt: prefix only returns the single "truthy" (current/best-ranked) value and misses historical data. However, for "current state" questions about multi-valued properties (like capitals, members, affiliations), prefer wdt: truthy paths which already reflect Wikidata's rank-based currentness semantics. Only switch to statement-level access when you need to explicitly inspect ALL statements (including deprecated/historical ones) or when the truthy path demonstrably misses data that should be there.
Return entity IDs for item-valued properties: When a Wikidata property has datatype wikibase-item (e.g., P735 "given name", P3150 "birthday", P1441 "present in work"), the query must return the entity QID, not a label string. Check the property datatype during graph exploration.
Use transitive P131+ for location containment: When filtering by geographic location, use wdt:P131+ wd:Q_location (transitive) instead of direct wdt:P131 wd:Q_location. Items may be in sub-administrative units (suburbs, districts) that are contained within the target location.
Compose property paths for country-level location queries: When the question asks about items "in country X" but the linking property points to a specific location (city, region), compose the path to reach the country level. Common patterns:
- Filming location to country:
wdt:P915/wdt:P131*/wdt:P17?
- Birthplace to country:
wdt:P19/wdt:P131*/wdt:P17?
- General location to continent:
wdt:P276?/wdt:P131*/wdt:P17?/wdt:P30
- Instance location to country:
wdt:P131*/wdt:P17
- Organization country (multiple paths):
(wdt:P17|wdt:P159/wdt:P17|wdt:P495) — organizations may have country via direct P17, headquarters location (P159→P17), or country of origin (P495)
The ? on P17 handles cases where the item already IS a country. Always verify via exploration which intermediate steps are needed.
Only include constraints stated in the question: Do not add plausibility filters, unit restrictions, or scope constraints that are not explicitly mentioned or directly required by the question. For example:
- Do not add
FILTER(?height < 3) or restrict planets to the solar system unless the question says so
- Do not add
MINUS { ?x wdt:P576 ?dissolved } (exclude dissolved entities) unless the question says "currently existing"
- Do not add
FILTER(?x != wd:Q_ENTITY) self-exclusion unless the question logically requires it (e.g., "not bordering Germany" does NOT exclude Germany itself — Germany doesn't border itself)
- Do not add extra type constraints that the question didn't mention
Filter by unit for monetary comparisons: When comparing monetary values (box office, GDP, budget), always filter for a common currency unit using wikibase:quantityUnit to ensure like-for-like comparison. Use psv: to access the value node with its unit.
Exclude ended relationships for current-state questions: For questions about current status ("Does X have a wife?", "Who is the current PM?"), add MINUS { ?stmt pq:P582 ?endDate } to exclude relationships that have ended.
Sum related sub-properties for total measurements: When a question asks for a total measurement (e.g., "how many floors", "total length", "overall height") and the entity has complementary sub-properties (e.g., P1101 "floors above ground" + P1139 "floors below ground"), sum them to get the total. Use BIND(?a + ?b AS ?total) or arithmetic in SELECT.
Prefer returning entities over counts: When a question asks "how many X?" or "how many X satisfy Y?", the expected output is almost always the list of matching entity QIDs, NOT a numeric count. Use SELECT ?x WHERE { ... } to return the entities themselves. Only use SELECT (COUNT(...) AS ?count) when the expected answer is clearly a large number (>50) and entities themselves aren't meaningful to list. Important: "How many movies has X directed?" → return the list of movie QIDs. "How many countries are there?" → return the list of country QIDs.
SELECT only the answer column: When the query uses ORDER BY on a value column (e.g., population, date), only SELECT the entity variable, not the ordering column. Including extra columns in SELECT may leak numeric/date values into the final answer. For example, SELECT ?country WHERE { ... ORDER BY DESC(?pop) } is correct; SELECT ?country ?pop WHERE { ... } would return the population value alongside the entity.
Match the question's scope exactly: Use exactly the relationships stated in the question. If the question says "Latin American country", map to the specific Wikidata relationship for "Latin America" (e.g., P361 Q12585). Do not broaden to "South American continent" or similar approximations. Stick to the most direct relationship matching the question's phrasing.
Consider subclass traversal on property values (conditionally): When matching property values against a general class (e.g., architectural style = brutalism, food = pizza, event = theft), the actual value on the entity may be a more specific subclass. Use wdt:P_PROP/wdt:P279* to also match subclasses of the target value. This is relevant for properties like:
- Architectural style (P149):
wdt:P149/wdt:P279* wd:Q_STYLE
- Significant event (P793):
ps:P793/wdt:P279* wd:Q_EVENT_TYPE
- Last meal (P3902):
wdt:P3902/wdt:P279* wd:Q_FOOD
- Taxon parent (P171): use
wdt:P171+ for transitive traversal
Guardrails — do NOT apply subclass traversal when:
- The question is a boolean/ASK query checking for an exact value (e.g., "is there a flag with pink?" → use exact
wdt:P462 wd:Q429220, not P279*)
- The property is
P106 (occupation) — Wikidata editors assign specific occupations directly; use direct matching first
- The initial direct query already returns a reasonable number of results (>5 for a list question)
- The property is
P1412 (languages spoken), P27 (citizenship), or other simple-valued properties where subclass semantics don't apply
Query through instances, not class items: When a question asks about members of a class ("paintings of X", "countries where Y are found", "collections with Z"), query through individual instances using ?x wdt:P31/wdt:P279* wd:Q_CLASS, then access properties on those instances. Do NOT rely on properties of the class item itself (e.g., do not query wd:Q_CLASS wdt:P17 ?country), as class-level data is typically incomplete.
Commit to one property direction: Once exploration verifies the correct property direction, use only that direction. Do NOT hedge by adding a UNION with the inverse property. Example: if P1376 (capital of) is the verified direction, do not UNION with ?x wdt:P36 wd:Q_CITY. Forward and inverse properties often have different coverage and adding both produces false positives.
Rules for Mention-Provided Entities
Rule: Verify class suitability — Before using a mention entity in wdt:P31 wd:Q_entity, run a quick count. If it returns 0 results, the entity may be a concept/movement rather than a type. Search for the correct classifying entity.
Rule: Check for combined/intermediate classes — When the question combines two concepts (e.g., "NP-complete video games"), prefer using a single intermediate class (wdt:P31 wd:Q_combined_class) over intersecting two separate type constraints. Intermediate classes are more complete in Wikidata.
Rule: Don't assume mention properties are the linking property — When a mention has a property field, it may indicate the property's role in the question semantics, not necessarily the exact triple pattern to use. Always verify through graph exploration.
Common Mistakes to Avoid
| Mistake |
Fix |
Using wdt:P31 wd:Q5 for subclasses |
Use wdt:P31/wdt:P279* wd:Q5 for subclass inclusion |
Using wdt:P106/wdt:P279* for occupations |
Prefer direct wdt:P106 wd:Q_OCCUPATION first — subclass traversal on occupation over-generates. Only add /wdt:P279* if the direct query returns < 3 results for a broad category |
| Missing label service |
Use OPTIONAL { ?x rdfs:label ?xLabel FILTER(LANG(?xLabel) = "en") } (not SERVICE wikibase:label which is unavailable) |
| Wrong property direction |
Verify with graph exploration before generating |
Unbounded P279* traversal |
Add LIMIT or restrict depth |
| Filtering on labels instead of IDs |
Use wd:QID not FILTER(CONTAINS(?label, "...")) |
| Missing DISTINCT with multiple optional patterns |
Add DISTINCT to SELECT |
Using FILTER(?x = wd:Q...) instead of direct triple |
Use wd:Q... wdt:P ?y directly |
| Qualifying objects in SELECT without GROUP BY |
Use aggregate functions or remove from SELECT |
Using wdt: for quantity values |
Use p:P.../psn:P.../wikibase:quantityAmount for normalized SI values |
Using wdt: for historical maximums |
Use p:P.../ps:P... to access all historical statements |
| Returning label strings for item-valued properties |
Return the entity QID (e.g., ?givenName as QID, not its label) |
Using direct wdt:P131 for location queries |
Use wdt:P131+ (transitive) to include sub-divisions |
| Adding plausibility filters not in the question |
Do not add FILTER(?x < 3) or restrict to solar system unless asked |
| Comparing monetary values without unit filter |
Use psv: + wikibase:quantityUnit to filter for same currency |
Using wdt:P26 for "currently married" |
Use p:P26 + MINUS { ?stmt pq:P582 ?end } to exclude ex-spouses |
| Using only P1101 for "total floors" |
Sum P1101 (above ground) + P1139 (below ground) for total |
| Returning COUNT when entities are expected |
Return entity list; only use COUNT for explicitly numeric questions with large result sets |
| Returning entity list when COUNT is expected |
For "How many X?" questions, use SELECT (COUNT(DISTINCT ?x) AS ?count). But first check if the parent entity has a direct quantity property (e.g., P1114, P1538, P1539) that stores the count — if it exists, use it directly as it's more authoritative than counting instances. |
Using wdt: for "current" position when qualifier is needed |
For "Who IS the current X?" (present tense), wdt: truthy value reflects the best-ranked statement which is usually current. But for positions with multiple holders over time (presidents, chairpersons), verify by checking: if wdt:P_position returns multiple values, switch to p:P39/ps:P39 with MINUS { ?stmt pq:P582 ?end } to get only active (non-ended) holders. |
| Broadening scope beyond what the question states |
"Latin American" = P361 Q12585; don't expand to "South American continent" |
Using wdt:P921/wdt:P279* (subclass traversal on topic) |
This massively over-generates because it returns items whose topic is ANY subclass of the target concept. Prefer direct wdt:P921 wd:Q_TOPIC unless there is explicit evidence that topic subclass traversal is needed. Only use P279* on the entity type side, not the topic/subject side. |
| Hedging with UNION of forward + inverse property |
Choose ONE direction after verification. P1376 and P36 are NOT interchangeable — one may cover more entities. |
Using wdt:P_PROP wd:Q_VALUE when values may be subclasses |
Consider wdt:P_PROP/wdt:P279* wd:Q_VALUE for style/event/food properties — but NOT for occupation, language, citizenship, or boolean/ASK queries (see Rule 23 guardrails) |
| Querying properties on the class item directly |
Query through instances: ?x wdt:P31/wdt:P279* wd:Q_CLASS . ?x wdt:P_PROP ?value — not wd:Q_CLASS wdt:P_PROP ?value |
| Replacing a declarative query with hardcoded VALUES |
Never inject world knowledge as VALUES. If the endpoint returns few results, that IS the answer. |
| Using only one location property for country-level queries |
Compose paths: P915/P131*/P17? for filming locations, P19/P131*/P17? for birthplaces |
Using P179 (part of series) for "what parts does X have?" |
Use outgoing wd:X wdt:P527 ?part (has part) instead — P179 incoming may include unauthorized additions |
| Using only P176 for "made by" questions |
Include P176|P127 (manufacturer OR owned by) for broader coverage |
| Using COUNT when entities are expected |
Return SELECT ?x WHERE {...} (entity list), not SELECT (COUNT(?x) AS ?c) — evaluation expects QIDs |
| Reading P1114 (quantity) for "how many instances?" |
Count instances: SELECT (COUNT(?x)) WHERE { ?x wdt:P31 wd:Q_CLASS } — P1114 on the class is often wrong/outdated |
| Using P1532 for team→country mapping |
Prefer wdt:P17 which has broader coverage on sports team entities |
| Returning ALL predecessors for "before X" when person held office multiple times |
When X held a position multiple non-consecutive times (e.g., Trump as president twice), "before X" means predecessor of the FIRST term. Use ORDER BY ?startDate LIMIT 1 on the P39 statements to get only the earliest term, then extract P1365 (replaces) from that statement. |
| Using P31 with a mentioned entity for "brands of X" |
Mentions may provide a product class (Q431289 "smartphone model") but brands are not typed as product instances. Instead search for companies (P31 Q4830453/Q783794) that manufacture (P176^ or P1056) the product type, or check if Wikidata has a "brand" class for the domain. |
| Using MINUS on all properties for "solely as X" |
MINUS { ?item ?anyProp wd:Q_person . FILTER(?anyProp != wdt:P_role) } is too aggressive — it removes items where the person appears in metadata properties (P1040 editor, P162 producer). For "solely as director", exclude only competing CREDIT roles: MINUS { ?film wdt:P58 wd:Q_person } (screenwriter) and MINUS { ?film wdt:P162 wd:Q_person } (producer). Or count roles: FILTER NOT EXISTS { ?film ?creditRole wd:Q_person . FILTER(?creditRole IN (wdt:P58, wdt:P162, wdt:P1431)) } |
Quantity Property Patterns
When a question asks for a numeric measurement (distance, mass, area, duration, speed, height, etc.), always use the normalized value path. This ensures consistent SI units regardless of how the data was originally entered.
Pattern: Retrieve a normalized quantity value
# "How far is X from Earth?" / "What is the area of X?" / "How heavy is X?"
SELECT ?value WHERE {
wd:Q_ENTITY p:P_QUANTITY/psn:P_QUANTITY/wikibase:quantityAmount ?value .
}
Examples:
- Distance from Earth (P2583):
p:P2583/psn:P2583/wikibase:quantityAmount → meters
- Duration (P2047):
p:P2047/psn:P2047/wikibase:quantityAmount → seconds
- Mass (P2067):
p:P2067/psn:P2067/wikibase:quantityAmount → kilograms
- Area (P2046):
p:P2046/psn:P2046/wikibase:quantityAmount → square meters
- Height (P2048):
p:P2048/psn:P2048/wikibase:quantityAmount → meters
- Speed (P2052):
p:P2052/psn:P2052/wikibase:quantityAmount → meters per second
- Wheelbase (P3039):
p:P3039/psn:P3039/wikibase:quantityAmount → meters
- Course length (P3157):
p:P3157/psn:P3157/wikibase:quantityAmount → meters
Pattern: Top-K by quantity with normalization
# "What is the tallest/fastest/heaviest X?"
SELECT ?entity WHERE {
?entity wdt:P31/wdt:P279* wd:Q_TYPE .
?entity p:P_QUANTITY/psn:P_QUANTITY/wikibase:quantityAmount ?value .
}
ORDER BY DESC(?value)
LIMIT 1
Pattern: Monetary comparison with unit filter
# "Which X has the highest box office (in USD)?"
SELECT ?entity WHERE {
?entity wdt:P31/wdt:P279* wd:Q_TYPE .
?entity p:P2142 ?stmt .
?stmt psv:P2142 ?valueNode .
?valueNode wikibase:quantityAmount ?amount ;
wikibase:quantityUnit wd:Q4917 . # Q4917 = US dollar
}
ORDER BY DESC(?amount)
LIMIT 1
Temporal and Historical Patterns
Pattern: Historical maximum (all-time best)
# "What is the highest Elo rating ever?" — need ALL statements, not just current
SELECT (MAX(?value) AS ?max) WHERE {
?entity p:P1087/ps:P1087 ?value .
}
Pattern: Current position holder (most recent start date)
# "Who is the current PM of X?" — use position held with temporal ordering
SELECT ?person WHERE {
?person p:P39 ?stmt .
?stmt ps:P39 wd:Q_POSITION .
?stmt pq:P580 ?startDate .
}
ORDER BY DESC(?startDate)
LIMIT 1
Pattern: Current relationship (exclude ended)
# "Does X currently have a spouse?"
ASK WHERE {
wd:Q_PERSON p:P26 ?stmt .
?stmt ps:P26 ?spouse .
MINUS { ?stmt pq:P582 ?endDate }
}
Pattern: Summing complementary sub-properties
# "How many floors does building X have?" (total = above + below ground)
SELECT (?above + ?below AS ?totalFloors) WHERE {
wd:Q_BUILDING wdt:P1101 ?above ;
wdt:P1139 ?below .
}
Other examples of complementary properties that may need summing:
- P1101 (floors above ground) + P1139 (floors below ground) = total floors
- Multiple distance/length components when asking for "total"
Pattern: Ordinal qualifier access
# "What is the Nth item in sequence X?"
SELECT ?value WHERE {
wd:Q_ENTITY p:P_PROPERTY ?stmt .
?stmt pq:P1545 "N" . # series ordinal
?stmt ps:P_PROPERTY ?value .
}
Integration with text2sparql Pipeline
Input from Previous Steps
The generation step receives:
- From wikidata-search: Resolved QIDs and PIDs with labels
- From graph-exploration: Verified paths, directions, qualifier structures
Output to Next Step
The generated SPARQL is passed to sparql-execution for execution and validation.
Troubleshooting
- Syntax errors from validator: Check bracket matching, semicolons between triple patterns, and proper string escaping.
- Unsure which SPARQL feature to use: Check
references/sparql-1.1-query-features.md before improvising syntax for negation, aggregation, property paths, subqueries, or inline bindings.
- Unsure which Wikidata prefix/model to use: Check
references/wikidata-data-model.md before choosing between wdt:, p:/ps:, pq:, psv:, psn:, or rank/reference access.
- Wrong results direction: Swap subject/object positions and re-run.
- No results with subclass traversal: Try without
P279* first to isolate the issue.
- Label service not working: Do NOT use
SERVICE wikibase:label — it causes HTTP 500 errors on the custom endpoint. Use OPTIONAL { ?x rdfs:label ?xLabel FILTER(LANG(?xLabel) = "en") } instead.
- Timeout during generation: The generation script itself should be fast; if it's slow, it's likely a validation step querying the endpoint.
1---2name: sparql-generation3description: SPARQL generation skill that constructs a SPARQL query from resolved Wikidata entities, properties, and graph paths. Use when you have verified Wikidata IDs and need to assemble them into a syntactically correct SPARQL query with proper triple patterns, filters, and aggregations.4---56# SPARQL Generation78Use this skill to generate SPARQL queries from resolved Wikidata entities, properties, and verified graph paths. The skill constructs syntactically correct queries using proper Wikidata modeling patterns.910## Files1112- `scripts/sparql_generator.py`: standalone script for generating and validating SPARQL queries13- `references/sparql-1.1-query-features.md`: SPARQL 1.1 language reference for query forms, property paths, aggregation, subqueries, `VALUES`, `BIND`, negation, and solution modifiers14- `references/wikidata-data-model.md`: Wikidata RDF/modeling reference for `wdt:` vs `p:/ps:`, qualifiers, references, ranks, normalized values, datatype handling, and statement/value nodes1516## When To Use This Skill1718Use this skill when:19- You have resolved entity IDs (QIDs) and property IDs (PIDs) from wikidata-search20- You have verified graph paths from graph-exploration21- You need to assemble a SPARQL query with proper triple patterns22- You want to validate SPARQL syntax before execution23- You need to apply common Wikidata query patterns (subclass traversal, label service, qualifiers)2425## Reference Usage2627Consult the bundled references whenever the query requires details beyond the core patterns in this file:2829- Use `references/sparql-1.1-query-features.md` when deciding which SPARQL construct to use or how to structure it correctly. Typical cases: `OPTIONAL` vs `UNION`, `FILTER NOT EXISTS` vs `MINUS`, property-path syntax, `VALUES`, `BIND`, aggregates with `GROUP BY`/`HAVING`, subqueries, `ORDER BY` with `LIMIT`, and variable-scope questions.30- Use `references/wikidata-data-model.md` when deciding which Wikidata RDF layer to query. Typical cases: choosing `wdt:` vs `p:/ps:`, accessing qualifiers with `pq:`, references with `pr:`, ranks with `wikibase:rank`, normalized quantities via `psn:`/`wikibase:quantityAmount`, datatype-specific handling, and understanding truthy versus full statement semantics.31- Prefer this `SKILL.md` for task-specific generation rules and project-specific guardrails; use the reference documents to resolve syntax/modeling uncertainty, not to replace the workflow here.3233## Requirements3435Install skill dependencies from the workspace root with `uv sync`.3637The script uses `rdflib` for SPARQL parsing/validation (optional) and basic Python for query construction. No API keys required.3839## Environment Variables4041None required.4243## Safety Rules4445- Generate only read-only queries (SELECT, ASK, CONSTRUCT, DESCRIBE).46- Always include a LIMIT clause during iterative development (remove only for final verified queries if appropriate).47- Never generate DELETE, INSERT, or UPDATE operations.48- Use verified IDs only — never guess QIDs or PIDs.4950## Script Modes5152### 1. Validate SPARQL Syntax (`validate`)5354Check that a SPARQL query is syntactically valid:5556```bash57uv run python .agents/skills/sparql-generation/scripts/sparql_generator.py \58 --mode validate \59 --sparql "SELECT ?x WHERE { ?x wdt:P31 wd:Q5 . } LIMIT 10"60```6162### 2. Generate from Template (`generate`)6364Generate a SPARQL query from structured inputs:6566```bash67uv run python .agents/skills/sparql-generation/scripts/sparql_generator.py \68 --mode generate \69 --question "What is the capital of France?" \70 --entities '{"France": "Q142"}' \71 --properties '{"capital": "P36"}' \72 --pattern "direct-lookup"73```7475### 3. Apply Pattern (`pattern`)7677Apply a named query pattern with entity/property substitution:7879```bash80uv run python .agents/skills/sparql-generation/scripts/sparql_generator.py \81 --mode pattern \82 --pattern "type-filter" \83 --entities '{"type": "Q6256", "constraint_property": "P30", "constraint_value": "Q18"}' \84 --properties '{"output_property": "P36"}'85```8687## Script Usage8889### Arguments9091Required:92- `--mode` / `-m`: Operation mode (`validate`, `generate`, `pattern`)9394Mode-specific:95- `--sparql`: SPARQL query string (required for `validate`)96- `--question`: Natural-language question (for `generate` mode context)97- `--entities`: JSON object mapping entity names to QIDs98- `--properties`: JSON object mapping property names to PIDs99- `--paths`: JSON array of verified graph paths from exploration100- `--pattern`: Named pattern to use (`direct-lookup`, `reverse-lookup`, `type-filter`, `aggregation`, `qualifier`, `subclass`, `date-filter`, `top-k`)101102Optional:103- `--include-labels`: Include the Wikidata label service (default: true)104- `--limit`: Add a LIMIT clause (default: none for final, 20 for exploration)105- `--output-file`: Write result JSON to a file instead of stdout106107## Return Shape108109### Validation Result110111```json112{113 "success": true,114 "mode": "validate",115 "sparql": "SELECT ?x WHERE { ?x wdt:P31 wd:Q5 . } LIMIT 10",116 "valid": true,117 "errors": [],118 "warnings": ["No label service included — results will show URIs instead of labels"],119 "error": null120}121```122123### Generation Result124125```json126{127 "success": true,128 "mode": "generate",129 "question": "What is the capital of France?",130 "sparql": "SELECT ?capital ?capitalLabel WHERE {\n wd:Q142 wdt:P36 ?capital .\n OPTIONAL { ?capital rdfs:label ?capitalLabel FILTER(LANG(?capitalLabel) = \"en\") }\n}",131 "assumptions": [132 "France = Q142 (country)",133 "capital = P36 (capital property)",134 "Direct property lookup (wdt:) — no qualifiers needed"135 ],136 "pattern_used": "direct-lookup",137 "error": null138}139```140141## Query Patterns Reference142143### 1. Direct Property Lookup144145```sparql146# "What is the X of Y?"147SELECT ?value ?valueLabel WHERE {148 wd:Q_ENTITY wdt:P_PROPERTY ?value .149 OPTIONAL { ?value rdfs:label ?valueLabel FILTER(LANG(?valueLabel) = "en") }150}151```152153### 2. Reverse Lookup154155```sparql156# "What entities have property X pointing to Y?"157SELECT ?entity ?entityLabel WHERE {158 ?entity wdt:P_PROPERTY wd:Q_TARGET .159 OPTIONAL { ?entity rdfs:label ?entityLabel FILTER(LANG(?entityLabel) = "en") }160}161```162163### 3. Type Filter164165```sparql166# "Which entities of type T satisfy condition C?"167SELECT ?entity ?entityLabel WHERE {168 ?entity wdt:P31 wd:Q_TYPE ;169 wdt:P_FILTER ?filterValue .170 FILTER(?filterValue > threshold)171 OPTIONAL { ?entity rdfs:label ?entityLabel FILTER(LANG(?entityLabel) = "en") }172}173```174175### 4. Subclass Traversal176177```sparql178# "All entities that are instances of T or any subclass of T"179SELECT ?entity ?entityLabel WHERE {180 ?entity wdt:P31/wdt:P279* wd:Q_TYPE .181 OPTIONAL { ?entity rdfs:label ?entityLabel FILTER(LANG(?entityLabel) = "en") }182}183```184185### 5. Aggregation (COUNT, AVG, etc.)186187```sparql188# "How many entities of type T?"189SELECT (COUNT(?entity) AS ?count) WHERE {190 ?entity wdt:P31 wd:Q_TYPE .191}192```193194### 6. Top-K / Ordering195196```sparql197# "Top N entities by property value"198SELECT ?entity ?entityLabel ?value WHERE {199 ?entity wdt:P31 wd:Q_TYPE ;200 wdt:P_MEASURE ?value .201 OPTIONAL { ?entity rdfs:label ?entityLabel FILTER(LANG(?entityLabel) = "en") }202}203ORDER BY DESC(?value)204LIMIT N205```206207### 7. Date Filters208209```sparql210# "Entities where date property is after/before a date"211SELECT ?entity ?entityLabel ?date WHERE {212 ?entity wdt:P31 wd:Q_TYPE ;213 wdt:P_DATE ?date .214 FILTER(?date >= "2000-01-01T00:00:00Z"^^xsd:dateTime)215 OPTIONAL { ?entity rdfs:label ?entityLabel FILTER(LANG(?entityLabel) = "en") }216}217```218219**Important**: Always use the full ISO 8601 format with time component (`T00:00:00Z`) for `xsd:dateTime` comparisons. Bare date strings (e.g., `"2000-01-01"^^xsd:dateTime`) fail on some endpoints.220221### 8. Qualifier Access222223```sparql224# "What is the value of property P with qualifier Q?"225SELECT ?value ?valueLabel ?qualifier WHERE {226 wd:Q_ENTITY p:P_PROPERTY ?stmt .227 ?stmt ps:P_PROPERTY ?value ;228 pq:P_QUALIFIER ?qualifier .229 OPTIONAL { ?value rdfs:label ?valueLabel FILTER(LANG(?valueLabel) = "en") }230}231```232233### 9. OPTIONAL for Non-Required Fields234235```sparql236# "List entities with property X, include Y if available"237SELECT ?entity ?entityLabel ?x ?y WHERE {238 ?entity wdt:P31 wd:Q_TYPE ;239 wdt:P_X ?x .240 OPTIONAL { ?entity wdt:P_Y ?y . }241 OPTIONAL { ?entity rdfs:label ?entityLabel FILTER(LANG(?entityLabel) = "en") }242}243```244245### 10. VALUES for Known Entity Sets246247```sparql248# "Information about specific entities"249SELECT ?entity ?entityLabel ?value WHERE {250 VALUES ?entity { wd:Q1 wd:Q2 wd:Q3 }251 ?entity wdt:P_PROPERTY ?value .252 OPTIONAL { ?entity rdfs:label ?entityLabel FILTER(LANG(?entityLabel) = "en") }253}254```255256## Generation Rules257258Before finalizing a query, cross-check any non-trivial syntax against `references/sparql-1.1-query-features.md` and any statement-level or datatype-specific Wikidata modeling against `references/wikidata-data-model.md`.2592601. **Use only verified IDs**: Never guess QIDs or PIDs. All IDs must come from wikidata-search or graph-exploration.2612. **Correct triple pattern direction**: Verified in graph exploration. `wd:Q wdt:P ?obj` vs `?subj wdt:P wd:Q`.2623. **Include labels using rdfs:label**: Use `OPTIONAL { ?x rdfs:label ?xLabel FILTER(LANG(?xLabel) = "en") }` for human-readable labels. NOTE: `SERVICE wikibase:label` is NOT available on the custom endpoint — it causes HTTP 500 errors. Always use `rdfs:label` OPTIONAL pattern instead. Omit labels entirely for count/aggregate-only queries.2634. **Use VALUES for known entities from the question**: When the question explicitly names a small set of entities (e.g., "information about France, Germany, and Italy"), use `VALUES ?entity { wd:Q1 wd:Q2 wd:Q3 }`. However, NEVER use VALUES to inject entities discovered from world knowledge that the endpoint didn't return — if a declarative query returns fewer results than expected, those ARE the results.2645. **Use OPTIONAL sparingly**: Only for genuinely optional fields that may not exist on all entities.2656. **Avoid Cartesian products**: Ensure all triple patterns share variables or are properly constrained.2667. **Use DISTINCT when needed**: Especially after JOINs that could produce duplicates.2678. **Bound exploration queries**: Always include LIMIT during iterative development.2689. **Use p:/ps:/pq: for qualifiers**: Switch from wdt: when qualifier access is needed.26910. **Filter placement**: Place FILTERs close to the triple patterns they constrain.27011. **Use normalized values for quantities**: When retrieving numeric quantity values (distance, mass, area, duration, speed, etc.), ALWAYS use the normalized value path `p:P.../psn:P.../wikibase:quantityAmount` instead of `wdt:P...`. This returns SI-normalized values (meters, kg, m², seconds) which are consistent regardless of how the data was entered. See "Quantity Property Patterns" below.27112. **Use full statement model for temporal/historical data**: When the question asks about historical extremes ("highest ever", "all-time record") or needs to count ALL statements including past ones (e.g., "how many spouses total"), use `p:P.../ps:P...` to access all statements. The `wdt:` prefix only returns the single "truthy" (current/best-ranked) value and misses historical data. However, for "current state" questions about **multi-valued properties** (like capitals, members, affiliations), prefer `wdt:` truthy paths which already reflect Wikidata's rank-based currentness semantics. Only switch to statement-level access when you need to explicitly inspect ALL statements (including deprecated/historical ones) or when the truthy path demonstrably misses data that should be there.27213. **Return entity IDs for item-valued properties**: When a Wikidata property has datatype `wikibase-item` (e.g., P735 "given name", P3150 "birthday", P1441 "present in work"), the query must return the entity QID, not a label string. Check the property datatype during graph exploration.27314. **Use transitive P131+ for location containment**: When filtering by geographic location, use `wdt:P131+ wd:Q_location` (transitive) instead of direct `wdt:P131 wd:Q_location`. Items may be in sub-administrative units (suburbs, districts) that are contained within the target location.27415. **Compose property paths for country-level location queries**: When the question asks about items "in country X" but the linking property points to a specific location (city, region), compose the path to reach the country level. Common patterns:275 - Filming location to country: `wdt:P915/wdt:P131*/wdt:P17?`276 - Birthplace to country: `wdt:P19/wdt:P131*/wdt:P17?`277 - General location to continent: `wdt:P276?/wdt:P131*/wdt:P17?/wdt:P30`278 - Instance location to country: `wdt:P131*/wdt:P17`279 - Organization country (multiple paths): `(wdt:P17|wdt:P159/wdt:P17|wdt:P495)` — organizations may have country via direct P17, headquarters location (P159→P17), or country of origin (P495)280 281 The `?` on P17 handles cases where the item already IS a country. Always verify via exploration which intermediate steps are needed.28216. **Only include constraints stated in the question**: Do not add plausibility filters, unit restrictions, or scope constraints that are not explicitly mentioned or directly required by the question. For example:283 - Do not add `FILTER(?height < 3)` or restrict planets to the solar system unless the question says so284 - Do not add `MINUS { ?x wdt:P576 ?dissolved }` (exclude dissolved entities) unless the question says "currently existing"285 - Do not add `FILTER(?x != wd:Q_ENTITY)` self-exclusion unless the question logically requires it (e.g., "not bordering Germany" does NOT exclude Germany itself — Germany doesn't border itself)286 - Do not add extra type constraints that the question didn't mention28717. **Filter by unit for monetary comparisons**: When comparing monetary values (box office, GDP, budget), always filter for a common currency unit using `wikibase:quantityUnit` to ensure like-for-like comparison. Use `psv:` to access the value node with its unit.28818. **Exclude ended relationships for current-state questions**: For questions about current status ("Does X have a wife?", "Who is the current PM?"), add `MINUS { ?stmt pq:P582 ?endDate }` to exclude relationships that have ended.28919. **Sum related sub-properties for total measurements**: When a question asks for a total measurement (e.g., "how many floors", "total length", "overall height") and the entity has complementary sub-properties (e.g., P1101 "floors above ground" + P1139 "floors below ground"), sum them to get the total. Use `BIND(?a + ?b AS ?total)` or arithmetic in SELECT.29020. **Prefer returning entities over counts**: When a question asks "how many X?" or "how many X satisfy Y?", the expected output is almost always the **list of matching entity QIDs**, NOT a numeric count. Use `SELECT ?x WHERE { ... }` to return the entities themselves. Only use `SELECT (COUNT(...) AS ?count)` when the expected answer is clearly a large number (>50) and entities themselves aren't meaningful to list. **Important**: "How many movies has X directed?" → return the list of movie QIDs. "How many countries are there?" → return the list of country QIDs.29121. **SELECT only the answer column**: When the query uses ORDER BY on a value column (e.g., population, date), only SELECT the entity variable, not the ordering column. Including extra columns in SELECT may leak numeric/date values into the final answer. For example, `SELECT ?country WHERE { ... ORDER BY DESC(?pop) }` is correct; `SELECT ?country ?pop WHERE { ... }` would return the population value alongside the entity.29222. **Match the question's scope exactly**: Use exactly the relationships stated in the question. If the question says "Latin American country", map to the specific Wikidata relationship for "Latin America" (e.g., `P361 Q12585`). Do not broaden to "South American continent" or similar approximations. Stick to the most direct relationship matching the question's phrasing.29323. **Consider subclass traversal on property values (conditionally)**: When matching property values against a general class (e.g., architectural style = brutalism, food = pizza, event = theft), the actual value on the entity may be a more specific subclass. Use `wdt:P_PROP/wdt:P279*` to also match subclasses of the target value. This is relevant for properties like:294 - Architectural style (P149): `wdt:P149/wdt:P279* wd:Q_STYLE`295 - Significant event (P793): `ps:P793/wdt:P279* wd:Q_EVENT_TYPE`296 - Last meal (P3902): `wdt:P3902/wdt:P279* wd:Q_FOOD`297 - Taxon parent (P171): use `wdt:P171+` for transitive traversal298 299 **Guardrails — do NOT apply subclass traversal when:**300 - The question is a boolean/ASK query checking for an exact value (e.g., "is there a flag with pink?" → use exact `wdt:P462 wd:Q429220`, not `P279*`)301 - The property is `P106` (occupation) — Wikidata editors assign specific occupations directly; use direct matching first302 - The initial direct query already returns a reasonable number of results (>5 for a list question)303 - The property is `P1412` (languages spoken), `P27` (citizenship), or other simple-valued properties where subclass semantics don't apply30424. **Query through instances, not class items**: When a question asks about members of a class ("paintings of X", "countries where Y are found", "collections with Z"), query through individual instances using `?x wdt:P31/wdt:P279* wd:Q_CLASS`, then access properties on those instances. Do NOT rely on properties of the class item itself (e.g., do not query `wd:Q_CLASS wdt:P17 ?country`), as class-level data is typically incomplete.30525. **Commit to one property direction**: Once exploration verifies the correct property direction, use only that direction. Do NOT hedge by adding a UNION with the inverse property. Example: if P1376 (capital of) is the verified direction, do not UNION with `?x wdt:P36 wd:Q_CITY`. Forward and inverse properties often have different coverage and adding both produces false positives.306307### Rules for Mention-Provided Entities308309- **Rule: Verify class suitability** — Before using a mention entity in `wdt:P31 wd:Q_entity`, run a quick count. If it returns 0 results, the entity may be a concept/movement rather than a type. Search for the correct classifying entity.310311- **Rule: Check for combined/intermediate classes** — When the question combines two concepts (e.g., "NP-complete video games"), prefer using a single intermediate class (`wdt:P31 wd:Q_combined_class`) over intersecting two separate type constraints. Intermediate classes are more complete in Wikidata.312313- **Rule: Don't assume mention properties are the linking property** — When a mention has a `property` field, it may indicate the property's role in the question semantics, not necessarily the exact triple pattern to use. Always verify through graph exploration.314315## Common Mistakes to Avoid316317| Mistake | Fix |318|---------|-----|319| Using `wdt:P31 wd:Q5` for subclasses | Use `wdt:P31/wdt:P279* wd:Q5` for subclass inclusion |320| Using `wdt:P106/wdt:P279*` for occupations | Prefer direct `wdt:P106 wd:Q_OCCUPATION` first — subclass traversal on occupation over-generates. Only add `/wdt:P279*` if the direct query returns < 3 results for a broad category |321| Missing label service | Use `OPTIONAL { ?x rdfs:label ?xLabel FILTER(LANG(?xLabel) = "en") }` (not SERVICE wikibase:label which is unavailable) |322| Wrong property direction | Verify with graph exploration before generating |323| Unbounded `P279*` traversal | Add LIMIT or restrict depth |324| Filtering on labels instead of IDs | Use `wd:QID` not `FILTER(CONTAINS(?label, "..."))` |325| Missing DISTINCT with multiple optional patterns | Add DISTINCT to SELECT |326| Using `FILTER(?x = wd:Q...)` instead of direct triple | Use `wd:Q... wdt:P ?y` directly |327| Qualifying objects in SELECT without GROUP BY | Use aggregate functions or remove from SELECT |328| Using `wdt:` for quantity values | Use `p:P.../psn:P.../wikibase:quantityAmount` for normalized SI values |329| Using `wdt:` for historical maximums | Use `p:P.../ps:P...` to access all historical statements |330| Returning label strings for item-valued properties | Return the entity QID (e.g., `?givenName` as QID, not its label) |331| Using direct `wdt:P131` for location queries | Use `wdt:P131+` (transitive) to include sub-divisions |332| Adding plausibility filters not in the question | Do not add `FILTER(?x < 3)` or restrict to solar system unless asked |333| Comparing monetary values without unit filter | Use `psv:` + `wikibase:quantityUnit` to filter for same currency |334| Using `wdt:P26` for "currently married" | Use `p:P26` + `MINUS { ?stmt pq:P582 ?end }` to exclude ex-spouses |335| Using only P1101 for "total floors" | Sum P1101 (above ground) + P1139 (below ground) for total |336| Returning COUNT when entities are expected | Return entity list; only use COUNT for explicitly numeric questions with large result sets |337| Returning entity list when COUNT is expected | For "How many X?" questions, use `SELECT (COUNT(DISTINCT ?x) AS ?count)`. But first check if the parent entity has a direct quantity property (e.g., P1114, P1538, P1539) that stores the count — if it exists, use it directly as it's more authoritative than counting instances. |338| Using `wdt:` for "current" position when qualifier is needed | For "Who IS the current X?" (present tense), `wdt:` truthy value reflects the best-ranked statement which is usually current. But for positions with multiple holders over time (presidents, chairpersons), verify by checking: if `wdt:P_position` returns multiple values, switch to `p:P39/ps:P39` with `MINUS { ?stmt pq:P582 ?end }` to get only active (non-ended) holders. |339| Broadening scope beyond what the question states | "Latin American" = P361 Q12585; don't expand to "South American continent" |340| Using `wdt:P921/wdt:P279*` (subclass traversal on topic) | This massively over-generates because it returns items whose topic is ANY subclass of the target concept. Prefer direct `wdt:P921 wd:Q_TOPIC` unless there is explicit evidence that topic subclass traversal is needed. Only use `P279*` on the **entity type** side, not the topic/subject side. |341| Hedging with UNION of forward + inverse property | Choose ONE direction after verification. `P1376` and `P36` are NOT interchangeable — one may cover more entities. |342| Using `wdt:P_PROP wd:Q_VALUE` when values may be subclasses | Consider `wdt:P_PROP/wdt:P279* wd:Q_VALUE` for style/event/food properties — but NOT for occupation, language, citizenship, or boolean/ASK queries (see Rule 23 guardrails) |343| Querying properties on the class item directly | Query through instances: `?x wdt:P31/wdt:P279* wd:Q_CLASS . ?x wdt:P_PROP ?value` — not `wd:Q_CLASS wdt:P_PROP ?value` |344| Replacing a declarative query with hardcoded VALUES | Never inject world knowledge as VALUES. If the endpoint returns few results, that IS the answer. |345| Using only one location property for country-level queries | Compose paths: `P915/P131*/P17?` for filming locations, `P19/P131*/P17?` for birthplaces |346| Using `P179` (part of series) for "what parts does X have?" | Use outgoing `wd:X wdt:P527 ?part` (has part) instead — P179 incoming may include unauthorized additions |347| Using only P176 for "made by" questions | Include `P176\|P127` (manufacturer OR owned by) for broader coverage |348| Using COUNT when entities are expected | Return `SELECT ?x WHERE {...}` (entity list), not `SELECT (COUNT(?x) AS ?c)` — evaluation expects QIDs |349| Reading P1114 (quantity) for "how many instances?" | Count instances: `SELECT (COUNT(?x)) WHERE { ?x wdt:P31 wd:Q_CLASS }` — P1114 on the class is often wrong/outdated |350| Using P1532 for team→country mapping | Prefer `wdt:P17` which has broader coverage on sports team entities |351| Returning ALL predecessors for "before X" when person held office multiple times | When X held a position multiple non-consecutive times (e.g., Trump as president twice), "before X" means predecessor of the FIRST term. Use `ORDER BY ?startDate LIMIT 1` on the P39 statements to get only the earliest term, then extract P1365 (replaces) from that statement. |352| Using P31 with a mentioned entity for "brands of X" | Mentions may provide a product class (Q431289 "smartphone model") but brands are not typed as product instances. Instead search for companies (`P31 Q4830453/Q783794`) that manufacture (`P176^` or `P1056`) the product type, or check if Wikidata has a "brand" class for the domain. |353| Using MINUS on all properties for "solely as X" | `MINUS { ?item ?anyProp wd:Q_person . FILTER(?anyProp != wdt:P_role) }` is too aggressive — it removes items where the person appears in metadata properties (P1040 editor, P162 producer). For "solely as director", exclude only competing CREDIT roles: `MINUS { ?film wdt:P58 wd:Q_person }` (screenwriter) and `MINUS { ?film wdt:P162 wd:Q_person }` (producer). Or count roles: `FILTER NOT EXISTS { ?film ?creditRole wd:Q_person . FILTER(?creditRole IN (wdt:P58, wdt:P162, wdt:P1431)) }` |354355## Quantity Property Patterns356357When a question asks for a numeric measurement (distance, mass, area, duration, speed, height, etc.), **always use the normalized value path**. This ensures consistent SI units regardless of how the data was originally entered.358359### Pattern: Retrieve a normalized quantity value360361```sparql362# "How far is X from Earth?" / "What is the area of X?" / "How heavy is X?"363SELECT ?value WHERE {364 wd:Q_ENTITY p:P_QUANTITY/psn:P_QUANTITY/wikibase:quantityAmount ?value .365}366```367368Examples:369- Distance from Earth (P2583): `p:P2583/psn:P2583/wikibase:quantityAmount` → meters370- Duration (P2047): `p:P2047/psn:P2047/wikibase:quantityAmount` → seconds371- Mass (P2067): `p:P2067/psn:P2067/wikibase:quantityAmount` → kilograms372- Area (P2046): `p:P2046/psn:P2046/wikibase:quantityAmount` → square meters373- Height (P2048): `p:P2048/psn:P2048/wikibase:quantityAmount` → meters374- Speed (P2052): `p:P2052/psn:P2052/wikibase:quantityAmount` → meters per second375- Wheelbase (P3039): `p:P3039/psn:P3039/wikibase:quantityAmount` → meters376- Course length (P3157): `p:P3157/psn:P3157/wikibase:quantityAmount` → meters377378### Pattern: Top-K by quantity with normalization379380```sparql381# "What is the tallest/fastest/heaviest X?"382SELECT ?entity WHERE {383 ?entity wdt:P31/wdt:P279* wd:Q_TYPE .384 ?entity p:P_QUANTITY/psn:P_QUANTITY/wikibase:quantityAmount ?value .385}386ORDER BY DESC(?value)387LIMIT 1388```389390### Pattern: Monetary comparison with unit filter391392```sparql393# "Which X has the highest box office (in USD)?"394SELECT ?entity WHERE {395 ?entity wdt:P31/wdt:P279* wd:Q_TYPE .396 ?entity p:P2142 ?stmt .397 ?stmt psv:P2142 ?valueNode .398 ?valueNode wikibase:quantityAmount ?amount ;399 wikibase:quantityUnit wd:Q4917 . # Q4917 = US dollar400}401ORDER BY DESC(?amount)402LIMIT 1403```404405## Temporal and Historical Patterns406407### Pattern: Historical maximum (all-time best)408409```sparql410# "What is the highest Elo rating ever?" — need ALL statements, not just current411SELECT (MAX(?value) AS ?max) WHERE {412 ?entity p:P1087/ps:P1087 ?value .413}414```415416### Pattern: Current position holder (most recent start date)417418```sparql419# "Who is the current PM of X?" — use position held with temporal ordering420SELECT ?person WHERE {421 ?person p:P39 ?stmt .422 ?stmt ps:P39 wd:Q_POSITION .423 ?stmt pq:P580 ?startDate .424}425ORDER BY DESC(?startDate)426LIMIT 1427```428429### Pattern: Current relationship (exclude ended)430431```sparql432# "Does X currently have a spouse?"433ASK WHERE {434 wd:Q_PERSON p:P26 ?stmt .435 ?stmt ps:P26 ?spouse .436 MINUS { ?stmt pq:P582 ?endDate }437}438```439440### Pattern: Summing complementary sub-properties441442```sparql443# "How many floors does building X have?" (total = above + below ground)444SELECT (?above + ?below AS ?totalFloors) WHERE {445 wd:Q_BUILDING wdt:P1101 ?above ;446 wdt:P1139 ?below .447}448```449450Other examples of complementary properties that may need summing:451- P1101 (floors above ground) + P1139 (floors below ground) = total floors452- Multiple distance/length components when asking for "total"453454### Pattern: Ordinal qualifier access455456```sparql457# "What is the Nth item in sequence X?"458SELECT ?value WHERE {459 wd:Q_ENTITY p:P_PROPERTY ?stmt .460 ?stmt pq:P1545 "N" . # series ordinal461 ?stmt ps:P_PROPERTY ?value .462}463```464465## Integration with text2sparql Pipeline466467### Input from Previous Steps468469The generation step receives:470- **From wikidata-search**: Resolved QIDs and PIDs with labels471- **From graph-exploration**: Verified paths, directions, qualifier structures472473### Output to Next Step474475The generated SPARQL is passed to sparql-execution for execution and validation.476477## Troubleshooting478479- **Syntax errors from validator**: Check bracket matching, semicolons between triple patterns, and proper string escaping.480- **Unsure which SPARQL feature to use**: Check `references/sparql-1.1-query-features.md` before improvising syntax for negation, aggregation, property paths, subqueries, or inline bindings.481- **Unsure which Wikidata prefix/model to use**: Check `references/wikidata-data-model.md` before choosing between `wdt:`, `p:/ps:`, `pq:`, `psv:`, `psn:`, or rank/reference access.482- **Wrong results direction**: Swap subject/object positions and re-run.483- **No results with subclass traversal**: Try without `P279*` first to isolate the issue.484- **Label service not working**: Do NOT use `SERVICE wikibase:label` — it causes HTTP 500 errors on the custom endpoint. Use `OPTIONAL { ?x rdfs:label ?xLabel FILTER(LANG(?xLabel) = "en") }` instead.485- **Timeout during generation**: The generation script itself should be fast; if it's slow, it's likely a validation step querying the endpoint.