Consulta y administración con SPARQL
Reúno aquí las dos caras de SPARQL 1.1 que toco en el TFM. La primera es la de consulta
sobre el grafo de conocimiento (EKG): patrones SELECT y CONSTRUCT, los
property paths y una consulta federada a Wikidata. La segunda es la de administración del
grafo con UPDATE (INSERT/DELETE), que sigo por indicación del director: el
mismo lenguaje que interroga el grafo también lo mantiene. Todas las cifras de salida que cito proceden de las
ejecuciones reales del entregable; las parafraseo, no invento ninguna.
1Consultas SELECT sobre el EKG
El EKG canónico (ekg-python-150.ttl) reúne 157 conceptos en
1772 triples afirmados, que el cierre OWL 2 RL eleva a 4786. Sobre ese grafo,
las consultas SELECT son la forma natural de interrogarlo. La consulta básica del entregable agrupa
los conceptos por tema y los devuelve en español, combinando GROUP BY, GROUP_CONCAT y
FILTER(LANG(…)).
PREFIX pyedu: <https://w3id.org/ekg-python/schema#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
# Conceptos agrupados por tema, en español (consulta 01).
SELECT ?tema (GROUP_CONCAT(?etiqueta; SEPARATOR=", ") AS ?conceptos)
WHERE {
?concepto pyedu:perteneceATema ?t .
?t rdfs:label ?tema .
?concepto rdfs:label ?etiqueta .
FILTER(LANG(?tema) = "es")
FILTER(LANG(?etiqueta) = "es")
}
GROUP BY ?tema
ORDER BY ?tema
# → 16 filas (un tema por fila, con sus conceptos).
Una segunda consulta SELECT está pensada para evidenciar la inferencia: recupera
todo lo que es pyedu:Concepto. Sin razonador devuelve 0 filas (ningún individuo se
tipa a mano como concepto), y con OWL 2 RL devuelve 157, porque rdfs:subClassOf
propaga el tipo. Ese contraste 0 → 157 es la prueba directa de que el razonador está actuando.
| Consulta | Qué demuestra | Salida real |
|---|---|---|
01_conceptos_por_tema.rq | SELECT + GROUP BY + GROUP_CONCAT + FILTER(LANG) | 16 temas con sus conceptos |
02_inferencia_conceptos.rq | SELECT como diagnóstico de la inferencia de tipos | 0 sin inferencia → 157 con OWL 2 RL |
05_errores_por_concepto.rq | OPTIONAL sobre reificación rdf:Statement | 16 errores (uno con fuente Keuning 2019) |
El salto procede de rdfs:subClassOf: toda subclase de concepto es pyedu:Concepto. Como
el enlazado a Wikidata usa skos:exactMatch y no owl:sameAs, las 30 entidades enlazadas
no se infieren como concepto; por eso el resultado es 157 y no 187.
2Property paths: el cierre transitivo en la consulta
Los property paths de SPARQL 1.1 permiten recorrer una cadena de relaciones sin razonador. El operador
+ sobre pyedu:requierePrerrequisito calcula, en tiempo de consulta, los prerrequisitos
directos e indirectos de un concepto. Es el contraste didáctico con owl:TransitiveProperty, que
materializaría los mismos hechos por inferencia en lugar de en la consulta.
pyedu:requierePrerrequisito+ desde "Búsqueda binaria": la consulta 03 devuelve 5 prerrequisitos. Representación esquemática con la paleta del proyecto; el conjunto exacto de nodos es ilustrativo del recorrido.PREFIX pyedu: <https://w3id.org/ekg-python/schema#>
PREFIX pyr: <https://w3id.org/ekg-python/id/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
# Prerrequisitos directos e indirectos de "Búsqueda binaria"
# mediante un property path (+); no necesita razonador (consulta 03).
SELECT DISTINCT ?prerequisito ?etiqueta
WHERE {
pyr:busqueda_binaria pyedu:requierePrerrequisito+ ?prerequisito .
?prerequisito rdfs:label ?etiqueta .
FILTER(LANG(?etiqueta) = "es")
}
ORDER BY ?etiqueta
# → 5 prerrequisitos de búsqueda binaria.
La misma idea, ordenando los pasos por dificultad creciente, da una ruta de aprendizaje
sugerida hacia un concepto objetivo (consulta 04). Allí el + se combina con OPTIONAL
sobre pyedu:tieneDificultad y ORDER BY ?dificultad.
El operador estrella *: cierre transitivo reflexivo
Junto al + (uno o más pasos), SPARQL 1.1 ofrece el operador * (cero o
más pasos), que añade el camino de longitud cero e incluye el propio nodo de partida.
Lo aprovecho sobre la jerarquía conceptual SKOS (skos:broader) para recuperar los conceptos más
generales que subsumen a uno dado, contándolo a él mismo (consulta 10). La diferencia entre * y
+ es ese nodo de partida.
PREFIX pyr: <https://w3id.org/ekg-python/id/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
# Conceptos más generales de "Recorrido en anchura (BFS)" por la
# jerarquía SKOS, INCLUIDO él mismo (cierre reflexivo con *; consulta 10).
SELECT DISTINCT ?general ?etiqueta
WHERE {
pyr:bfs skos:broader* ?general .
?general rdfs:label ?etiqueta .
FILTER(LANG(?etiqueta) = "es")
}
ORDER BY ?etiqueta
# → 2 filas con * (BFS + Grafo); con + serían 1 (solo Grafo).
* y cuándo +Se usa * cuando el resultado debe contener también el concepto de partida (p. ej. "este concepto y
todos sus generales") y + cuando interesan solo los alcanzados por al menos un salto (p. ej. "sus
prerrequisitos", sin incluirse a sí mismo). Ninguno de los dos necesita razonador: el motor de consulta recorre
la conectividad del grafo en el momento de evaluar el patrón.
3CONSTRUCT: materializar un grafo nuevo
Mientras SELECT devuelve una tabla, CONSTRUCT devuelve un grafo RDF nuevo.
Lo aprovecho para aplanar el cierre transitivo de prerrequisitos en triples directos
(pyedu:tienePrerrequisitoTransitivo), útil para exportar o cargar el resultado en un sistema que no
razone. Sobre el grafo sin inferencia produce 340 triples; con OWL 2 RL, 356.
PREFIX pyedu: <https://w3id.org/ekg-python/schema#>
# Materializa como triples directos el cierre transitivo
# de prerrequisitos: devuelve un grafo nuevo (consulta 06).
CONSTRUCT {
?concepto pyedu:tienePrerrequisitoTransitivo ?prerequisito .
}
WHERE {
?concepto pyedu:requierePrerrequisito+ ?prerequisito .
}
# → 340 triples sin inferencia; 356 con OWL 2 RL.
4SPARQL como administración: UPDATE, INSERT y DELETE
SPARQL no sirve solo para preguntar. Su capa UPDATE (la que ejecuto en GraphDB desde la consola
Query & Update, o en cualquier almacén RDF4J) es el mecanismo con el que se administra el
grafo: dar de alta hechos, corregirlos y retirarlos. Lo incluyo por indicación del director, para mostrar que el
mantenimiento del EKG vive en el mismo lenguaje que lo consulta. Los bloques de abajo operan sobre el esquema y
los identificadores reales del proyecto (pyedu: y pyr:), parafraseados de la ontología
ekg-python-150.ttl.
Estos UPDATE son ejemplos de administración del grafo sobre el modelo real; no forman parte de las
ejecuciones del benchmark ni alteran las cifras canónicas (157 / 1772 / 4786), que se recalculan tras cada cambio
del grafo. Se ilustra la operativa, no se reportan recuentos de filas afectadas.
Alta de hechos con INSERT DATA
Para incorporar un concepto nuevo y enlazarlo a su tema y a sus prerrequisitos, INSERT DATA añade
triples concretos. El patrón es el mismo que ya emplea el Turtle del grafo, solo que expresado como operación de
escritura.
PREFIX pyedu: <https://w3id.org/ekg-python/schema#>
PREFIX pyr: <https://w3id.org/ekg-python/id/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
# Alta de un concepto, con su tema, su prerrequisito y su dificultad.
INSERT DATA {
pyr:comprension_listas a pyedu:EstructuraDeDatos ;
rdfs:label "List comprehension"@en, "Comprensión de listas"@es ;
skos:prefLabel "Comprensión de listas"@es ;
pyedu:perteneceATema pyr:T_colecciones ;
pyedu:requierePrerrequisito pyr:bucle_for ;
pyedu:tieneDificultad 3 .
}
Corrección con DELETE / INSERT … WHERE
Para corregir un valor existente, el patrón DELETE … INSERT … WHERE retira el
triple antiguo y coloca el nuevo en una sola transacción. Aquí reajusto la dificultad declarada de un concepto:
PREFIX pyedu: <https://w3id.org/ekg-python/schema#>
PREFIX pyr: <https://w3id.org/ekg-python/id/>
# Reajusta la dificultad de "Recursión" de su valor actual a 5.
DELETE { pyr:recursion pyedu:tieneDificultad ?d . }
INSERT { pyr:recursion pyedu:tieneDificultad 5 . }
WHERE { pyr:recursion pyedu:tieneDificultad ?d . }
Retirada de hechos con DELETE WHERE
Y para dar de baja todo lo afirmado sobre un recurso obsoleto, DELETE WHERE elimina
los triples que casen con el patrón. Conviene usarlo con cuidado y dentro de la transacción de mantenimiento:
PREFIX pyr: <https://w3id.org/ekg-python/id/>
# Retira todos los triples cuyo sujeto sea un concepto obsoleto.
DELETE WHERE { pyr:concepto_obsoleto ?p ?o . }
Después de cualquier INSERT/DELETE conviene reejecutar la validación SHACL (el grafo
canónico es conforme, 0 violaciones; el control negativo ejemplo-invalido.ttl dispara
6) y recalcular las cifras del grafo, ya que cambian con cada edición.
5Consulta federada a Wikidata
La consulta federada conecta el EKG con la nube de datos enlazados. Mediante SERVICE, recupera del
endpoint público de Wikidata la descripción en español de cada concepto enlazado con skos:exactMatch.
Hay 30 enlaces, pero la federada devuelve 20 filas, porque solo 20 entidades tienen
schema:description en español recuperable. Es la única consulta del entregable que requiere red; la
página que estás leyendo es offline y muestra su salida ya capturada.
PREFIX pyedu: <https://w3id.org/ekg-python/schema#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
PREFIX schema: <http://schema.org/>
# Consulta FEDERADA: descripción @es desde Wikidata (consulta 08).
SELECT ?concepto ?etiqueta ?descripcionWikidata
WHERE {
?concepto skos:exactMatch ?wd ; rdfs:label ?etiqueta .
FILTER(STRSTARTS(STR(?wd), "http://www.wikidata.org/entity/"))
FILTER(LANG(?etiqueta) = "es")
SERVICE <https://query.wikidata.org/sparql> {
?wd schema:description ?descripcionWikidata .
FILTER(LANG(?descripcionWikidata) = "es")
}
}
# → 20 filas. Ej.: python → "lenguaje de programación de alto nivel".
›Una muestra de la salida real (id · etiqueta · descripción Wikidata @es)
| id | Etiqueta | Descripción (Wikidata, es) |
|---|---|---|
python | Python | lenguaje de programación de alto nivel |
recursion | Recursión | método en ciencias de computación |
busqueda_binaria | Búsqueda binaria | algoritmo de búsqueda |
diccionario | Diccionario | estructura de datos que asocia claves con valores |
clase | Clase | en programación orientada a objetos, plantilla para la creación de objetos de datos según un modelo predefinido |
6Ver también
El grafo de conocimiento
La ontología EKG, sus métricas canónicas, la grafoteca interactiva y las ocho consultas con su salida real.
Sistema RAG
Cómo el subgrafo recuperado por SPARQL se inyecta en el prompt de los sistemas C y D.
Recursos
Ficheros del entregable: ontología, consultas .rq, formas SHACL y guía de carga en GraphDB.
Si este trabajo te resulta útil y quieres referenciarlo, esta es la cita recomendada.
Bueno Junquero, A. (2026). Integración de un grafo de conocimiento educativo con un LLM mediante RAG. Trabajo Fin de Máster, Máster Universitario en Investigación en Inteligencia Artificial, UNED. Director, José Luis Fernández Vindel.