Finto, SearXNG ja ontologiset sanastot

You are currently viewing Finto, SearXNG ja ontologiset sanastot

Finto on ontologinen sanasto. Tai oikeammin se on palvelu, joka tarjoaa myös sanastoja. Jos kiinnostaa ontologian filosofinen aspekti, niin käytä hakua. Siinä ollaan metafyysissä asioissa, joista (minusta) kannattaa keskustella hämyisessä huoneessa suitsukkeiden tuoksussa teekuppi kädessä. Sanastojen kohdalla kyse on siitä miten jokin asia luokitellaan. Kirjaston luokittelu on ontologinen järjestelmä ja ontologinen sanasto tekee sanoille ja käsitteille hieman samaa.

Melkoisen usein ontologia jaetaan kahteen osaan: filosofiseen pohdiskeluun kaiken olevan perimmäisestä olemuksesta ja sen paikasta hierarkiassa, tai tietotekniikan ja varsinkin tekoälyjen tarpeeseen saada termille hierarkia ja luokitus.

Kun minä tarvitsen ontologisen sanaston määritelmän vaikkapa (koti)koirasta, niin minua ei kiinnosta sen filosofinen suhde kaikkeen olevaan — joka sekin on toki mielenkiintoinen aihe. Minua kiinnostaa silloin ontologian teknisempi puoli: rakenne + merkitys + suhteet.

Tapaus kotikoira

Biologiassa tieteellinen taksonomia on enemmänkin erikoistapaus, tai typistetty, ontologiaan liittyvästä luetteloinnista. Taksonomia esittää vain kategoriat, alaspäin tarkentuvan rakenteen.

Taso Luokitus
Domaini Eukaryota
Kunta Animalia
Pääjakso Chordata
Luokka Mammalia
Lahko Carnivora
Alalahko Caniformia
Heimo Canidae
Suku Canis
Laji Canis lupus
Alalaji Canis lupus familiaris

Jos tuohon ympätään mukaan enemmän ontologinen ote, niin taulukko voisi olla taksonomian lisäksi myös tätä:

Käsite / Taso Arvo / Esimerkki Suhde / Ominaisuus
Domaini Eukaryota aitotumallinen eliö
Kunta Animalia kuuluu eläinkuntaan
Pääjakso Chordata selkäjänteiset
Luokka Mammalia nisäkäs, jolla karva ja maidontuotanto
Lahko Carnivora petoeläin
Alalahko Caniformia ”koiramainen” petoeläin
Heimo Canidae koiraeläimet
Suku Canis sisältää sudet, kojootit, sakaalit ja koirat
Laji Canis lupus harmaasusi
Alalaji Canis lupus familiaris kesykoira, suden alalaji
Sukulaisuussuhteet Canis latrans (kojootit), Canis aureus (sakaali) läheisiä lajeja
Suhde ihmiseen Domestikoitu n. 50 000 vuotta sitten elää ihmisen lemmikkinä ja hyötyeläimenä
Ominaisuuksia Nelijalkainen, laumaeläin, sosiaalinen, vaihtuva turkki käyttäytymis- ja fyysiset piirteet
Ekologinen rooli Peto, raadonsyöjä, ihmisen apu vaikuttaa ekosysteemeihin ja kulttuuriin

Finto.fi

Finto on Kansalliskirjaston projekti, joka tarjoaa ontologia- ja käsitesanastoja. Vahvimmin ymmärtääkseni tietoteknisiin luokittelutarpeisiin, mutta voi niitä ihminenkin käyttää.

Minulla sen käyttö on enemmältikin uteliaisuutta jonkun käsitteen suhteen, ja mihin yhteyksiin se kuuluu. Useimmiten tarve on kylläkin englanninkielelle, mutta ei suomenkaan käyttö minulle ole poikkeuksellista.

Koska sanastoilla on niin vahva tietotekninen kysyntä, niin se useimmiten tarjoavat myös API:n. Ja jos tai kun syöte on sellaista JSON-muotoa, jota SearXNG pystyy käyttämään, niin sain työkalun hieman korostaa ontologisia hakuosumia.

Asennus

Minä käytän Kansalliskirjaston Fintoa. Se tarjoaa REST API:n, joten oletin sen olevan kohtuullisen helposti kytkettävissä SearXNG json engineen. Oletus oli väärä. Heidän käyttämänsä JSON ei ole sellainen, jota SearXNG söisi suoraan. Jos sinua kiinnostaa aihe syvemmältä, niin osannet vilkaista syötteen, mutta ongelma tuli sellaisessa kuin @context. Se listaa asiat, mutta json engine pysähtyy siihen, eikä anna tuloksia.

curl -s "https://api.finto.fi/rest/v1/search?vocab=ysa&query=koira&lang=fi" | jq

❯ Näytä koodi
{
  "@context": {
    "skos": "http://www.w3.org/2004/02/skos/core#",
    "isothes": "http://purl.org/iso25964/skos-thes#",
    "onki": "http://schema.onki.fi/onki#",
    "uri": "@id",
    "type": "@type",
    "results": {
      "@id": "onki:results",
      "@container": "@list"
    },
    "prefLabel": "skos:prefLabel",
    "altLabel": "skos:altLabel",
    "hiddenLabel": "skos:hiddenLabel",
    "@language": "fi"
  },
  "uri": "",
  "results": [
    {
      "uri": "http://www.yso.fi/onto/ysa/Y96272",
      "type": [
        "skos:Concept"
      ],
      "localname": "Y96272",
      "prefLabel": "koira",
      "lang": "fi",
      "vocab": "ysa"
    }
  ]
}

Olin omituisessa tilanteessa. Sain komentoriviltä osumat, myös testatessani haku-urlia, mutta engine antoi logeissa 200 OK vastauksen ja hakutuloksissa ei löydy. Minulla kesti taasen useampi tunti, kun ensin yritin ymmärtää mitä tapahtuu ja sen jälkeen miten korjata tilanne.

Minun piti saada siivottua @context pois. Tai rakentaa kokonaan uusi engine, joka ylittää taatusti vaatimattomat kykyni. Pitkä tarina lyhyesti on, että tuo poisto on tehtävä web-serverillä, ei SearXNG:ssä, koska en osaa ja siihen minulla olisi kaksi tapaa:

  • käyttää Nginxin javascript moduulia (njs) — tuo kaatui siihen, että en saanut sitä asennettua versio- ja riippuvuussuhteiden takia, joka ilmeisesti oli seuraus itse käännetystä Nginxistä ja että yritin asennusta väärästä reposta. En riidellyt tuon kanssa enempää, koska en kuitenkaan olisi osannut tehdä vaadittavaa JS-scriptiä.
  • käyttää Plesk-serveriä välissä ja siivota asiat siellä hieman perinteisempään tapaan.

Ketju olisi siis

  • Asiakas tulee Nginxiin porttiin 443 ja tekee normaalisti vhostissa haku.eksis.eu hakunsa
  • Haku lähtee searxng-containeriin ja annetaan enginelle
  • Engine lähettää pyynnön
    • hostin sisäiselle Nginx vhostille, joka proxyaa sen Flaskille, tai
    • sisäisen verkon kautta
  • Flask-containerista pyyntö lähetetään finto.fi ja saadaan json, josta python-scripti siivoaa @context pois kiusaamasta

Pientä pyörimistä, mutta kaikkiaan tarvitaan vain joko yksi proxymääritys lisää sisäistä liikennettä hoitavassa Nginxin vhostissa — minulla on se jo valmiina, koska tarvitsin moisen omille WordPresseilleni — tai containereihin parin rivin networks-määrittely. Plus yksi docker container ja python scripti.

Kaikki muu ratkesi kohtuullisen kivuttomasti Googlella, mutta koska en ole koodari, vaan kopypeistaaja, niin pythonin kanssa meinasi tulla itku.

Asennus alkaa tarvittavan hakemiston luomisella: mkdir /opt/finto-adapter. Ja kuten aina, niin polku ja nimi on vapaavalintaiset.

Tarvitaan myös kolme tiedostoa:

  • docker-compose.yml käynnistämään containerin
  • Dockerfile luomaan gunicornin containerin sisäistä liikennettä varten ja Pleskin, jossa työt tekevät python-scripti pyörii
  • app.yml joka korjaa JSON vastauksen (ja on ehkä samalla se Plesk; vaikka käytän Pleskiä muuallakin, niin se on minulle hyvin mystinen konsepti)

Container

Tarvitaan container:

❯ Näytä koodi
nano /opt/finto-adapter/docker-compose.yml
❯ Näytä koodi
services:
  finto-adapter:
    container_name: finto-adapter
    build: .
    restart: unless-stopped
    networks: [searxnet]

networks:
  searxnet:
    external: true

Network täytyy lisätä myös /opt/searxng/docker-compose.yml:

❯ Näytä koodi
services:
  searxng:
    ...
    networks: [searxnet]
    ...

networks:
  searxnet:
    external: true

Jos Valkey on käytössä, niin myös se tarvitsee networks: [searxnet] maininnan.

Verkon nimi on vapaavalintainen. Minä käytän tuota vain siksi, että se liittyy SearXNG:n toimintaan ja on… no, net.

Kun käytetään verkkoa, niin ports: saattaa muuttua tarpeettomaksi. Siksi sitä ei ole määritelty containerissa finto-adapter. Mutta SearXNG:n olisi perinjuurin vaikeaa kommunikoida Nginxin kanssa ilman portteja — paitsi ehkä jos Nginx on myös docker-versio. Siinä siirrytään minulle täysin tuntemattomalle alueelle.

Verkko täytyy myös luoda. Tämä on ymmärtääkseni ainutkertainen komento, eikä tarvita toiste, ellei varsinaisesti ja tarkoituksella poista networksiä.

❯ Näytä koodi
docker network create searxnet

Nginx

Networksiä ei ole pakko luoda. Pyynnön voi tehdä myös Nginxin vhostin kautta. En uppoa suuremmin sen varsinaiseen rakentamiseen, koska se on tehty jutussa Searxng ja WordPress.

Kannattaako tämä suunta vai networks? Kokeilin kumpaakin. Molemmissa on komponentti, joka voi mennä rikki. Joko Nginxin vhost ja sen yhteydet, tai dockerin network. En minä tiedä kumpi on suurempi riski. Mutta näiden välillä on kolme minulle jossain määrin merkityksellistä eroa.

Nginxin vhost tarjoaa minulle sellaisen login, jota osaan suoraan lukea ja josta näen historian. Helpottaa ongelmien selvittämistä. Mutta kuinka usein tarvitsen moista? En kovinkaan usein, mutta kun tarvitsen, niin koen tarvitsemani. Dockerin kohdalla koen olevani samassa tilanteessa kuin Varnishin kanssa, jossa tilanteessa ”mitä hemmettiä äsken tapahtui” tai ”mikä hajosi viime yönä” minulla ei ole mitään suoraa logia ihmettelyä varten. Kaikki on kyettävä toistamaan ja seuraamaan livenä.

Tai sitten en vaan osaa.

Toinen on mahdottomuus käyttää curlia testaamisessa mihinkään muuhun kuin tutkittaessa ymmärtääkö containerit liikenteen hostin suunnasta. SearXNG:n image ei tunne curlia (eikä nanoa tai muuta editoria, joten cat on ainoa tapa tutkia tekstiä). Mutta wget löytyy, joten containerin sisällä täytyy käyttää sitä.

Kolmas tekijä on nopeus. Molemmat toimivat hostin sisällä, jolla ei ole varsinaista tolkutonta kuormaa ja vapaata rammia löytyy. Ytimet eivät ole todellakaan parasta kastia, mutta niitä pitäisi olla sinällään riittävästi, eikä mikään logitus paljasta kiusaa tekevää pullonkaulaa. Sitä en tiedä miten Nginx hoitaa oman sisäisen työnjakonsa hostien välillä.

Networks on nopeampi. Nyt ei puhuta mistään isoista viiveistä, mutta silminnähtävistä. Sen verran selvästä erosta, että purin Nginx-virityksen ja siirsin yhteydet networksille.

Jos haluaakin tehdä asiat Nginxin kautta, niin tämä on oma vhost searxng-internal.conf. Joten muuta oleelliset asiat, älä kopioi suoraan. Jätin siitä pois WordPress-osat.

❯ Näytä koodi
server {
    listen 172.18.0.1:9000;
    server_name _;

    # Lokit erilliseen tiedostoon
    access_log /var/log/nginx/xng-access.log main;
    error_log  /var/log/nginx/xng-error.log;


    location = /finto/search {

        # salli vain searxng:n IP tai localhost
        allow 127.0.0.1;
        allow 172.18.0.0/16;   # docker-verkko jos tarpeen
        deny all;

        proxy_pass http://127.0.0.1:9001/finto/search;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_read_timeout 15s;
    }

    # Kaikki muu 403:lla kiinni
    location / {
        return 403;
    }
}

Jep. Koska tuo vhost on sisäinen, niin IP-rajoitukset ovat turhia. Mutta on hyvää käytäntöä laittaa ne, niin siitä tulee tapa. Miksikö sallin niin hostin localhostin kuin dockerin/containerin käyttämät? Koska olen ihan pihalla siitä mikä IP milloinkin liikkuu docker-maailmassa.

Muuta enginen urlissa domain oikeaksi. Se, että mikä on ”oikea” riippuu siitä mitä kaikkea olet tehnyt containerissa, mutta tässä esimerkissä se on 172.18.0.1:9000 eli containerin hostiin näkyvä IP. Bridgen IP ei toimi, vaikka monet dokumentit sitä esimerkkinä käyttävätkin.

Plesk

Tämä todellakin kirjoitetaan isolla alkukirjaimella. Se ei ole typo tai laitteen automaattisesti asettama iso alkukirjan. Kantapään kautta opittu asia.

❯ Näytä koodi
nano /opt/finto-adapter/Dockerfile
❯ Näytä koodi
FROM python:3.12-slim
RUN pip install --no-cache-dir flask requests gunicorn
WORKDIR /app
COPY app.py /app/
CMD ["gunicorn","-b","0.0.0.0:8080","app:app"]
  • sisäinen järjestelmä, niin en jaksanut alkaa pohtimaan IP-osoitteita

Ja itse scripti:

❯ Näytä koodi
nano /opt/finto-adapter/app.yml
❯ Näytä koodi
from flask import Flask, request, jsonify
import requests

app = Flask(__name__)

@app.get("/finto/search")
def finto_search():
    upstream = "https://api.finto.fi/rest/v1/search"
    r = requests.get(upstream, params=request.args, headers={"Accept":"application/json"}, timeout=10)
    r.raise_for_status()
    data = r.json()
    data.pop("@context", None)          # tämä on se joka sotkee json_engineä
    if "results" not in data or not isinstance(data["results"], list):
        data["results"] = []
    return jsonify(data)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080)

Container, ja nuo muutkin, täytyy käynnistää:

❯ Näytä koodi
docker compose up -d --build

Sammuttamiselle ei ole suuremmin tarvetta, mutta minä teen sen aina ja poikkeuksetta ennen uudelleenkäynnistystä: docker compose down. Käynnistäminen --build kanssa tarvitaan, jos app.py tai Dockerfile on muokattu. Jos on muuttanut docker-compose.yml niin tavallinen käynnistys riittää. Joten tässäkin menen aina varovaisuus edellä ja jokainen kerta uudelleenkäynnistys tehdään docker compose up -d --build komennolla.

json engine

Minä en ole aivan varma tämän ”oikeellisuudesta” ja vaikuttaako oikeastaan mikään mihinkään muuhun kuin hiukan UI puolelle. Silloin url on se ainoa määräävä asia kyselylle. Finto kun ei palauta varsinaisesti sisältöä varsinaisena sivuna, jota esiteltäisiin hakutuloksissa. Esitettävä sisältö pitäisi rakentaa enginessä, ja sellaista en osaa tehdä.

Tämä hakee Fintosta käyttäen sanastoa YSO (Yleinen Suomalainen Ontologia):

❯ Näytä koodi
- name: finto.yso
    #inactive: true
    engine: json_engine
    shortcut: yso
    categories: [general, science]
    enable_http: true
    search_url: http://finto-adapter:8080/finto/search?vocab=yso&query={query}&lang=fi
    results_query: results
    title_query: prefLabel
    url_query: uri
    content_query: vocab
    weight: 2

Koska networks on käytössä, niin ei tarvitse sinällään miettiä IP-osoitteita, mutta hostin, tässä containerin, nimi tarvitaan. Se on se mikä on asetettu container_name: kohdassa, mutta sen saa selville tälläkin:

❯ Näytä koodi
docker compose ps -a

Lopuksi searxng:n uudelleenkäynnistys ja logi kuuntelemaan:

❯ Näytä koodi
docker compose down && docker compose up -d && docker logs -f searxng

Useampi engine

Minulla on käytössä Finton suuntaan kolme engineä: ilman määriteltyä sanastoa, YSO ja YSO-paikat. Haku kaikista sanastoista on minulle oikeastaan tarpeeton, koska yleensä YSO riittää. Se on kuitenkin jäänyt, vaikka pidentääkin hakutuloksia.

Se, että mistä Finton tarjoamasta sanastosta haetaan, kerrotaan haku-urlin kohdassa vocab=

Tämä hakee kaikista, koska sanastoa ei ole määritelty:

❯ Näytä koodi
search_url: http://finto-adapter:8080/finto/search?vocab=&query={query}&lang=fi

Tämä taasen käyttää sanastoa YSO-paikat:

❯ Näytä koodi
search_url: http://finto-adapter:8080/finto/search?vocab=yso-paikat&query={query}&lang=fi

Tietysti myös shotcut: täytyy vaihtaa.

Testaaminen

Nopein tapa testata on tietysti tehdä haku !bang avulla, niin muut tulokset eivät sotke.

❯ Näytä koodi
!yso koira

Ja vastaus on varmaan tällainen:

Aivan. Ei se tuon kummallisempaa hakutulosta anna.

Jos saat

  • timeoutin, niin joku ei kuuntele jossain tai haku-url on väärin
  • jos tulee http-error, niin se on luultavasti 403 (siksi container jätettiin logittamaan) ja silloin hostin määrityksissä on jotain pielessä

Siinä vaiheessa alkaa selvittäminen, ja aivan ensimmäiseksi tarkistetaan molempien containerien tiedostot, ja että ne ylipäätään ovat käynnissä. Minulla yleisin syy on typo jossain kohtaa.

Tällä voi kokeilla searxng-containerin sisältä (vaihda url siihen mitä käytät):

❯ Näytä koodi
docker exec -it searxng sh -lc 'wget -qO- "http://finto-adapter:8080/finto/search?vocab=ysa&query=koira&lang=fi" | head'

Vastauksen pitäisi olla tällainen:

❯ Näytä koodi
{"results":[{"lang":"fi","localname":"Y96272","prefLabel":"koira","type":["skos:Concept"],"uri":"http://www.yso.fi/onto/ysa/Y96272","vocab":"ysa"}],"uri":""}

Esimerkkinä tietysti perin typerä, koska tuo on toimivasta systeemistä ja toimivuuden näkisi jo UI:n kautta. Mutta jos mitään ei tule, niin tarkista ainakin search_url.

Jos ratkaisu ei ole noin helppo, niin alkaa savotta. Minä en tunne näiden sielunelämää niin vahvasti, että pystyisin selvittämään miten debuggausta kannattaa lähteä rakentamaan.

Ja paljon riippuu siitäkin millaista ratkaisua on ylipäätään käytetty. Hakeeko SearXNG esimerkiksi Nginxin kautta, IP:llä containerien välillä ja networksiä hyödyntämämällä. Mutta ensin täytynee päättää toimivatko eri palvelut ylipäätään tai oletettavasti toimivat, koska silloin on joku tolpanväli ilman lankaa. Tai jos verkot ja yhteydet pitäisi olla kunnossa, niin jokin palvelu ei tee sitä mitä pitäisi.

Hakukoneiden käyttö tulee tuossa tutuksi.

Kannattaako Finton käyttö enginellä?

Se mikä kannattaa ja mikä ei, on tarve- ja makukysymys. Jos minulla on akuutti ja aito tarve ontologiselle sanastolle, niin menen suoraan Fintoon. Tai jonnekin muualla, en minä aina suomea käytä. Mutta jos et milloinkaan tarvitse sanojen luokittelua, niin et tarvitse kuin uteliaisuutta varten.

Omassa käytössä 95 % on uteliaisuutta ja 4 % on jotain, joka haiskahtaa tarpeelta tai käsitykseltä tarpeesta. Se puuttuva 1 % on niitä tapauksia, joissa tökkään osumaa ja kun se aukeaa, niin en tiedä miksi moisen aukaisin. Kaipa se on eräänlaista keittiön ovi -oireilua, jota myös uuden tilan ongelmaksi voidaan kutsua.

Jos et tunnista millään tasolla sanojen luokittelun tarvetta (tai uteliaisuutta), niin älä asenna. Jos hakukoneella on muitakin käyttäjiä, niin ehkä sen voi asentaa. Ainahan enginen saa otettua pois käytöstä hakukoneet-välilehdessä — asia, jonka jokaisen olisi syytä muistaa ja joka ilmeisesti pitäisi erikseen opettaa käyttäjille. Aivan kuten somessa syöte kannattaa laittaa sellaiseksi, että ei ihan joka päivä vituta sen lukeminen, niin hakukoneet kannattaa rajoittaa sellaisiin, joista saa haluamansa ja tarvitsemansa vastaukset (niissä rajoissa kuin mitä hakukoneet ylipäätään pystyvät toimimaan).

Jakke Lehtonen

Teen B2B-markkinoille sisällöntuottoa sekä UX-testauksia. Samaan liittyy myös koulutukset yrityksille ja webmaailman kanssa muutoin painiville. Serverien sielunelämää on joutunut ohessa opettelmaan. Toinen puoli toiminnasta on koirien ravitsemuksen ja ruokinnan suunnittelua sekä varsinkin omistajien kouluttamista hoitamaan koiriaan oikein ja vielä paremmin. Profiili: Jakke Lehtonen

Keskustele foorumilla Katiskan foorumi

WordPressin kommentit: