Skip to content

Database Schema

The Observatorio del Congreso uses a Popolo-Graph unified schema stored in SQLite. It contains 12 tables organized into four functional groups:

GroupTablesPurpose
Core entitiesperson, organization, areaLegislators, parties, geographic divisions
Legislative activitymotion, vote_event, vote, countBills, voting sessions, individual votes, tallies
Structural relationsmembership, postParty affiliations, committee seats
Power networksrelacion_poder, actor_externo, evento_politicoLoyalty ties, external actors, political events

All tables use text-based primary keys with a prefix convention (e.g. P00001, VE_D00001). Foreign keys enforce referential integrity with ON DELETE CASCADE where appropriate. CHECK constraints validate enums and ranges at the database level.

+-------------+
| area |
+-------------+
| id (PK) |
| nombre |
| parent_id FK|----+
+-------------+ |
^ |
| |
+---------------+ +------------------+ | +------------------+
| organization | | post | | | actor_externo |
+---------------+ +------------------+ | +------------------+
| id (PK) |<---| org_id (FK) | | | id (PK) |
| nombre | | area_id (FK) |--+ | nombre |
| abbr | +------------------+ | tipo |
| clasificacion | | area_id (FK) |--+
+------+--------+ +------------------+ |
| +----------------+ |
| | membership | |
| +----------------+ |
| | id (PK) | |
+---org_id-->| person_id (FK) |-----+ |
| | rol | | |
| | on_behalf_of FK|-----+ |
| +----------------+ | |
| v |
| +-------------+ |
| | person | |
| +-------------+ |
| | id (PK) | |
| | nombre | |
| | genero | |
| +-------------+ |
| | |
| v |
| +----------------+ +-------------+ |
| | vote_event | | vote | |
| +----------------+ +-------------+ |
+--org_id| id (PK) |<--| vote_event | |
| motion_id (FK)| | voter_id FK | |
+-------+--------+ +-------------+ |
| |
v |
+-------------+ +------------------+ |
| motion | | count | |
+-------------+ +------------------+ |
| id (PK) | | id (PK) | |
| texto | | vote_event_id FK |----------+
| clasificacion | option | |
+-------------+ | group_id (FK) ---+----------+
+------------------+
+-------------------+ +-------------------+
| relacion_poder | | evento_politico |
+-------------------+ +-------------------+
| id (PK) | | id (PK) |
| source_type | | fecha |
| source_id | | tipo |
| target_type | | descripcion |
| target_id | | motion_id (FK) ---+--> motion
| tipo | +-------------------+
| peso (1-5) |
+-------------------+
source/target: person | organization | actor_externo

All primary keys use a text prefix to identify the entity type and, where applicable, the chamber of origin. Numeric portions are zero-padded to 5 digits.

EntityPrefixExamplePadding
Vote Event (Diputados)VE_DVE_D000015 digits
Vote Event (Senado)VE_SVE_S000015 digits
Vote (Diputados)V_DV_D000015 digits
Vote (Senado)V_SV_S000015 digits
Motion (Diputados)Y_DY_D000015 digits
Motion (Senado)Y_SY_S000015 digits
Membership (Diputados)M_DM_D000015 digits
Membership (Senado)M_SM_S000015 digits
PersonPP000015 digits
CountCC000015 digits

These are the core political organizations stored in the organization table:

IDAbbreviationNameClassification
O08DiputadosCamara de Diputadosgobierno
O09SenadoCamara de Senadoresgobierno
O11MORENAMovimiento de Regeneracion Nacionalpartido
O12PANPartido Accion Nacionalpartido
O13PRIPartido Revolucionario Institucionalpartido
O14PRDPartido de la Revolucion Democraticapartido
O15PTPartido del Trabajopartido
O16PVEMPartido Verde Ecologista de Mexicopartido
O17MCMovimiento Ciudadanopartido

Stores geographic divisions used for electoral districts and states. Areas can be nested via parent_id to model the hierarchy: district within state.

ColumnTypeNullableDescription
idTEXTNO (PK)Primary key, prefix A01, A02
nombreTEXTNOName of the area
clasificacionTEXTNOClassification: estado, distrito, circunscripcion
parent_idTEXTYESForeign key to area(id). District points to its parent state
geometryTEXTYESOptional GeoJSON geometry
clasificacion IN ('estado', 'distrito', 'circunscripcion')

Stores political parties, legislative chambers, coalitions, and government institutions.

ColumnTypeNullableDescription
idTEXTNO (PK)Primary key, prefix O01, O02
nombreTEXTNOOfficial name (UNIQUE)
abbrTEXTYESAbbreviation (MORENA, PAN, etc.)
clasificacionTEXTNOType: partido, bancada, coalicion, gobierno, institucion, otro
fundacionTEXTYESFounding date in ISO 8601
disolucionTEXTYESDissolution date in ISO 8601
clasificacion IN ('partido', 'bancada', 'coalicion', 'gobierno', 'institucion', 'otro')

Core table for legislators (deputies and senators). Includes demographic data, seat type, internal factions, and vulnerability assessments.

ColumnTypeNullableDescription
idTEXTNO (PK)Primary key, prefix P01, P02
nombreTEXTNOFull name
fecha_nacimientoTEXTYESBirth date in ISO 8601
generoTEXTYESGender: M, F, NB
curul_tipoTEXTYESSeat type: mayoria_relativa, plurinominal, suplente
circunscripcionINTEGERYESElectoral circumscription number (1-5)
start_dateTEXTYESStart of legislative term
end_dateTEXTYESEnd of legislative term
corriente_internaTEXTYESInternal faction: Monreal, AMLO, Sheinbaum, institucionalista, or NULL
vulnerabilidadTEXTYESPolitical vulnerability: alta, media, baja, or NULL
observacionesTEXTYESFree-text notes
identifiers_jsonTEXTYESJSON object with external IDs
genero IN ('M', 'F', 'NB')
curul_tipo IN ('mayoria_relativa', 'plurinominal', 'suplente')
circunscripcion BETWEEN 1 AND 5
corriente_interna IN ('Monreal', 'AMLO', 'Sheinbaum', 'institucionalista', NULL)
vulnerabilidad IN ('alta', 'media', 'baja', NULL)

membership — Person-Organization Relations

Section titled “membership — Person-Organization Relations”

Links persons to organizations with role, date range, and optional coalition representation.

ColumnTypeNullableDescription
idTEXTNO (PK)Primary key, prefix M01, M02
person_idTEXTNOForeign key to person(id), ON DELETE CASCADE
org_idTEXTNOForeign key to organization(id), ON DELETE CASCADE
rolTEXTNORole: diputado, senador, suplente
labelTEXTYESHuman-readable description of the membership
start_dateTEXTNOStart date of membership
end_dateTEXTYESEnd date. NULL means currently active
on_behalf_ofTEXTYESForeign key to organization(id). Coalition member voting under another party

Represents formal positions within organizations (e.g., committee chair, board secretary).

ColumnTypeNullableDescription
idTEXTNO (PK)Primary key, prefix T01, T02
org_idTEXTNOForeign key to organization(id)
area_idTEXTYESForeign key to area(id) for geographically bound posts
labelTEXTNOTitle of the position
start_dateTEXTNOStart date
end_dateTEXTYESEnd date. NULL means position is still held

Stores legislative proposals including constitutional reforms, secondary laws, and ordinary legislation.

ColumnTypeNullableDescription
idTEXTNO (PK)Primary key, prefix Y01, Y02
textoTEXTNOFull text or summary of the initiative
clasificacionTEXTNOType: reforma_constitucional, ley_secundaria, ordinaria, otra
requirementTEXTNOVote threshold required: mayoria_simple, mayoria_calificada, unanime
resultTEXTYESOutcome: aprobada, rechazada, pendiente, retirada, or NULL
dateTEXTYESDate of the initiative
legislative_sessionTEXTYESLegislative session label, e.g. “LXVI Legislatura”
fuente_urlTEXTYESSource URL for the initiative
clasificacion IN ('reforma_constitucional', 'ley_secundaria', 'ordinaria', 'otra')
requirement IN ('mayoria_simple', 'mayoria_calificada', 'unanime')
result IN ('aprobada', 'rechazada', 'pendiente', 'retirada', NULL)

Records voting sessions. Each vote event is tied to a motion and an organization (chamber).

ColumnTypeNullableDescription
idTEXTNO (PK)Primary key, prefix VE01, VE02
motion_idTEXTNOForeign key to motion(id)
start_dateTEXTNODate and time of the vote
organization_idTEXTNOForeign key to organization(id) (chamber)
resultTEXTYESOutcome: aprobada, rechazada, empate, or NULL
sitl_idINTEGERYESSITL (Sistema de Informacion Legislativa) voting ID
voter_countINTEGERYESNumber of voters recorded
legislaturaTEXTYESLegislature code: LX, LXI, …, LXVI
requirementTEXTYESVote threshold: mayoria_simple, mayoria_calificada, unanime, or NULL
source_idTEXTYESOriginal portal ID, used for deduplication
identifiers_jsonTEXTYESJSON with external identifiers
result IN ('aprobada', 'rechazada', 'empate', NULL)
requirement IN ('mayoria_simple', 'mayoria_calificada', 'unanime', NULL)

Records each individual legislator’s vote within a vote event.

ColumnTypeNullableDescription
idTEXTNO (PK)Primary key, prefix V01, V02
vote_event_idTEXTNOForeign key to vote_event(id)
voter_idTEXTNOForeign key to person(id)
optionTEXTNOVote cast: a_favor, en_contra, abstencion, ausente
groupTEXTYESParty affiliation at the time of the vote
option IN ('a_favor', 'en_contra', 'abstencion', 'ausente')

Aggregated vote tallies per option per group (party). Used for quick statistical queries without scanning individual vote rows.

ColumnTypeNullableDescription
idTEXTNO (PK)Primary key, prefix C01, C02
vote_event_idTEXTNOForeign key to vote_event(id)
optionTEXTNOVote option: a_favor, en_contra, abstencion, ausente
valueINTEGERNOCount of votes for this option (>= 0)
group_idTEXTYESForeign key to organization(id). The party being tallied
option IN ('a_favor', 'en_contra', 'abstencion', 'ausente')
value >= 0

Stores political actors outside the legislature who influence legislative outcomes: governors, mayors, former presidents, judges, party leaders.

ColumnTypeNullableDescription
idTEXTNO (PK)Primary key, prefix AE01, AE02
nombreTEXTNOFull name
tipoTEXTNOActor type: gobernador, alcalde, ex_presidente, dirigente, juez, otro
area_idTEXTYESForeign key to area(id). Geographic jurisdiction
start_dateTEXTYESStart of relevance period
end_dateTEXTYESEnd of relevance period
observacionesTEXTYESFree-text notes
tipo IN ('gobernador', 'alcalde', 'ex_presidente', 'dirigente', 'juez', 'otro')

Polymorphic relationship table modeling political connections between any combination of persons, organizations, and external actors. This is a Popolo extension specific to this project.

ColumnTypeNullableDescription
idTEXTNO (PK)Primary key, prefix RP01, RP02
source_typeTEXTNOSource entity type: person, organization, actor_externo
source_idTEXTNOID of the source entity
target_typeTEXTNOTarget entity type: person, organization, actor_externo
target_idTEXTNOID of the target entity
tipoTEXTNORelationship type: lealtad, presion, influencia, familiar, clientelismo, conflicto, alianza
pesoINTEGERNOWeight/strength of the relationship (1-5)
start_dateTEXTYESStart of the relationship
end_dateTEXTYESEnd of the relationship
fuenteTEXTYESSource of the relationship data
notaTEXTYESAdditional notes
source_type IN ('person', 'organization', 'actor_externo')
target_type IN ('person', 'organization', 'actor_externo')
tipo IN ('lealtad', 'presion', 'influencia', 'familiar', 'clientelismo', 'conflicto', 'alianza')
peso BETWEEN 1 AND 5

Records significant political events that may affect legislative behavior or outcomes.

ColumnTypeNullableDescription
idTEXTNO (PK)Primary key, prefix EP01, EP02
fechaTEXTNODate of the event
tipoTEXTNOType of event (free text)
descripcionTEXTNODescription of the event
consecuenciaTEXTYESExpected or observed consequences
fuente_urlTEXTYESSource URL
motion_idTEXTYESForeign key to motion(id). Related legislative initiative

The following indexes are defined to optimize common query patterns:

Index NameTableColumns
idx_membership_personmembershipperson_id
idx_membership_orgmembershiporg_id
idx_vote_event_motionvote_eventmotion_id
idx_vote_event_sourcevote_eventsource_id
idx_vote_votervotevoter_id
idx_vote_eventvotevote_event_id
idx_count_eventcountvote_event_id
idx_count_groupcountgroup_id
idx_relacion_sourcerelacion_podersource_type, source_id
idx_relacion_targetrelacion_podertarget_type, target_id
idx_relacion_tiporelacion_podertipo
idx_person_corrientepersoncorriente_interna
idx_actor_tipoactor_externotipo

Four triggers enforce date consistency between start_date and end_date fields:

Fires BEFORE INSERT on person. Ensures end_date is not earlier than start_date when both are present.

CREATE TRIGGER trg_person_dates
BEFORE INSERT ON person
BEGIN
SELECT RAISE(ABORT, 'end_date must be >= start_date')
WHERE NEW.end_date IS NOT NULL
AND NEW.start_date IS NOT NULL
AND NEW.end_date < NEW.start_date;
END;

Same validation on UPDATE of person.

CREATE TRIGGER trg_person_dates_update
BEFORE UPDATE ON person
BEGIN
SELECT RAISE(ABORT, 'end_date must be >= start_date')
WHERE NEW.end_date IS NOT NULL
AND NEW.start_date IS NOT NULL
AND NEW.end_date < NEW.start_date;
END;

Fires BEFORE INSERT on membership. Prevents memberships with inverted date ranges.

CREATE TRIGGER trg_membership_dates
BEFORE INSERT ON membership
BEGIN
SELECT RAISE(ABORT, 'end_date must be >= start_date')
WHERE NEW.end_date IS NOT NULL
AND NEW.end_date < NEW.start_date;
END;

Same validation on UPDATE of membership.

CREATE TRIGGER trg_membership_dates_update
BEFORE UPDATE ON membership
BEGIN
SELECT RAISE(ABORT, 'end_date must be >= start_date')
WHERE NEW.end_date IS NOT NULL
AND NEW.end_date < NEW.start_date;
END;

Current data volumes in the production database:

MetricValue
Individual votes~3,510,053
Vote events~8,123
Persons~3,849
Organizations~20
Legislatures covered7 (LX through LXVI)