SPARQL‐kurs ‐ del 2 - Utdanningsdirektoratet/Grep_SPARQL GitHub Wiki

Denne siden er en del av SPARQL-kurs

<-- del 1   del 3 -->

Del 2: Vi gjør oss kjent med Greps SPARQL-tjeneste

- og bruker det vi har lært i del 1

1. Åpne Workbench

Vi går rett på sak, og går til https://sparql-beta-data.udir.no/sparql. Da kommer du til siden som er Greps implementasjon av et produkt som heter GraphDB, og det du ser, kalles GraphDBs Workbench. Det er her vi som mennesker kan lage og kjøre spørringer, og teste disse mot dataene til Grep. Det å bruke dette som maskiner, kommer vi tilbake til senere (og vi nevner det så vidt lengre nede i denne delen).

Vi bruker Beta til testing. Den har dagferske data, akkurat som "Prod."). Som navnet indikerer – Prod. skal kun brukes til produksjonsformål.

2. Velg riktig repositorium (repo)

Velg repository, klikk deretter på "SPARQL"
Velg repoet "201906" oppe til høyre på siden, og klikk deretter "SPARQL" i venstremenyen hvis den ikke allerede er valgt (du skal ikke logge inn).

Da får du dette bildet:
Side med default-spørring ("gi meg alt du har, men bare 100 forekomster")
Spørringen du ser her, betyr: "gi meg alt du har i databasen (?s ?p ?o), men bare 100 forekomster"
Neste gang du kommer til denne siden, er det ikke sikkert at du er nødt til å velge repo, og klikke på "SPARQL" i venstremenyen, men det er litt avhengig av nettleser og cookies lokalt hos deg.

Den første spørringen mot Grep

Skriv spørringen ved å erstatte teksten i det store redigeringsfeltet med teksten med eksempelspørringen i boksen nedenfor.

PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX u: <http://psi.udir.no/ontologi/kl06/>
select * where { 
    ?s rdf:type u:aarstrinn .
}

Det skal nå se slik ut:
Lim inn spørringen og klikk "Run")
Trykk på knappen "Run", så får du dette resultatet:
image
Den oppmerksomme leser ser at forekomster av ting får prefiksen d: i resultatet, selv om du ikke har skrevet det i PREFIX-delen. Det er fordi vi har forhåndsdefinert denne prefixen til å være http://psi.udir.no/kl06/ i GraphDB/Workbench for Grep. Vi utdyper dette med prefikser for Grep senere.

I denne spørringen er subjektet en variabel vi har kalt opp etter "subjekt", ?s. Predikatet er rdf:type. Vi vil altså liste opp forekomster av en spesifikk type, nemlig den typen vi har spesifisert på objekt-plassen, u:aarstrinn.

Nå visste jo jeg på forhånd at det er noe som heter <http://psi.udir.no/ontologi/kl06/aarstrinn>. Hva om jeg kommer hit for første gang og ønsker å vite hvilke typer elementer som er tilgjengelig i Grep? Vi vil ha alle forekomster av ?s som er rdf:type "ett eller annet". Vi bare kaller "ett eller annet" for ?o(etter objekt).?o` er altså nå en variabel, og ikke en konstant som i den første spørringen. Vi skriver:

PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX u: <http://psi.udir.no/ontologi/kl06/>

select distinct ?o where { 
    ?s rdf:type ?o .
}

Som vi var inne på i del 1, så har vi SELECT DISTINCT og vi velger å vise kun ?o. DISTINCT er nyttig her, for ellers ville vi vist ?o for alle forekomster av ting som er av en eller annen type. I vårt tilfelle ville vi fått nesten 60 000 treff. Men ved å skrive SELECT DISTINCT ?s vår vi (i skrivende stund) 51 treff. Her er et utsnitt av hva vi får:
image
Legg merke til de 7 første treffene som er i navnerommene til rdf:, owl: og rdfs. De har ingen praktisk bruk for oss i våre spørringer. I Grep har vi laget et navnerom vi har kalt u: som vi i PREFIXen øverst i spørringen har definert som http://psi.udir.no/ontologi/kl06/. Alle forekomstene av disse listes altså opp under "o" i resultatsettet, sammen med de fra rdf: osv.

Hva er et navnerom i SPARQL?:
Tenk deg at du har mange forskjellige dokumenter på datamaskinen din. For å holde orden på dem, legger du dem i mapper. Hver mappe har et navn som hjelper deg å finne dokumentene du leter etter. For eksempel, du har en mappe for "Bilder", en for "Dokumenter", og en annen for "Musikk".

I SPARQL og RDF-verdenen bruker vi noe lignende som heter "navnerom" for å organisere data. Et navnerom fungerer som en mappe eller en kategori som grupperer sammen relaterte ressurser og deres navn.

Hvorfor er navnerom viktig?
Navnerom hjelper oss med å unngå navnekonflikter. For eksempel, tenk deg at du har to forskjellige dokumenter med samme navn "Rapport.doc". Hvis du legger begge i samme mappe, vil det bli forvirrende. Men hvis du legger én "Rapport.doc" i "Dokumenter"-mappen og den andre i "Prosjekter"-mappen, er det ingen konflikt.

På samme måte, i RDF, kan vi har to forskjellige begreper som heter "type". Ved å bruke navnerom, kan vi skille dem fra hverandre. Dette gir mening når du leser de neste par avsnittene. Det som skiller dette fra sammenligningen med mapper, er at vi her snakker om URIer, og ikke lokasjoner på en PC. Det fine med URIer, er også at starten av en URI (domenet), lett kan se hvem som eier dette navnerommet. Dermed er det lettere å avgjøre om dette er en kilde di vil stole på.

Nå passer det faktisk bra å nevne at vi i Grep har vi laget vår egen type-referanse, kalt u:grep-type som vi kan skrive i stedet for rdf:type. Da får vi alle Grep-typene, uten de fra de andre navnerommene utenfor psi.udir.no. Alle elementer i Grep er da både rdf:type og u:grep-type. De to gir litt forskjellig svar, alt etter hvilket navnerom du bruker.

I spørringene i dette kurset hender det at vi veksler mellom dem, særlig fordi RDF-standarden har definert at vi i stedet for rdf: bare kan skrive a som i den grammatiske artikkelen "a" i engelsk. Altså som ?s a u:aarstrinn ("?s is a u:aarstrinn"). Mer semantisk får du det ikke 😉.

Øvelse 1: List Grep-typer

Første øvelse blir da å skrive en spørring der vi kun lister opp grep-typene.
Se fasit nederst på siden



Den andre spørringen mot Grep: Slå opp en forekomst av en type

I den andre spørringen skal vi slå opp en forekomst av en type og se hva den har å by på.
OK – vi vet at subjektet nå skal være være med i regningen. Hvis vi nå fjerner DISTINCT og tar med ?s i `SELECT, så får vi listet opp alle forekomstene av typen Årstinn. Deretter kan vi velge en av dem som vi slår opp.

Først lister vi altså opp alle forekomstene av Årstinn:

PREFIX u: <http://psi.udir.no/ontologi/kl06/>

select ?s where { 
    ?s a u:aarstrinn .
}

OK – la oss velge d:aarstrinn10 fra resultatet over. Da skriver vi:

PREFIX u: <http://psi.udir.no/ontologi/kl06/>
PREFIX d: <http://psi.udir.no/kl06/>

select * where { 
    d:aarstrinn10 ?p ?o .
}

Her har vi altså forekomsten av noe ("noe" fordi vi i denne spørringen ikke bryr oss om type) på subjektplassen av spørringen, og vi ber om alle ?p (predikat) og ?o (objekt) for denne vi nå slår opp direkte. Vi får dette resultatet:
image
Her ser vi alt hva elementet "Tiende årstrinn" har å by på. Studer alle predikat-objekt-parene en liten stund...

Det første vi legger merke til, er at vi ser p-o-paret rdf:type u:aarstrinn, og u:grep-type u:aarstrinn. Disse kjenner vi nå, men vi ser nå viktigheten av at alle elementene er merket med hva de er. Videre ser vi en del ting som elementet har. Vi har ting som id, kode. Id kan enten være en URI, som i dette tilfellet, men også ting som ser slik ut: "e633b624-cdb0-4053-b522-8efc938304e9". Dette kalles UUID. Meningen med id, er uansett at de skal være globalt unike. I SPARQL bruker vi ikke så mye energi på IDer. Vi er mer opptatt av URIer som også er globalt unike identifikatorer. For eksempel ser vi nede i resultatsettet for d:aarstrinn10 at den har p-o-paret u:uri d:aartrinn10. Vi sier da at tiende årstrinn har en egenskap u:uri med verdien d:aarstrinn10. Det er URIer vi slår opp for å se eller bruke det de har. Det er URIen d:aarstrinn10 som er "telefonnummeret" til "tiende årstrinn".

De som bruker REST-APIet, bruker en annen identifikator for å slå opp elementer (de har en annen "telefonkatalog", om du vil). De bruker egenskapen u:url-data (se etter den i resultatet). Det gjør at du kan slå opp json-fila https://beta-data.udir.no/kl06/v201906/aarstrinn/aarstrinn10 i dette tilfellet. Klikk på lenken, så får du se... Og har du en extension til nettleseren din som viser "JSON" pent, så blir det lettere å lese det du får opp, men det er ikke viktig for dette kurset.

Videre ser vi noen ?o som har en alfakrøll og språkkoder etter seg. For eksempel: u:tittel "Tiende årstrinn"@default. For det første er dette en tekststreng (fordi teksten er mellom anførselstegn), og for det andre har vi markert hvilket språk teksten er på.

Vi i Grep har diktet opp et språk vi har kalt @default i tillegg til @nob (bokmål), @nno (nynorsk) og @sme (nordsamisk). Hvis språket vi spør etter ikke finnes i triplemønsteret du har i spørringen, kan du alltid bruke @default. Det er hovedspråket som du alltid kan bruke som et fallback-språk. Da får du tekststrengen på det språket elementet er fastsatt på. Dette er særlig viktig for læreplaner og tilhørende elementer. Læreplanen kan være fastsatt på bokmål, men ikke oversatt til nynorsk. Hvis du da spør spesifikt etter nynorsk-teksten, vil du ikke få treff. Da er @default bra å ha.

Vi kommer tilbake til det med språk i neste del, men først tar vi med en spørring der vi ikke lister opp alle egenskapene til det årstrinnet vi har slått opp, men velger oss ut noen utvalgte:

Den tredje spørringen mot Grep: Velge noen utvalgte egenskaper vi viser i resultatet

I de neste avsnittene kommer vi inn på tre nye ting vi kan lære:

  1. at teksten vi ber om, gjentas i resultatet for hvert språk (vi kommer tilbake til filtrering i neste del)
  2. at tekststrenger både kan være språkversjonert, og ikke
  3. at vi ikke trenger å gjenta objektet for hver ting vi spør om

Nå ønsker vi å vise koden til dette elementet, og vi vil vise kode og tittel:

PREFIX u: <http://psi.udir.no/ontologi/kl06/>
PREFIX d: <http://psi.udir.no/kl06/>

select *  where { 
    d:aarstrinn10 u:kode ?kode .
    d:aarstrinn10 u:rekkefoelge ?rekkefoelge .
    d:aarstrinn10 u:tittel ?tittel .
}

Vi får dette resultatet:
image
Vi får tre resultater, fordi hver linje er sant for hvert språk av tittel. Hadde vi filtrert, slik at vi kun ser en av oversettelsene, hadde vi kun fått ett resultat.

Vi ser også at kode er en tekststreng, "aarstrinn10", og at den ikke har definert noe språk.

Så ser vi også i spørringen vår, at vi har gjentatt objektet for hver linje i spørringen. Det trenger vi ikke. Spørringen nedenfor er i prinsippet helt lik den over:

PREFIX u: <http://psi.udir.no/ontologi/kl06/>
PREFIX d: <http://psi.udir.no/kl06/>

select *  where { 
    d:aarstrinn10 u:kode ?kode ;
                  u:rekkefoelge ?rekkefoelge ;
                  u:tittel ?tittel .
}

Vi bruker semikolon på slutten av hver triple så lenge det er det samme subjektet vi spør om. Så avslutter vi med punktum til slutt.

En liten anekdote:
Vi trenger i prinsippet ikke å skrive spørringen så "pent" som i disse eksemplene, men det er lettere for oss som mennesker å lese dem slik. Den følgende spørringen er helt lik den forrige, og vil fungere helt fint:

PREFIX u:<http://psi.udir.no/ontologi/kl06/>PREFIX d:<http://psi.udir.no/kl06/>select*where{d:aarstrinn10 u:kode ?kode;u:rekkefoelge ?rekkefoelge;u:tittel ?tittel.}



Faktisk er det mer slik SPARQL-motoren mottar spørringen. I tillegg en interessant ting – spørringen er en eneste stor URL som vi kan sende over nettet. Klikk på denne lenken (det er den samme spørringen som over), så får du se: https://sparql-beta-data.udir.no/sparql?name=&infer=true&sameAs=true&query=PREFIX%20u%3A%3Chttp%3A%2F%2Fpsi.udir.no%2Fontologi%2Fkl06%2F%3EPREFIX%20d%3A%3Chttp%3A%2F%2Fpsi.udir.no%2Fkl06%2F%3Eselect*where%7Bd%3Aaarstrinn10%20u%3Akode%20%3Fkode%3Bu%3Arekkefoelge%20%3Frekkefoelge%3Bu%3Atittel%20%3Ftittel.%7D

De som kjører SPARQL-spørringer maskinelt i sine applikasjonskoder, bruker en URL som ligner denne, men da en slags API-variant. Dette er beskrevet nærmere i avsnittet "endepunkt for maskiner" i artikkelen "Hvordan-bruke-Greps-SPARQL-tjeneste" i denne wikien. Men vi er ikke helt der ennå 😉



Øvelse 2: Finn en forekomst av en annen Grep-type, og finn ut hva den inneholder

Denne øvelsen har ingen fasit. Meningen er bare å få dette litt inn i fingrene



Tips

I REST-wikien vår har vi listet opp alle typene i Grep. Se på lenkene i den første kolonnen i tabellen på siden, og klikk på typene. Da får du blant annet lese en beskrivelse av de ulike typene - altså en forklaring på hva de ulike typene er/betyr (på norsk og engelsk)



Oppsummering, del 2

I denne delen har vi gjort oss litt kjent med Greps sparql-tjeneste, og vi har kjørt noen enkle spørringer. Vi skal nå ha det vi trenger får å kunne

  • liste opp hvilke tilgjengelige typer/klasser Grep har å tilby
  • velge oss en type, og liste opp forekomster av denne
  • velge oss en forekomst av en type, og liste hva denne har å tilby

Del 3 skal sette oss i stand til å kunne bruke noen enkle filtre i spørringene, og spesielt se på hvordan vi kan håndtere språkversjoering av tekststrenger.





<-- del 1   del 3 -->



Fasit, øvelser

Fasit, Øvelse 1: List Grep-typer

Denne skulle være enkel. Vi bare bytter rdf:type med u:grep-type:

PREFIX u: <http://psi.udir.no/ontologi/kl06/>

select distinct ?o where { 
    ?s u:grep-type ?o .
}

Vi får nå 44 treff (i skrivende stund), altså bare Grep-typer.
Men legg merke til at vi har fjernet linjen PREFIX rdf: .... Den trenger vi ikke lenger i denne spørringen.

<-- del 1   del 3 -->

⚠️ **GitHub.com Fallback** ⚠️