Italian Parliament graph: why the Chamber and Senate don’t speak the same language

6/17/20266 min read
Italian Parliament graph: why the Chamber and Senate don’t speak the same language

Direct answer: the Italian Chamber of Deputies and the Senate of the Republic publish their data as Linked Open Data, but they use two different, only partially aligned ontologies — OCD for the Chamber, OSR for the Senate. The same concept (a member’s surname, the legislature, the start of a mandate, a legislative act) is modeled with different properties, formats and even namespaces. To build a single graph that an AI agent can query reliably, you can’t just «merge the data»: you need two separate query builders and a normalization layer. Here we walk through the concrete problems we hit while mapping the relations, with code examples.

TL;DR

  • Surname: the Chamber uses foaf:surname, the Senate foaf:lastName. Same data, different property.
  • Legislature: the Chamber expresses it as a full URI, the Senate as a plain integer. A query that works on one endpoint fails on the other.
  • Mandate dates: ocd:startDate/ocd:endDate (Chamber) vs osr:inizio/osr:fine (Senate).
  • Parliamentary groups: the Senate reuses the Chamber’s classes (ocd:gruppoParlamentare) but with OSR properties. A confusing hybrid.
  • Acts: ocd:atto (Chamber) vs osr:Ddl (Senate) — different classes and namespaces.
  • Documentation: the Senate ontology has no complete official documentation: we reconstructed it by reverse-engineering the example queries.
  • Bottom line: the two ontologies are coordinated but not identical. You need separate query builders per institution.

Why it matters: a graph is only as good as its relations

We are building Open·Parlamento, an agent that answers questions on law and public data while always citing the source. Its core is a knowledge graph: nodes (members, acts, groups, votes) and typed relations between them (presents, belongs to, votes, amends). An AI agent can give reliable answers only if those relations are clean and consistent.

The problem is that, before anything reaches the graph, the data comes from two SPARQL sources that model reality differently. It all started with RepublicMCP, the open-source connector that exposes the Chamber and Senate as agentic tools: mapping the two ontologies is exactly where these incompatibilities surfaced.

What’s the difference between OCD and OSR?

OCD (Chamber of Deputies ontology) and OSR (Senate of the Republic ontology) are the two RDF vocabularies the two houses use to publish their data. They share a general approach (FOAF for people, classes for mandates, groups, acts) but diverge in the details that matter when you write queries.

Concept Chamber (OCD) Senate (OSR) Compatible?
Surname foaf:surname foaf:lastName
Legislature URI (…/repubblica_19) integer (19)
Mandate start ocd:startDate osr:inizio
Mandate end ocd:endDate osr:fine
Mandate relation ocd:rif_mandatoCamera osr:mandato
Legislative act ocd:atto osr:Ddl
Parliamentary group ocd:gruppoParlamentare ocd:gruppoParlamentare (reused) ⚠️ hybrid

The trickiest case: the legislature

It looks like a detail, but it breaks queries. The Chamber wants the full URI of the legislature; the Senate a plain integer.

# Chamber — the full URI is required
?mandato ocd:rif_leg <http://dati.camera.it/ocd/legislatura.rdf/repubblica_19> .

# Senate — the number is enough
?mandato osr:legislatura 19 .

On the Chamber side the short form does not work: ?mandato ocd:rif_leg 19 returns nothing. It’s the kind of silent incompatibility that raises no errors — it simply gives you zero results, and it’s hard to diagnose.

Dates: same meaning, different property and format

Beyond the different names (startDate/endDate vs inizio/fine), the Chamber encodes dates as integers in YYYYMMDD format:

# Chamber: compare as integer, NOT as string
FILTER(xsd:integer(?date) >= 20240101)

And to find members currently in office you need two opposite SPARQL patterns:

# Chamber: exclude those with an end date
MINUS { ?mandato ocd:endDate ?end }

# Senate: filter where the end is unbound
OPTIONAL { ?mandato osr:fine ?end }
FILTER(!bound(?end))

The parliamentary-groups hybrid

The most surprising detail: for parliamentary groups the Senate reuses the Chamber’s classes (ocd:gruppoParlamentare, ocd:adesioneGruppo) — but populates them with OSR properties (osr:gruppo, osr:inizio, osr:fine).

PREFIX osr: <http://dati.senato.it/osr/>
PREFIX ocd: <http://dati.camera.it/ocd/>

?senator ocd:aderisce ?membership .       # Chamber class
?membership osr:gruppo ?group ;            # Senate property!
            osr:inizio ?start .            # Senate property!
?group a ocd:gruppoParlamentare .          # Chamber class again

The same goes for mandates: the classes ocd:mandatoCamera and ocd:mandatoSenato both live in the Chamber’s namespace, even the Senate’s one. Without an explicit map of these exceptions, any attempt to write a «generic» query fails.

What about documentation?

For the Chamber there’s reasonably complete ontology documentation. For the Senate there isn’t: OSR has no comparable explicit official documentation. We had to reconstruct the model by reverse engineering, starting from the official example queries and empirically verifying which properties exist. Some entities (speeches, documents) remain only partially documented.

How to solve it: normalize, don’t «merge»

The lesson is clear: the two ontologies are coordinated but not identical, so you can’t handle them with the same code. The strategy that works:

  1. Separate query builders per institution. Each house gets its own module that knows its property names, formats and patterns.
  2. Centralized configuration of the differences. Surname, legislature, dates and «in office» operators live in a per-institution map, not scattered across the code.
  3. A normalization layer into the graph. Before entering the knowledge graph, the data is mapped to a single model with typed relations and provenance — so the AI agent queries one coherent graph, not two SPARQL dialects.

That’s exactly what Open·Parlamento does: turning sources that, on their own, can’t be queried reliably into a single graph — always citing the original.

FAQ

What is an ontology in public data?

It’s a formal vocabulary (in RDF) that defines a domain’s entities and the relations between them: for Parliament, classes like «member», «mandate», «group», «act» and properties like «belongs to» or «presents». It lets data be published as SPARQL-queryable graphs instead of plain tables.

Why don’t the Chamber and Senate share a single ontology?

They are autonomous institutions that digitized their data at different times and in different ways. The result is two ontologies (OCD and OSR) that draw on common standards (FOAF) and partly reuse each other, yet diverge in the practical details of properties, formats and namespaces.

Can they still be merged into a single graph?

Yes, but it takes a normalization layer: separate query builders to read each source and a mapping to a single model before writing into the knowledge graph. That’s what we built for Open·Parlamento.


We build AI agents that answer on real data — citing the source, not inventing it. See how Open·Parlamento works and the open-source projects, or let’s talk about your knowledge graph.

Knowledge GraphOpen DataAISPARQL

Scritto da Giulio Garofalo