SPARQL – Noen utvalgte tips og triks - Utdanningsdirektoratet/Grep_SPARQL GitHub Wiki

Denne siden er under utvikling

Den beste måten å bruke denne siden på, er å selv teste spørringene nedenfor i SPARQL-løsningen vår. Gå til https://sparql-beta-data.udir.no/sparql og kjør spørringene der, så ser du resultatene. Da er det også lettere å følge og forstå forklaringene. På siden Hvordan-bruke-Greps-SPARQL-tjeneste#graphdb-workbench-for-mennesker kan du sette deg inn i hvordan du kommer i gang.

Innhold:

En slags YouTube-versjon av noe av det du finner på denne siden: https://www.youtube.com/watch?v=4U1qRC4AUAE

Det mest grunnleggende

Hvis du kjenner til spørrespråk for vanlige relasjonsdatabaser, for eksempel SQL, vet du at det der handler om tabeller, innholdet i dem og relasjoner mellom tabellene. RDF og SPARQL fungerer ikke på samme måten, selv om det i høy grad handler om relasjoner. En enkel måte å forklare et SPARQL-endepunkt på, er å se på det som en database med én eneste stor tabell med kun tre kolonner som hver av dem har en semantisk funksjon – henholdsvis subjekt, predikat og objekt. Med disse tre kolonnene kan du i prinsippet uttrykke det samme som hvilken som helst database.

Illustrasjon: For eksempel kan uttrykket "Jeg (med denne ID-en) heter Are", kan uttrykkes slik:
| <https://www.eksempel.no/personnummer/14046512345> | <http://xmlns.com/foaf/0.1/name> | "Are" |

Det er tungvint å skrive disse lange URLene hele tiden, derfor vil du se at vi i starten av spørringene ofte bruker såkalte prefix. Uttrykket over kan da uttrykkes slik:

PREFIX pnr: <https://www.eksempel.no/personnummer/>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>

Subjekt Predikat Objekt
pnr:14046512345 foaf:name "Are"

SPARQL står for "Simple Protocol and RDF (Resource Description Framework) Query Language".


Den mest grunnleggende spørringen du kan skrive, er :
Spørring 1

# spørring 1
select * where { 
	?s ?p ?o .
} limit 500 

Spørring 1
Her er både subjektet, predikatet og objektet variabler (variabler starter alltid med "?"), og spørringen vil da returnere alt som er i databasen du kjører spørringen i. Derfor har jeg lagt til limit 500 for å begrense resultatet til 500.
# helt øverst i spørringen er tegnet for kommentar. Alt etter "#" i linjen fjernes fra evalueringen, men er fin å bruke til forklaringer.

Oppslag på et element:
Spørring 2

# Spørring 2
PREFIX d: <http://psi.udir.no/kl06/>
select * where { 
	d:NOR01-06 ?p ?o .
}

Her kjenner vi koden til et element, http://psi.udir.no/kl06/NOR01-06 som vi ved hjelp av prefixet d: forkorter til d:NOR01-06.
?p og ?o gir oss hhv propertiene/egenskapene (?p), og egenskapenes verdier (?o).

Hvis du ikke kjenner noen læreplankode, kan du søke dem opp ved for eksempel å skrive
Spørring 3

# Spørring 3
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX u: <http://psi.udir.no/ontologi/kl06/>
select * where { 
	?laereplan rdf:type u:laereplan_lk20 .
}

Her ligger variabelen på subjekt-plassen, og vi kjenner verdiene for predikatet og objektet. Denne spørringen gir en liste over alle læreplaner av LK20-typen.

Vi kan også la både subjekt og predikatet være variabler, mens vi angir objektet f.eks. slik:
Spørring 4

# Spørring 4
select * where { 
	?s ?p "Læreplan i norsk"@nob .
}

Da får vi en liste over alle læreplaner som har strengen "Læreplan i Norsk". ("@nob angir språk – i dette tilfellet bokmål). Og i resultatet ser vi at denne strengen på objektplassen er verdien for predikatet u:tittel, mens subjektet lister opp læreplaner som har nøyaktig denne tittelen.

Objektet kan være alle mulige datatyper, som string, dato, IRI, integer, boolian osv. I det neste eksempelet er objektet en IRI:
Spørring 5

# Spørring 5
PREFIX d: <http://psi.udir.no/kl06/>
PREFIX u: <http://psi.udir.no/ontologi/kl06/>
select * where { 
	d:NOR01-06 u:grep-type ?o .
}

"NOR01-06" er altså en læreplan av LK20-typen. u:grep-type er en egenskap for alle elementer for å angi hvilken typen/klasse de er i Greps datamodell. Så hvis du i spørring 3 heller ikke kjenner til typene, kan du liste alle grep-typene slik:
Spørring 6

# Spørring 6
PREFIX d: <http://psi.udir.no/kl06/>
PREFIX u: <http://psi.udir.no/ontologi/kl06/>
select distinct ?type where { 
    ?s u:grep-type ?type
}

Her ser du vi har skrevet distinct i select-delen av spørringen, og i tillegg ber vi om at bare ?type skal vises i resultatet ("type" blir da overskriften for raden i tabellen du får i resultatet). Distinct, fordi vi ikke vil ha med alle elementer som er av de ulike typene (altså innholdet i arrayet), men kun de ulike typene én gang hver (distinkt).

Apropos array – den neste spørringen kan også skrives slik:
Spørring 7

# Spørring 7
PREFIX d: <http://psi.udir.no/kl06/>
PREFIX u: <http://psi.udir.no/ontologi/kl06/>
select distinct ?type where { 
    [] u:grep-type ?type
}

Vi bruker symbolet for array ([]) når vi ikke bryr oss om hvilke elementer som er i arrayet, og i denne konkrete spørringen spør vi jo uansett bare etter det vi har kalt ?type. I denne sammenhengen er ikke dette et tomt array, men et symbol for array som representerer en mengde gitt av de andre delene av grafmønsteret vi beskriver i spørringen.

Den forrige spørringen gir altså alt som er definert som grep-typer i Grep-databasen, men det finnes også to universelle metoder som fungerer for alle RDF/SPARQL-repoer, enten:
Spørring 8

# Spørring 8
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
select distinct ?type where { 
    ?s rdf:type ?type

eller enda enklere:
Spørring 9

# Spørring 9
select distinct ?type where { 
    ?s a ?type

a her, er bare en innebygget måte etter SPARQL-standarden å skrive rdf:type uten å bruke prefixet rdf:. a er alltid predikat i spørringen. Bokstaven "a" er rett og slett den ubestemte artikkelen på engelsk, som i "?s is a ?type".

Det nest mest grunnleggende

Nå kan vi utvide spørringene for å finne det du vil ha fram i spørringen, men også traversere og spørre videre i objekter som er relatert til det objektet vi ser på. Vi ser også på hvordan vi håndterer språkversjoner av tekststrenger.

Vi tar utgangspunkt i spørring 3, "oppslag på element". Her bytter vi variabelen ?p med noe vi fant i resultatet, nemlig u:tittel, så kaller vi objekt-variabelen ?tittel:
Spørring 10

# Spørring 10
PREFIX d: <http://psi.udir.no/kl06/>
select * where { 
	d:NOR01-06 u:tittel ?tittel .
}

Da får vi dette resultatet:

tittel
1 "Læreplan i norsk"@default
2 "Læreplan i norsk"@nob
3 "Læreplan i norsk"@nno
4 "Curriculum for Norwegian"@eng

Men vi kan også velge å hente en tittel på kun ett spesifikt språk:
Spørring 11

# Spørring 11
PREFIX u: <http://psi.udir.no/ontologi/kl06/>
PREFIX d: <http://psi.udir.no/kl06/>
select  * where { 
	d:NOR01-06 u:tittel ?tittel .
    FILTER(lang(?tittel) = "default")
}

Da får vi følgende resultat:

tittel
1 "Læreplan i norsk"@default

Vi i Grep har diktet opp et språk vi har kalt @default for å kunne hente tekstverdien på det språket som læreplanen er fastsatt på (forskriftsfestet språk). Læreplaner kan enten være fastsatt på bokmål, nynorsk eller nordsamisk. Hvis du lurer på hvilket språk læreplanen er fastsatt på, kan du legge til dette i spørringen:
Spørring 12

# Spørring 12
PREFIX u: <http://psi.udir.no/ontologi/kl06/>
PREFIX d: <http://psi.udir.no/kl06/>
select  * where { 
	d:NOR01-06 u:tittel ?tittel ;
            u:fastsatt-spraak ?fastsattSpraak .
    FILTER(lang(?tittel) = "default")
}

Resultat:

tittel fastsattSpraak
1 "Læreplan i norsk"@default d:nob

Legg merke til semikolon og punktum i spørringen over. Vi kan spørre videre på d:NOR01 ved å bruke ; på slutten av hver linje helt til vi er ferdige. Da avslutter vi med .. Men følgende spørring er egentlig helt den samme:
Spørring 13

# Spørring 13
PREFIX u: <http://psi.udir.no/ontologi/kl06/>
PREFIX d: <http://psi.udir.no/kl06/>
select  * where { 
	d:NOR01-06 u:tittel ?tittel .
        d:NOR01-06 u:fastsatt-spraak ?fastsattSpraak .
    FILTER(lang(?tittel) = "default")
}

Men da må vi i tilfelle avslutte hver linje med .. Ingen tvil om at metoden med ; er enklere og mer oversiktlig.

Videre – vi kan hente ut hvilken læreplan NOR01-06 erstatter, og vi tar med tittelen til denne i samme slengen:
Spørring 14

# Spørring 14
PREFIX u: <http://psi.udir.no/ontologi/kl06/>
PREFIX d: <http://psi.udir.no/kl06/>
select  * where { 
	d:NOR01-06 u:tittel ?tittel ;
            u:grep-type ?type ;
            u:fastsatt-spraak ?fastsattSpraak ;
            u:erstatter ?erstatter .
    ?erstatter u:tittel ?erstTittel ;
               u:grep-type ?erstType .
    FILTER (
               lang(?tittel) = "default" 
               && (lang(?erstTittel) = "default")
    )
}

Resultat:

tittel type fastsattSpraak erstatter erstTittel erstType
1 "Læreplan i norsk"@default u:laereplan_lk20 d:nob d:NOR1-05 "Læreplan i norsk"@default u:laereplan

Legg merke til at når vi får fram noe fra en variabel, kan vi spørre videre på denne (som i ?erstatter u:tittel ?erstTittel .), og vi kan borre videre – for eksempel som her – finne hvilken Grep-type vi har fått fram. Vi har her også lagt til en linje under NOR01-06 for å finne grep-typen for denne for sammenligning. Her ser du at vi har to læreplan-typer: 'laereplan' (for LK06-planene) og 'laereplan_lk20' (for LK20-planene).

Legg også merke til filteret nederst i spørringen. Det samme kan skrives slik:
Spørring 15

# Spørring 15
    FILTER(lang(?tittel) = "default")
    FILTER (lang(?erstTittel) = "default")
<br><a href="#q16"><sup>Spørring 16</sup></a>

Apropos filter – vi kan også bruke regulære uttrykk (regular expressions/regex) i filtre. I spørringen under, spør vi etter alle læreplaner av LK20-typen som inneholder "norsk" i tittelen:

# Spørring 16
PREFIX u: <http://psi.udir.no/ontologi/kl06/>
PREFIX d: <http://psi.udir.no/kl06/>
select  * where { 
	?lp a u:laereplan_lk20 ;
        u:tittel ?tittel .
    FILTER (lang(?tittel) = "default" && REGEX(str(?tittel), "norsk", "i" ))
}
ORDER BY ?lp

Husk at vi må spesifisere datatypen vi vil behandle ?tittel med, derfor skriver vi REGEX(str( - altså "str" for string (tekststreng). "i"-flagget betyr at vi ikke bryr oss om store og små bokstaver. Vi tar ikke opp mer om regex i denne omgang, men det finnes mange ressurser der ute som handler om det. Regex er noe som kan brukes i mange (alle?) programmerings- og spørrespråk.

Setningen over om å spesifisere datatype for regex, gjelder ikke alle SPARQL-løsninger. I GraphDB som vi bruker for Grep i dag, må vi gjøre det. I Virtuoso som som vi brukte tidligere, var ikke dette nødvendig (det kan hende dette er innstilling man kan gjøre i bakkant).

En annen ting vi i Grep ofte bruker regex-filter til, er å filtrere på status – om vi skal returnere enten utgåtte eller publiserte elementer. Ting som er utgått, slettes ikke. Vi bare setter et utgått-stempel på dem.

I eksempelet nedenfor bruker vi to regex-filtre. Det første for å finne alle revisjonene av "Læreplan i norsk", uavhengig av om de er av LK06- eller LK20-typen, og det andre for å kun vise læreplaner som er publisert.
Spørring 17

# Spørring 17
PREFIX u:<http://psi.udir.no/ontologi/kl06/>
PREFIX d:<http://psi.udir.no/kl06/>
SELECT DISTINCT * WHERE {
    [] a u:opplaeringsfag ; u:laereplan-referanse ?lp . # lure sparql til å hente både LK06- og LK20-planer ;-)
    ?lp u:status ?status ;
        u:tittel ?lpTittel .
    FILTER (
        (REGEX(str(?lp), "NOR1-", "i") || REGEX(str(?lp), "NOR01-", "i" ))
        && REGEX(str(?status), "publisert", "i")
        && (lang(?lpTittel) = "default")
    )
}

Husk: En læreplan kan ha 'status' "publisert", men ikke gyldig (ennå/lenger). Bruk derfor egenskapen 'gyldig-fra' og evt. 'gyldig-til' for å avgjøre om læreplanen er i bruk. Læreplanene bruker å bli publisert i god tid før de skal gjelde, slik at skolen kan forberede seg.

Legg også merke til || (OR) i det første regex-filteret. Her sier vi at vi kun er ute etter ?lp som inneholder "NOR1-" ELLER "NOR01-".

Kodene til LK06-læreplaner består av tre bokstaver, etterfulgt av ett tall + bindesstrek + to tall (f.eks. NOR1-01), mens LK20-planer har dette mønsteret: Tre bokstaver, etterfulgt av to tall + bindestrek + to tall (f.eks. NOR01-06). Læreplan i norsk har disse revisjonene: NOR1-01, NOR1-02, NOR1-03, NOR1-04, NOR1-05 og så kom fagfornyelsen (LK20) og vi fikk NOR01-06.

De to siste tallene er altså løpenummer for revisjoner/versjoner av en og samme læreplan.

Vi har bare måttet legge til en posisjon etter det vi kaller "grunnkoden" ("NOR") fordi vi for noen læreplaner er oppe i 9 ulike varianter av et fag, f.eks. MAT09-01, "Læreplan i matematikk fellesfag vg1 teoretisk (matematikk T)". Hadde vi ikke hatt to tall etter grunnkoden, ville vi fått problemer hvis det skulle dukke opp en tiende variant.

Dette er selvsagt en av ulempene med å putte menneskelig semantikk i koder. Fordelen er på den andre siden at lærere i hele landet kan kodene på sine fag, og da er det greit at det er et mennskelesbart mønster.

Litt om "status":

Tilbake til regex-filteret på ?status i spørringen over. Vi kan selvsagt skrive URLen til status-verdien direkte på objektplassen i spørringen, f.eks ?lp u:status st:publisert der vi på forhånd har prefikset PREFIX st: <https://beta-data.udir.no/kl06/v201906/status/status_>, men det gjør spørringen miljøspesifikk. I Beta-miljøet er prefikset st: "https://beta-data.udir.no/kl06/v201906/status/status_", mens det samme prefikset er i Prod-miljøet "https://data.udir.no/kl06/v201906/status/status_". Med andre ord – status-verdien er ikke en del av ontologien (som kan nås via u:), men en 'url-data'-verdi *1. Men når du har testet spørringen din, og vil bruke Prod-miljøet i ditt produksjonssystem, kan du fint filtrere på publiserte elementer ved å skrive ?lp u:status st:publisert, forutsatt at du tar med prefixen PREFIX st: <https://data.udir.no/kl06/v201906/status/status_ i spørringen.




Property paths

- om å kaste flyndre med SPARQL

Vi tar utgangspunkt i spørringen over som handler om at en læreplan erstatter en annen læreplan.
Spørring 18

# Spørring 18
PREFIX u: <http://psi.udir.no/ontologi/kl06/>
PREFIX d: <http://psi.udir.no/kl06/>
select  * where { 
	d:NOR01-06 u:tittel ?tittel ;
            u:grep-type ?type ;
            u:erstatter ?erstatter .
    ?erstatter u:tittel ?erstTittel ;
            u:gyldig-fra ?gyldigFra ;
            u:grep-type ?erstType .
    FILTER(lang(?tittel) = "default" && (lang(?erstTittel) = "default"))
} ORDER BY ?gyldigFra

Denne erstatter-relasjonen mellom læreplaner kan vi følge helt fra NOR1-01 og helt fram til dagens revisjon, NOR01-06. NOR01-06 erstatter NOR1-05 som erstatter NOR1-04 som erstatter NOR1-03 som erstatter NOR1-02 som til slutt erstatter NOR1-01. For å få fram dette i SPARQL, trenger vi bare å tilføye ett eneste tegn, nemlig * etter predikatet u:erstatter, slik: u:erstatter* ?erstatter .
Spørring 19

# Spørring 19
PREFIX d: <http://psi.udir.no/kl06/>
select  * where { 
	d:NOR01-06 u:tittel ?tittel ;
            u:grep-type ?type ;
            u:erstatter* ?erstatter .
    ?erstatter u:tittel ?erstTittel ;
            u:gyldig-fra ?gyldigFra ;
            u:grep-type ?erstType .
    FILTER(lang(?tittel) = "default" && (lang(?erstTittel) = "default"))
} ORDER BY ?gyldigFra

Her har vi tatt med gyldig fra-dato slik at vi kan sortere (ORDER BY) kronologisk etter ?gyldigFra. Vi får da hele rekken med erstatter-relasjoner med utgangspunkt i NOR01-06. Vi har kastet flyndre fra NOR01-05... Hvis du ikke vil ha med elementet du har tatt utgangspunkt i, erstatter du * med + i spørringen.

Du kan gjøre samme øvelsen med u:erstattes-av. Dette er den samme relasjonen, bare andre veien.

Spørringen over bruker et prinsipp i SPARQL, kalt "Property Path". Vi kan skrive
Spørring 20

# Spørring 20
PREFIX u: <http://psi.udir.no/ontologi/kl06/>
PREFIX d: <http://psi.udir.no/kl06/>
select  * where { 
d:NOR01-06 u:erstatter/u:erstatter/u:erstatter ?erstatter
}

for å "sprette" tre ganger, men utfordringen er at vi ofte ikke vet hvor mange "sprett" vi trenger for å komme til endes i denne erstatter-rekken. Derfor er altså u:erstatter* en smart løsning.

Eksempelet nedenfor er også et matnyttig eksempel på bruk av property path. Her spør vi etter fagkoder for en gitt læreplan:
Spørring 21

# Spørring 21
PREFIX u: <http://psi.udir.no/ontologi/kl06/>
PREFIX d: <http://psi.udir.no/kl06/>
select  * where { 
	d:NOR01-06 u:kompetansemaalsett/u:etter-fag/u:fagkode-referanser ?fagkoder .
} ORDER BY ?fagkoder

Legg merke til det som er markert med rødt i grafen nedenfor. Det illustrerer at egenskapene (propertiene) på rekke og rad i predikat delen (midten) av spørringen d:NOR01-06 u:kompetansemaalsett/u:etter-fag/u:fagkode-referanser ?fagkoder i spørringen utgjør en sti av proprties. Derav navnet "Proprty Paths". Fra læreplan til fagkoder

Hvis man skulle spørre om de samme uten å bruke Property Path, ville spørringen se slik ut:
Spørring 22

# Spørring 22
PREFIX u: <http://psi.udir.no/ontologi/kl06/>
PREFIX d: <http://psi.udir.no/kl06/>
select  * where { 
	d:NOR01-06 u:kompetansemaalsett ?kms .
        ?kms u:etter-fag ?opplaeringsfag .
        ?opplaeringsfag u:fagkode-referanser ?fagkoder .
} ORDER BY ?fagkoder





Sorteringskode for lærepelan, uavhengig av revisjon

Her får du to tips i ett:
Både hvordan du kan binde begge læreplantypene til én variabel,
og hvordan du kan sortere lista du da får etter revisjoner av hver plan

La oss si du har en spørring som lister opp alle læreplaner, uansett "reform" (altså både LK06- og LK20-læreplaner). Så ønsker du å sortere disse etter koden, slik at f.eks. revisjonene av læreplanen i Norsk kommer etter hverandre kronologisk slik:

  • NOR1-01
  • NOR1-02
  • NOR1-03
  • NOR1-04
  • NOR1-05
  • NOR01-06

Hver av disse kodene representerer hver sin revisjon av læreplanen i Norsk

Men problemet er at LK20-læreplanene har en ekstra posisjon etter bokstavdelen av koden (i dette tilfellet "NOR"), derfor vil den siste i rekken (NOR01-06) være den første i sorteringen.

Løsningen kan være å kombinere funksjonene BIND, BIND(IF og konkatinering CONCAT. Vi konstruerer rett og slett vår egen kode vi kan sortere på.

For eksempel slik:
Spørring 23

# Spørring 23
PREFIX u: <http://psi.udir.no/ontologi/kl06/>
SELECT DISTINCT ?kode ?tittel where { 
        [] a u:opplaeringsfag ; u:laereplan-referanse ?lp .
    ?lp u:kode ?kode ;
        u:tittel ?tittel ;
        u:grep-type ?grepType.
    BIND(SUBSTR(?kode, 1, 3 ) as ?grunnkode )
        BIND(
            IF(
               ?grepType = u:laereplan_lk20, ?kode,
               (CONCAT(?grunnkode, "0",(SUBSTR(?kode, 4, 5)) ))
            ) AS ?sorteringskode
        )
    FILTER (lang(?tittel) = "default")
} ORDER BY ?sorteringskode

Forklaring:

  1. I den første linja etter SELECT-linja spør vi etter verdien for egenskapen 'u:laereplan-referanse' i arrayet ([]) av 'opplaeringsfag'. Disse verdiene blir da bundet til variabelen ?lp.
    Siden 'opplaeringsfag' kan finne på å referere til både LK06- og LK20-planer, er dette en bra metode for å hente ut alle læreplaner, uavhengig av om de er av LK06- eller LK20-typen.
  2. De neste tre linjene henter vi ut ?kode, `?tittelog?grep-type``` (den siste for å kunne skille på LK06 og LK20 nedenfor)
  3. BIND(SUBSTR(?kode, 1, 3 ) as ?grunnkode ) Her henter vi ut de tre første tegnene i læreplankoden og kaller dem ?grunnkode.
  4. Den første linja under BIND(IF: Hvis læreplanens 'grep-type' er 'u:laereplan_lk20', henter vi ut ?kode, men:
  5. Hvis læreplanen ikke er av LK20-typen i linja over (underforstått, u:laereplan) konkatinerer vi slik av vi får ?grunnkode + "0", etterfulgt av resten av koden (?kode) slik at for eksempel "NOR1-01" blir til "NOR01-01", og dermed får den samme formen som LK-20-planen "NOR01-06".
  6. Resultatet av IF-setningen bindes til ?sorteringskode, og denne kan vi bruke i siste linja i spørringen: ORDER BY ?sorteringskode

Behandle datoer (gjøre om dato til årstall)

Først ser vi på en metode ved hjelp av variabel-tildeling med strengmanipulering og regulære uttrykk (regex). Det vi ønsker, er å gjøre om en dato som f.eks "2020-06-15T14:53:36.0809377" til "2020". I spørringen nedenfor vil det si at vi ønsker å få ut årstallet fra strengen vi får fra egenskapen "sist-endret" for læreplaner:
Spørring 24

# Spørring 24
PREFIX u: <http://psi.udir.no/ontologi/kl06/>
select ?lp ?dato (substr((?dato), 1, 4) AS ?aarstall) where  {
    ?lp a u:laereplan_lk20 ;
          u:sist-endret ?dato .
} ORDER BY DESC(?aarstall)
LIMIT 100

I spørringen over, skjer alt dette i select-delen av spørringen. Da trenger vi ikke å skrive "BIND(" osv, – bindingen skjer implisitt. Men vi kan også putte variabeltildelingen inne i selve spørringen. Da må vi sette "BIND(" foran uttrykket slik:
Spørring 25

# Spørring 25
PREFIX u: <http://psi.udir.no/ontologi/kl06/>
select ?lp ?dato ?aarstall where  {
    ?lp a u:laereplan_lk20 ;
          u:sist-endret ?dato .
    BIND (substr((?dato), 1, 4) AS ?aarstall)
} ORDER BY DESC(?aarstall)
LIMIT 100

Her er en annen variant som ikke består av variabeltildeling på den samme måten som i de to forrige spørringene, men som likevel gir det samme resultatet. Vi bruker funksjoner, knyttet til dato som dataformat (her fra skjemaet xsd:dateTime kombinert med YEAR-funksjonen):
Spørring 26

# Spørring 26
PREFIX u: <http://psi.udir.no/ontologi/kl06/>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
select ?lp ?dato ?aarstall
where {
    ?lp a u:laereplan_lk20 ;
        u:sist-endret ?dato
    BIND (xsd:dateTime(?dato) AS ?sistEndretDato) 
    BIND (YEAR(?sistEndretDato) as ?aarstall)    
} 
ORDER BY DESC(?aarstall)
limit 100 

Her passer vi på at ?sistEndretDato kommer ut som et gyldig datoformat (xsd:dateTime). Dermed kan vi behandle '?SitEndretDato' i neste linje som en faktisk dato, og videre gjøre det vi får av ?aarstall om til nettopp årstall (med BIND(YEAR-funksjonen).

Gyldige kryssløp det kan søkes på i et gitt tidspunkt (et litt vrient SPARQL case)

I forkant av hver vår får vi spørsmål om hvilke gyldige kryssløp elever, lærlinger og praksiskandidater kan søke på, enten fra et vg1- eller vg2-programområde til vg3. Like ofte som spørsmålet kommer, skaper dette litt utfordringer for oss som skal hjelpe til. Se den egne artikkelen som beskriver dette i detalj, og som er ment som en "note to self" og en dokumentasjon vi kan falle tilbake til neste gang spørsmålet kommer.




Fotnote 1:
Merk at status-verdiene ligger i samme navnerom som når vi spør etter 'url-data' for alle elementer i Grep, altså slik, enten

  • http://data.udir.no/kl06/v201906/status/status_publisert eller
  • http://data.udir.no/kl06/v201906/status/status_utgaatt.

Prefikset PREFIX d: <http://data.udir.no/kl06/v201906/> gjør at man da kunne tenke seg å skrive d:status/status_, men siden SPARQL ikke tillater skråstrek i prefikset, har vi derfor valgt å lage oss en egen prefiks for status:

  • PREFIX st: <http://data.udir.no/kl06/v201906/status/status_>

Da kan vi kalle på de to statusene ved enten å skrive st:publisert eller st:utgaatt.
(Se "*1" i teksten)

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