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

Denne siden er en del av SPARQL-kurs

<-- del 4   del 6 -->

Del 5, Instruksjonen BIND og dens funksjoner

Overskriften høres ganske pretensiøs ut, og den er kanskje det, rett ut sagt, men vi skal ta det, skritt for skritt. Denne delen er det ikke meningen at vi i dette kurset skal ha i fingrene, slik som de forrige. Vi kan heller se på det som et lite oppslagsverk vi kan ty til ved behov. Jeg gir noen eksempler på BIND-instruksjoner du kan leke deg med (vri og endre på), og bruke eksemplene som en liten verktøykasse. Det kan også hende du kan finne ressurser på nettet som bruker BIND-instruksjoner som møter dine behov som kanskje ikke blir besvart her. Meningen her er bare å gi deg eksempler på hvilke type ting du kan gjøre med BIND.

BIND er en SPARQL-instruksjon som brukes for å tildele verdien av et uttrykk til en ny variabel i en SPARQL-forespørsel. Dette er nyttig når du vil beregne en verdi eller gjøre en operasjon på eksisterende data og deretter bruke denne nye verdien i resten av forespørselen.

BIND IF

Instruksjonen BIND kan ha mange funksjoner, og IF er en av dem. Du har kanskje hørt om IF-setninger som brukes i mange sammenhenger med programmering.

La os ta et praktisk eksempel fra Grep. Jeg ønsker å liste opp alle læreplanene i LK20 sammen med titlene. Men så vet jeg at det finnes 16 læreplaner som er fastsatt på nordsamisk. I de tilfellene, ønsker jeg å heller vise bokmålsoversettelsen av tittelen, sien jeg ikke forstår samisk.

For å få til dette, må vi ty til BIND, sammen med funksjonen IF:
Hvis fastsatt språk er nordsamisk, gi meg tittelen på bokmål. Ellers, bare gi meg default-språket (tittelen på default-språket for de læreplanene som er fastsatt på nordsamisk, er nemlig på nordsamisk). Da kan vi gjøre slik:

PREFIX u: <http://psi.udir.no/ontologi/kl06/>
PREFIX d: <http://psi.udir.no/kl06/>
select distinct * where { 
    ?lp a u:laereplan_lk20;
        u:fastsatt-spraak ?fastsattSpr ;
        u:tittel ?lpTittel
    
 # BIND nedenfor (kombinert med filteret nedenfor) tvinger nordsamiske titler til å vises i bokmål
 BIND (
        IF(?fastsattSpr = d:sme, "nob" , "default"
            ) AS ?spraak
    )   
    FILTER (lang(?lpTittel) = ?spraak)   
} ORDER BY DESC(?spraak)

Vi binder altså noe som til slutt skal bli variabelen ?spraak (AS ?spraak), og det er denne vi skal bruke i filteret i stedet for å skrive FILTER (lang(?lpTittel) = "default"). Vi skriver heller FILTER (lang(?lpTittel) = ?spraak). Og leser vi IF-setningen, ser vi at det enten blir "default" eller "nob".

Vi tar IF-setningen linje for linje, trinn for trinn:

        IF(?fastsattSpr = d:sme, "nob" , "default"
            ) AS ?spraak
    )
  1. hvis ?fastsattSpr er nordsamisk, putt "nob" i variabelen ?spraak, hvis ikke, skriv heller "default"`
  2. AS ?spraak er det vi binder if-setningen til, og er de verdiene (etter reglene vi satt i forrige linje) skal putte i filteret lengre nede i spørringen

BIND CONCAT

Her er et eksempel som viser hvordan BIND CONCAT fungerer (og vi forlater Grep et øyeblikk):

PREFIX foaf: <http://xmlns.com/foaf/0.1/>

SELECT ?fulltNavn
WHERE {
  ?person foaf:firstName ?fornavn .
  ?person foaf:lastName ?etternavn .
  BIND(CONCAT(?fornavn, " ", ?etternavn) AS ?fulltNavn)
}

Instruksjonen BIND kan ha mange funksjoner, og eksempelet over bruker funksjonen CONCAT.
La oss starte med ytterpunktene av BIND-delen: `BIND(CONCAT(det som skjer i dette parenteset) AS ?fulltNavn), eller sagt med ord; konkatener det som er inni parentesen, og bind det til en ny variabel vi kaller ?fulltNavn.

Så har vi det som skjer inni parentesen:
(?firstName, " ", ?lastName)
Med ord: Fornavn, mellomrom og etternavn. Det er det vi skal konkatenere. Du kan lese mer generelt om konkatenering på Wikipedia.

Så kan vi uppe gamet ved å snakke om BIND IF, og i tillegg bestemme hvilken datatype vi ønsker å få ut (IRI):

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 .
    ?lp u:kode ?kode ;
        u:grep-type ?grepType.
    BIND(
        IF(?grepType = u:laereplan_lk20, IRI(CONCAT("https://www.udir.no/lk20/", ?kode)),
            IRI(CONCAT("https://www.udir.no/kl06/)", ?kode))) AS ?lpUrl
        )
}

Vi tar det, trinn for trinn, linje for linje etter SELECT:

  1. vi spør etter [] (hva som helst i det tomme arrayet (lista)) som er opplæringsfag
  2. som har lærpelan-referanse (som vi binder til variabelen ?lp). Her vil vi både få læreplaner av typen u:laereplan(LK06-planer) og u:laereplan_lk20 (LK20-planer)
  3. så slår vi opp disse læreplanene og ber om kode
  4. og hvilken grep-type de er

Så begynner moroa med BIND:
Vi skal binde noe som til slutt skal være en ny variabel, kalt ?lpUrl (altså URLen til læreplanene). Med dette menes URLene til alle læreplanene på udir.no.

Dette trenger en liten forklaring. På udir.no vises alle læreplanene som egne nettsider, og hver læreplan har sin egen URL. I Kunnskapsløftet LK06, begynner alle læreplan-URLene med https://www.udir.no/kl06/ etterfulgt av koden til læreplanen. I Kunnskapsløftet LK20, fagfornyelsen, begynner alle læreplan-URLene med http://www.udir.no/lk20/ etterfulgt av koden til læreplanen. Disse URLene finnes ikke i Grep, men det er det som er trikset med hele denne spørringen. Siden vi kjenner mønsteret, kan vi gjenskape det her, for er det noe vi har i Grep, så er det koden til læreplanene, uansett om de er av LK06- eller LK20-typen. Vi kjenner også typen til de ulike læreplanene (pkt. 4 i punktlista over).

Så altså – BIND IF
IF-setningen er bygd opp slik:

    BIND(
        IF(?grepType = u:laereplan_lk20, IRI(CONCAT("https://www.udir.no/lk20/", ?kode)),
            IRI(CONCAT("https://www.udir.no/kl06/)", ?kode))) AS ?lpUrl
        )

Hvis ?grepType er u:laereplan_lk20, så skal vi konkatenere slik at vi får ut "https://www.udir.no/lk20/", etterfulgt av ?kode
Så neste linje: Hvis ikke den er av LK20-typen, skal vi konkatenere slik at vi får "https://www.udir.no/kl06/", etterfulgt av ?kode. I begge tilfeller vil vi at ?lpUrl skal være en klikkbar lenke i resultatet (IRI).

Det at vi blander inn grep-typen "opplæringsfag" i starten av spørringen, er bare et triks for å lokke fram begge typene læreplaner til én og samme variabel, ?lp, for egenskapen u:laereplan-referanse peker til begge typene. Alle læreplaner har en "baklengs-kobling" til opplæringsfag.

BIND SUBSTR

Instruksjonen BIND kan som sagt ha mange funksjoner, og eksempelet nedenfor bruker vi en funksjon som heter "substring", SUBSTR – altså en del av en streng.

La oss bruke et praktisk eksempel fra Grep.

Vi vet at koden til LK06-læreplaner er bygd opp slik: [tre bokstaver][ett tall][bindestrek][to tall], som i "NOR1-05".
Koden til LK20-planer er derimot bygd opp slik: [tre bokstaver][to tall][bindestrek][to tall], som i "NOR01-06". Med alle matematikk-planene som kom med fagfornyelsen, slapp vi opp for siffer i tallet etter de tre første tegnene, så vi måtte legge til et siffer

Anekdote:
Tallene før bindestreken i læreplankodene representerer hvilken læreplan det er snakk om NOR1 eller NOR01 er fellesfaget Norsk. Tallene etter bindestreken representerer hvilken revisjon det er snakk om. NOR1-01 er den første revisjonen, etterfulgt av NOR1-02, NOR1-3, NOR1-04, NOR1-05 og til slutt NOR01-06 (og her smatt nullen inn i forbindelse med fagfornyelsen og læreplanen bytter i tillegg grep-type). Koder som begynner med NOR3 eller NOR03 er læreplan for norsk med elever med samisk som førstespråk, og er altså en helt annen læreplan i NOR-serien. Så fikk du litt læreplankodehistorikk fra Grep på kjøpet...

Så til det praktiske behovet vi ønsker å tilfredsstille med en spørring. Vi ønsker å liste opp læreplanene alfabetisk etter læreplan-koden, slik at vi lett kan følge hver læreplan-revisjon. Men det blir ikke lett hvis LK06- og LK20-kodene er bygd opp forskjellig. Hva om vi i spørringen tvinger LK06-kodene til å ha samme mønster som LK20-kodene – alstå med et "0" rett etter de tre bokstavene? Da kan vi sortere slik at NOR01-06 (som egentlig er NOR1-05) kommer etter NOR01-5. Hvis ikke, får vi dette resultatet (og her har jeg markert "NOR1-" og vi ser at NOR01-06 ikke ligger etter NOR1-05 slik vi ønsker i dette eksempelet.
image

Her er hva vi kan gjøre:

PREFIX u: <http://psi.udir.no/ontologi/kl06/>
SELECT DISTINCT ?kode ?sorteringskode  ?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 )
        # Lager alle lp-koder til samme format så de kan sorteres etter revisjon innenfor hver læreplan
        BIND(
            IF(
               ?grepType = u:laereplan_lk20, (SUBSTR(?kode, 1, 8)),
               (CONCAT(?grunnkode, "0",(SUBSTR(?kode, 4, 5)) ))
            ) AS ?sorteringskode
        )
    FILTER (lang(?tittel) = "default")
} ORDER BY ?sorteringskode
OFFSET 720   # bare for at vi lett finner igjen NOR01-eksemplene i resultatet

Vi tar det linje for linje, trinn for trinn etter SELECT:

  1. Alt inni det tomme/anonyme arrayet [] som er opplæringsfag
  2. og som har u:laereplan-referanse, binder vi til variabelen ?lp .
  3. Vi slår opp disse læreplanene i ?lp og henter koden ?kode
  4. og henter dennes tittel
  5. og grep-type (som enten er u:laereplan eller u:laereplan_lk20
  6. BIND(SUBSTR(?kode, 1, 3 ) as ?grunnkode ): Tar en substring (fra og med 1. tegn til 3. tegn) av ?kode og binder det til ?grunnkode
  7. BIND(: parentesen begynner her, og slutter før filteret nedenfor
  8. IF(: parentesen for selve IF-setningen begynner her, og slutter rett før AS ?sorteringskode
  9. Hvis ?grepType er av LK20-typen, ta tegn 1 til 8
  10. ...og konkatener de 8 første tegnene (som i prinsippet er alle)
  11. hvis ?grepType ikke er av LK20-typen, ta grunnkoden, og legg til "0" foran tegnene fra 4. til 5. posisjon¨
  12. og bind alt dette til variabelen ?sorteringskode
  13. Puhh - så var det filteret: ?tittel skal være i default-språket
  14. og vi sorterer etter ?sorteringskode
  15. OFFSET 720: ...og vi hopper i resultatsettet så resultatet begynner fra linje 721, slik at vi lett finner igjen Norsk-planene fra forklaringene over.

Da ser vi at NOR01-06 kommer etter NOR1-05.
image

Andre funksjoner BIND kan ha:

Her er en liste over noen vanlige funksjoner som kan brukes innenfor BIND-instruksjonen i SPARQL. Disse funksjonene hjelper deg med å utføre forskjellige operasjoner på dataene dine. Vi har delt dem inn i kategorier, og vi tar også med de vi har vært innom:

Strengfunksjoner

CONCAT: Setter sammen flere strenger til én streng.

BIND(CONCAT(?firstName, " ", ?lastName) AS ?fullName)

STRLEN: Returnerer lengden på en streng.

BIND(STRLEN(?str) AS ?length)

UCASE: Konverterer en streng til store bokstaver.

BIND(UCASE(?str) AS ?upperCase)

LCASE: Konverterer en streng til små bokstaver.

BIND(LCASE(?str) AS ?lowerCase)

SUBSTR: Henter en delstreng fra en streng.

BIND(SUBSTR(?str, 1, 3) AS ?substring)

REPLACE: Erstatter deler av en streng med en annen streng.

BIND(REPLACE(?str, "old", "new") AS ?newStr)

Matematiske funksjoner

*+, -, , /: Utfører grunnleggende matematiske operasjoner.

BIND(?a + ?b AS ?sum)
BIND(?a - ?b AS ?difference)
BIND(?a * ?b AS ?product)
BIND(?a / ?b AS ?quotient)

ABS: Returnerer den absolutte verdien av et tall.

BIND(ABS(?number) AS ?absoluteValue)

CEIL: Returnerer det minste heltallet som er større enn eller lik et tall.

BIND(CEIL(?number) AS ?ceiling)

FLOOR: Returnerer det største heltallet som er mindre enn eller lik et tall.

BIND(FLOOR(?number) AS ?floor)

ROUND: Avrunder et tall til nærmeste heltall.

BIND(ROUND(?number) AS ?rounded)

Datofunksjoner

NOW: Returnerer dagens dato og tid.

BIND(NOW() AS ?currentDate)

YEAR: Returnerer året fra en dato.

BIND(YEAR(?date) AS ?year)

MONTH: Returnerer måneden fra en dato.

BIND(MONTH(?date) AS ?month)

DAY: Returnerer dagen fra en dato.

BIND(DAY(?date) AS ?day)

HOURS, MINUTES, SECONDS: Returnerer timer, minutter og sekunder fra en dato.

BIND(HOURS(?dateTime) AS ?hours)
BIND(MINUTES(?dateTime) AS ?minutes)
BIND(SECONDS(?dateTime) AS ?seconds)

Logiske funksjoner

IF: Evaluerer en betingelse og returnerer én verdi hvis betingelsen er sann, og en annen verdi hvis betingelsen er falsk.

BIND(IF(?age > 18, "Adult", "Minor") AS ?status)

COALESCE: Returnerer den første ikke-null verdien fra en liste med argumenter.

BIND(COALESCE(?optionalValue, "DefaultValue") AS ?value)

EXISTS / NOT EXISTS: Sjekker om et mønster eksisterer eller ikke eksisterer.

BIND(EXISTS { ?s ?p ?o } AS ?exists)

Andre funksjoner

IRI: Konstruerer en IRI fra en streng.

BIND(IRI(?uriString) AS ?iri)

STR: Konverterer en IRI til en streng.

BIND(STR(?iri) AS ?str)

LANG: Returnerer språktaggen for en tekstverdi.

BIND(LANG(?label) AS ?language)

DATATYPE: Returnerer datatype-IRI for en verdi.

BIND(DATATYPE(?value) AS ?dataType)

Dette er noen av de mest brukte funksjonene du kan bruke i BIND-instruksjonen i SPARQL. Ved å kombinere BIND med disse funksjonene, kan du utføre komplekse datamanipuleringer og få de nøyaktige resultatene du trenger i ulike situasjoner.

Denne delen har ikke oppgaver, men her er det stoff nok til å prøve seg fram med egne eksempler.



Oppsummering, Del 5

Denne delen av kurset er tenkt som et oppslagsverk for BIND-instruksjonen i SPARQL. Vi har vært innom hvordan BIND brukes for å tildele verdier til nye variabler, og hvordan dette kan brukes sammen med funksjoner som IF, CONCAT og SUBSTR for å manipulere og analysere data. Praktiske eksempler illustrerer hvordan BIND kan tilpasses ulike behov. Målet er å gi deg en verktøykasse med eksempler du kan eksperimentere med i dine egne SPARQL-spørringer.

I den neste delen skal vi også inn i en artig funksjonalitet – vi skal kaste flyndre med SPARQL.




<-- del 4   del 6 -->
⚠️ **GitHub.com Fallback** ⚠️