Eksperimentering med Ontologi, versjon 0.2_gml - Utdanningsdirektoratet/Grep_SPARQL GitHub Wiki
Denne siden er en del av Ontologi for Grep
Den første delen av denne artikkelen, bygger på en ontologi der siste leddet i URIen til typer/Classes er med liten forbokstav. Fila kan lastes ned og implementeres i en egen løsning, men er også tilgjengelig i en periode i https://sparql-beta-data.udir.no/sparql (velg repoet "OntologiLowercase").
Vår policy i dette ontologi-eksperimentet har så langt vært at URI for owl:Class, owl:DatatypeProperty og owl:ObjectProperty har levd i samme navnerom. Det viser seg å skape en del trøbbel i graf-sammenheng, særlig når vi får typer/klasser som deler navn (og dermed også URI) med properties (homonymi i URI-sammenheng).
Eksempel:
- Typen (owl:Class) "Kompetansemålsett": http://psi.udir.no/ontologi/kl06/kompetansemaalsett
- **Egenskapen (i dette tilfellet owl:ObjectProperty) "kompetansemålsett": http://psi.udir.no/ontologi/kl06/kompetansemaalsett
Disse har altså samme URI. Det går fint å bare søke opp distinkt alle ?s a owl:Class
. Da får vi de 44 typene som vi p.t. har. Men – hvis vi også vil ta med rdfs:label og rdfs:comment, så drar vi også med oss ?s som er ?s a owl:ObjectProperty
der ?s også deler URI med ?s a owl:Class
. I skrivende stund får vi nå 68 treff der vi forventet 44.
Spørringen:
PREFIX owl: <http://www.w3.org/2002/07/owl#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
select distinct * where {
?s a owl:Class ;
rdfs:label ?label .
FILTER (LANG(?label)="nb")
FILTER (LANG(?label)="nb")
} ORDER BY ?s
Utsnitt av retur:
Da får vi noen ekstra oppføringer som den som er rammet inn i rødt. Den trippelen er ikke skrevet inn i ontologi-fila, men SPARQL tolker/utleder at det finnes en slik trippel. Årsaken er at hvis du legger inn mange grafer/tripler der u:kompetansemaalsett er subjekt, så vil alle kombinasjoner av property-objekt-par også være sanne for subjektet u:kompetansemaalsett. Vi kan vise dette eksplisitt ved følgende spørring:
PREFIX u: <http://psi.udir.no/ontologi/kl06/>
select * where {
u:kompetansemaalsett ?property ?objekt
}
Som gir:
Det første vi ser, er at u:kompetansemaalsett er både av typen owl:Class og owl:ObjectProperty. Så om vi enten ber om u:kompetansemaalsett som klasse eller property, så er alt annet under i tabellen også sant, både der subjektet er owl:Class og der subjektet er owl:ObjectProperty. Det er u:kompetansemaalsett som er subjekt i spørringen, og alt i tabellen handler om dette ene subjektet.
Vi skriver mer om en mulig løsning nedenfor, men først litt om URIer.
Den første versjonen av Grep som kom i 2005/2006 var basert på Emnekart/TopicMap. I den norske Wiki-artikkelen (se lenken) er Grep nevnt som ett av de emnekart-baserte systemene i Norge. Emnekart finnes i sekken semantisk teknologi, sammen med RDF/SPARQL. En annen sekk hvor begge disse teknologiene forekommer, er "Linked Data" eller "Linked Open Data" med Tim Berners-Lee i spissen. Hovedprinsippet er: "Uniform Resource Identifiers (URIs) should be used to name and identify individual things", blant annet.
Rundt 2015(?) gikk vi bort fra Emnekart, og gikk over til et REST-basert API, men tilbyr RDF/SPARQL i tillegg. Men ser du nærmere på REST-APIet vårt, er det lett å se at URIene lever i beste velgående der.
Vår løsning har menneskelesbare URIer, og har levd godt med det til nå. Vi skriver http://psi.udir.no/ontologi/kl06/kompetansemaalsett og ikke f.eks http://psi.udir.no/ontologi/kl06/3c1d0b79-bbf7-49c6-bd15-18eeac7ddd52. Så har det med tiden blitt slik at vi har fått en rekke properties som heter det samme som typen. Etter vår policy til nå, og uten en ontologi, så har ikke dette vært noe praktisk problem. I daglig bruk er det ikke typen/klassen som er subjektet eller objektet i spørringen, men forekomstene av typen. To eksempler på URI for forekomst av kompetansemålsett:
- http://psi.udir.no/kl06/KMS354 (type: u:kompetansemaalsett)
- http://psi.udir.no/kl06/KV370 (type: u:kompetansemaalsett_lk20)
Legg merke til at forekomstene av de ulike typene ligger i navnerommet
http://psi.udir.no/kl06/,
mens navnerommet for typer og egenskaper er
http://psi.udir.no/ontologi/kl06/
Siden meningen med URIer er at de skal være globalt unike identifikatorer, så kan vi ikke lage statements der én og samme URI både er en owl:Class og owl:ObjectProperty samtidig.
Properties er todelt i OWL; vi har owl:DatatypePropery og owl:ObjectProperty.
- owl:DataTypeProperty holder på egenskaper der "range" er datatyper; litteraler, boolske verdier, tall, datoer osv
- owlObjectProperty holder på egenskaper der "range" er referanse til ett eller flere andre objekter (f.eks i egen løsning)
Vi er ikke redd for at vi får like URIer mellom de to property-typene, men som sagt har vi flere (p.t. 12) mellom owl:ObjectProperty og owl:Class. Dette gjelder følgende:
- dokumenttype
- fagkategori
- fagtype
- kompetansemaalsett
- oppgave
- opplaeringsfag
- opplaeringsnivaa
- programfag
- sensur
- sluttkompetanse
- spraaknivaa
- status
Det er ingen typer som sammenfaller med owl:DataTypeProperty.
Hvis vi ser på disse som egenskaper (owl:ObjectProperty), så er 'rdfs:domain' for disse, forekomster av typen av samme navn. For eksempel har en gitt fagkode egenskapen 'opplaeringsfag', som da er referanse til et array av forekomster av typen 'opplaeringsfag'.
Fagkode som bruker egenskapen opplæringsfag:
d:ADI2001 u:opplaeringsfag d:ADI2Z01
Elementet ADI2Z01 er av typen opplæringsfag:
d:ADI2Z01 rdf:type u:opplaeringsfag
Opplæringsfag som type i en ontologi:
u:opplaeringsfag rdf:type owl:Class
Opplæringsfag som egenskap i en ontologi:
u:opplaeringsfag rdf:type owl:ObjectProperty
Som vi har vist tidligere, er de to siste på kollisjonskurs.
Som vi har sett, er det ved navnerommene og utformingen av URIer hvor skoen trykker.
Siden vi offisielt p.t. ikke har noen ontologi (vi er tross alt i tenke- og utprøvingsboksen), kunne vi flyttet hele ontologien til et annet navnerom, f.eks.
- http://owl.udir.no/ontologi/kl06/classes/ og
- http://owl.udir.no/ontologi/kl06/properties/ (blanding av de to property-typene)
Evt på norsk:
Denne løsningen sørger for det første at ontologien som helhet løsrives fra data (ontologien ligger i owl.udir.no-domenet), og for det andre at typer og egenskaper ligger adskilt (kl06/classes vs kl06/properties).
Typer og egenskaper kan også skilles på en annen måte, f.eks. slik:
- http://owl.udir.no/ontologi/kl06/Opplaeringsfag (opplæringsfag som type)
- http://owl.udir.no/ontologi/kl06/opplaeringsfag (opplæringsfag som egenskap)
På den måten trenger vi ikke å forholde oss til egne "mapper" for typer og egenskaper; vi skiller ved hjelp av stor og liten forbokstav.
I ontologi-prosjektet har vi allerede stor forbokstav i rdfs:label for typer, og liten forbokstav for egenskaper. Ved å gjøre dette også for selve URIen, vil u:Oppplaeringsfag og u:opplaeringsfag være globalt unike i og med at URIer brukt i RDF/SPARQL er case sensitive.
Hvis vi går for en løsning med stor forbokstav for typer, kan vi leke med tanken om at dette fortsatt kan ligge i domenet psi.udir.no som i dag. Vi har tross alt skilt mellom typer og egenskaper. Da får vi:
Da vil følgende tripler gi god mening:
- d:ADI2Z01 rdf:type u:opplaeringsfag
- d:ADI2Z01 owl:Class u:Opplaeringsfag
- d:ADI2001 u:opplaeringsfag d:ADI2Z01
Men vi kan ikke si:
- u:opplaeringsfag owl:Class u:Opplaeringsfag
Et gitt et prefix o: http://owl.udir.no/ontologi/kl06/, gir det ikke bedre mening å si:
- u:opplaeringsfag owl:Class o:Opplaeringsfag
Vi er like langt, for følgende vil etter samme mønster også være sant:
- u:opplaeringsfag owl:ObjectProperty o:opplaeringsfag
En tredje vei, vil være å døpe om de egenskapene som er homonyme med typer, f.eks slik:
- er-dokumenttype
- er-fagkategori
- er-fagtype
- har-kompetansemaalsett
- har-oppgave
- har-opplaeringsfag
- er-opplaeringsnivaa
- har-programfag
- har-sensur
- gir-sluttkompetanse
- er-spraaknivaa
- har-status
Men det bryter med kontrakten fullstendig. Alle må skrive om sine løsninger.
Grunnen til at vi i dagens løsning kan ha homonyme egenskaper og typer, er at egenskapene alltid er i predikat-posisjonen i en trippel, mens typene er i objekt-posisjonen (i en spørring med ?s ?p ?o (?subjekt, ?predikat, ?objekt)). Men i en ontologi, vil de ulike typene og egenskapene også forekomme i subjekt-posisjon, der predikat kan være f.eks rdfs:label der label-teksten/strengen ligger i objekt-posisjonen. Med andre ord – typene og egenskapene er subjekter du kan slå opp.
Hvis vi skal ha en ontologi som lever side ved side med dataene, må vi skrive om ganske mye i selve dataene.
For det første må vi skrive om, slik at type-URI-ene har stor forbokstav. d:ADI2Z01 kan ikke være rdf:type u:opplaeringsfag, men f.eks u:Opplaeringsfag
Da kan u:Opplaeringsfag være rdf:type owl:Class.
Etter vår versjons-policy, fører dette til at vi da i tilfelle må vente på en neste totalomskriving av Grep.
I Beta-miljøet har vi lagt inn to repositorier som begge inneholder owl-ontologi der verdier for owl:Class har stor forbokstav. Den ene med kun ontologien, og den andre med ontologi sammen med datene (v201906):
- "OntologiUppercase"
- "OntologiUpperMedV201906"
Last ned ontologi-test_Uppercase.ttl og importer til din egen løsning, eller gå til https://sparql-beta-data.udir.no/sparql og velg ett av de to repoene med "uppercase" eller "upper" i navnet, som beskrevet nedenfor.
I et repo i GrpahDB der innholdet kun er en OWL-ontologi, og alle owl-Class har stor forbokstav som i URIen http://psi.udir.no/ontologi/kl06/Opplaeringsfag, slipper vi forvirringen med typer og klasser som har like URIer. Med dette som en regel, vil brukeren også venne seg til at en URI i ontologi-navnerommet med stor forbokstav alltid er en owl:Class.
I repoet OntologiUppercase i Beta-miljøet har vi gjort dette, og vi har mulighet til å teste hvordan dette vil fungere i praksis. Husk at dette repoet ikke har forekomster av typer (det vi kan kalle data). Det betyr at f.eks. d:NOR01-06
fortsatt har predikatet u:grep-type
(eller rdf:type
eller a
), med objektet u:laereplan_lk20
og ikke u:Laereplan_lk20
.
Følgende spørring viser at vi kan kan hente fram hhv. u:opplaeringsfag
og u:Opplaeringsfag
uten at de er blandet i hver spørring:
PREFIX u: <http://psi.udir.no/ontologi/kl06/>
SELECT DISTINCT * where {
u:opplaeringsfag ?p ?o
}
Her ser vi at u:opplaeringsfag
er rdf:type
owl:ObjectProperty
.
Tilsvarende for u:Opplaeringsfag
:
PREFIX u: <http://psi.udir.no/ontologi/kl06/>
SELECT DISTINCT * where {
u:Opplaeringsfag ?p ?o
}
Her ser vi at u:Opplaeringsfag
er rdf:type
owl:Class
.
I repoet OntologiUpperMedV201906 ligger både ontologien (der owl:Class er skrevet med stor forbokstav) og dataene. Skulle et slukt repo være "fullkommet, burde også trippelen som definerer opplæringsfaget 'ADI2Z01' vært skrevet slik: ADI2Z01 rdf:type u:Opplaeringsfag
, men det blir som vi har vært inne på, å bryte med kontrakten (v201906). Derfor har vi bare importert dataene slik de er i Produksjonsmiljøet. Her er 'ADI2Z01' skrevet slik: ADI2Z01 rdf:type u:Opplaeringsfag
I spørringen nedenfor "trikser" vi litt for å omgå dette. Vi skriver om u:opplaeringsfag" til
u:Opplaeringsfagved hjelp av
BIND```:
PREFIX u: <http://psi.udir.no/ontologi/kl06/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT ?s ?tittel ?grepType ?owlClass ?rdfsLabel ?rdfsComment WHERE {
?s a u:opplaeringsfag;
u:tittel ?tittel ;
u:grep-type ?grepType .
# Binder slik at vi henter u:en_gitt_kode og transformerer den til u:En_gitt_kode med uppercase.
# Fra ?grepType til ?owlClass
# Krav: Den transformerte verdien skal fortsatt være en IRI
BIND(
IRI(CONCAT(
"http://psi.udir.no/ontologi/kl06/",
UCASE(SUBSTR(STRAFTER(STR(?grepType), "http://psi.udir.no/ontologi/kl06/"), 1, 1)),
SUBSTR(STRAFTER(STR(?grepType), "http://psi.udir.no/ontologi/kl06/"), 2)
)) AS ?owlClass
)
# Vi kan fortsatt bruke (hente ting fra) ?owlClass - i dette tilfellet ?rdfsLabel og rdfs:comment
?owlClass rdfs:label ?rdfsLabel ;
rdfs:comment ?rdfsComment .
FILTER (
(LANG(?rdfsLabel) = "nb")
&& (LANG(?rdfsComment) = "nb")
&& (LANG(?tittel) = "nob")
)
}
ORDER BY ?tittel
LIMIT 10
Klipp fra returen:
Så lenge vi er i v201906, nøyer vi oss med å holde ontologien i et Beta-miljø for testøyemed. I et produksjonmiljø bør det etter vår mening være samsvar mellom rdf:type (i data) og owl:Class (i ontologi).
Eksempel 1: Oppslag på type:
PREFIX u: <http://psi.udir.no/ontologi/kl06/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
SELECT ?type ?rdfsLabel ?rdfsComment WHERE {
u:Opplaeringsfag rdfs:label ?rdfsLabel ;
rdfs:comment ?rdfsComment ;
a ?type .
FILTER (
(LANG(?rdfsLabel) = "nb")
&& (LANG(?rdfsComment) = "nb")
)
}
Retur:
Eksempel 2: Oppslag på egenskap:
PREFIX u: <http://psi.udir.no/ontologi/kl06/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
select * where {
u:opplaeringsfag rdfs:label ?rdfsLabel ;
rdfs:comment ?rdfsComment.
FILTER (
(LANG(?rdfsLabel)="nb")
&& (LANG(?rdfsLabel)="nb")
)
}
Retur:
Den eneste forskjellen på disse to spørringene, er u:Opplaeringsfag
i den første, og u:opplaeringsfag
i den andre. Videre ser vi at vi får to treff for den andre spørringen. Det er fordi vi spør om u:opplaeringsfag a ?type
, og vi ser at vi har rdf:Property
og owl:ObjectProperty
under ?type
i tabellen. owl:objectProperty er en sub-class av rdf:Property.
For å finne ut av dette i repoet 'v201906' (kun data, uten ontologi), vil dette være en svært tung spørring. Vi må da løpe igjennom alle forekomster av en gitt type, for deretter hente ut alle forekomster av egenskaper. Hvis vi vil finne ut av hvilke egenskaper som finnes for typen opplæringsfag, kunne vi selvsagt slått opp et tilfeldig opplæringsfag, men da vil vi kunne gå glipp av noen av egenskapene. For eksempel har ikke alle opplæringsfag egenskapen "merkelapper".
I en ontologi, er dette definert, og spørringen er svært enkel (spørringen nedenfor tok 0.1s):
PREFIX u: <http://psi.udir.no/ontologi/kl06/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT * WHERE {
?egenskaper rdfs:domain u:Opplaeringsfag
}
Samme som over, men uten ontologi (vi går til repoet 'v201906'):
PREFIX u: <http://psi.udir.no/ontologi/kl06/>
SELECT DISTINCT ?egenskaper
WHERE {
[] a u:opplaeringsfag; # her løper vi igjennom alle opplæringsfag
?egenskaper ?o .
FILTER (!CONTAINS(STR(?egenskaper), "gyldighet-"))
}
Denne spørringen tok 0.3s.
Legg merke til filteret med negasjonen "!CONTAINS". Mange av predikatene i (?egenskaper) fører til blanke noder for gyldighetsinformasjon i referanseobjekter (egen artikkel som forklarer fenomenet). Disse elimineres med dette filteret.
Den første av de to siste spørringene gir 23 treff, og den siste gir 24. Det er fordi den andre spørringen løper igjennom forekomstene, og de har en egenskap som forteller hvilken type de er (rdf:type). I den første løper vi ikke igjennom forekomster av en type, kun egenskaper og om de har
rdfsdomain u:opplaeringsfag
Denne gjennomganger viser av vi med dagens kontrakt (v201906) ikke kan ha en OWL-ontologi uten å ta hensyn til at vi har noen homonyme rdf:type
med det som i en ontologi vil være owl:ObjectProperty
.
Det vi må vurdere for fremtiden, er om vi skal gå for URIer som ikke er semantisk lesbare.
I EU sine løsninger ser det ut til at det er løpenummer e.l. som gjelder. Her ser vi f.eks at URIen http://eurovoc.europa.eu/2467
, "education policy", er av - - rdf:type <http://www.w3.org/2004/02/skos/core#Concept>
-
rdf:type <http://eurovoc.europa.eu/schema#ThesaurusConcept>
og -
rdf:type <http://www.w3.org/1999/02/22-rdf-syntax-ns#about>
Vi må slå opphttp://eurovoc.europa.eu/2467 skos:prefLabel
for å finne ut at det handler om "education policy"@en ( i dette tilfellet på engelsk)
Gå til https://data.europa.eu/sparql og kjør følgende spørring:
select distinct * where {
<http://eurovoc.europa.eu/2467> ?p ?o
}
Det samme prinsippet gjelder for Wikidata (SPARQL-endepunkt for Wikipedia). Her er både egenskaper og forekomster numeriske (med en bokstavprefiks). For eksempel har egenskapen wdt:P31, labelen "forekomst av" (prefix wdt: <http://www.wikidata.org/prop/direct/>
), og wd:Q515, "by" (prefix wd: <http://www.wikidata.org/entity/>
og wd:Q585, "Oslo".
Denne typen løsninger krever at man må være tydelig i dokumentasjonen som gjør det enkelt å få oversikt (hva er toppkonsepter og hvordan enkelt finne hva toppkonseptene består av osv.). Dette gjør også at vi under oppbygging av spørringer må ta med labler for å kunne følge med som menneske underveis. Erfaringsmessig, er dette en tyngre måte å jobbe på, men når sørringene er klare, har det selvsagt ingen betydning.
Fordelen med en numerisk tilnærming, er at endringer av termer ikke trenger å påvirke identifikasjon.
Utdanningsprogrammet
u:HS
hette i starten "Helse- og sosialfag", men endret navn til "Helse- og oppvekstsfag". Koden "HS" består. Med en numerisk kode, hadde vi trolig ikke trengt å opprette egenskapen "tidligere-navn".
Hvis vi ikke går for en OWL-type av en ontologi, men heller går for SKOS (Simple Knowledge Organization System), må/bør vi følge den norske applikasjonsprofilen til Begrepskatalogen SKOS-AP-NO. Der er det utstrakt bruk av UUID. Dette er stort sett likt som det du finne av data fra EU.
Denne artikkelen er et notat gjort etter testing av OWL som ontologi. I denne testen har vi ikke brukt alt hva OWL kan gi etter standarden – kun det med labler for typer og egenskaper, samt hvordan ting henger sammen ved hjelp av
rdf:domain
ogrdf:range
.
I denne runden, har vi ikke brukt mulighetene som ting som rdfs:subClassOf kan gi.
I en løsning med SKOS, får vi dette med labler, men vi må bygge opp en hierarkisk struktur ved hjelp av referanse-egenskapeneSKOS:broader
ogSKOS:narrower
(ikke det samme som domain/range i RDF).