Oskar Dudycz

Pragmatycznie o programowaniu

Ciekawe narzędzie - Algolia, po co i jak używać

2021-03-29 oskar dudyczNarzędzia

cover

Cześć!

W zeszłym tygodniu na swoim blogu napisałem o tym jak tworzyć dokumentację, żeby jej utrzymywanie nie bolało (zobacz tutaj). Dokumentacja zresztą jest to coś co było jednym z głównych zadań od kiedy dołączyłem do Event Store. Żmudna, mrówcza robota, ale trzeba ją zrobić. Dokumentacji dla EventStoreDB brakowało sporo. Teraz jest dużo lepiej, krok po kroku zasypujemy doły. Niedawno zasypałem jeden z głębokich dołów - wyszukiwanie treści.

W Event Store używamy VuePress jako silnika do dokumentacjci. Jest to statyczny generator HTML napisany w Vue. Ogólnie super sprawa. Ładnie wygląda, fajnie się rozszerza - #polecam. VuePress posiada również własny wbudowany silnik wyszukiwania treści. Niestety jest bardzo prosty - #niePolecam. Przez jakiś czas go używaliśmy, bo coś lepszy taki niż żadny, no ale umówmy się - dokumentacja bez dobrego wyszukiwania to jednak słabo.

Po krótkim research stwierdziliśmy, że użyjemy gotowego narzędzia - Algolia. Jest to rozwiązanie SASSowe pozwalające na indeksowanie treści na stronie oraz zaawansowane wyszukiwanie tekstowe. Także załatwia wszystkie podstawowe rzeczy typu wyszukiwanie bez rozróżniania wielkość liter, korekta literówek, szukanie fraz itd. Fakt, że jest to rozwiązanie SASS powoduje, że nie trzeba do tego stawiać jakiejś architektury, ani martwić się o kwestie operacyjne. Po prostu działa, i to szybko

Oczywiście nie jest tak idealnie, że nic nie trzeba robić. W teorii Algolia ma program, do którego można się zgłosić, że sami mogą indeksować treść Twojej strony. Ma on jednak ograniczone opcje do dostosowania do swoich potrzeb. Można samemu sobie skonfigurować “scrapera”, który przeleci się po naszej stronie i zindeksuje jej treść z zadanymi przez nas kryteriami. Jak to zrobić? Najłatwiej przy pomocy obrazu Dockera.

Zanim przejdziemy do Dockera to najpierw zdefiniujmy konfigurację. Jest ona zrobiona poprzez JSON. Np. plik config.json wyglądać może tak:

{
  // nazwa indeksu w Algolia
  "index_name": "scraper-test",
  // url strony, którą będziemy indeksować
  "start_urls": ["https://developers.eventstore.com/"],
  // selectory, każdy lvl to nagłówek
  "selectors": {
    "lvl0": ".sidebar h3.version",
    "lvl1": {
      "selector": ".sidebar-heading.open span",
      "global": true,
      "default_value": "Documentation"
    },
    "lvl2": {
      "selector": ".content__default h1",
      "strip_chars": "#"
    },
    "lvl3": {
      "selector": ".content__default h2",
      "strip_chars": "#"
    },
    "lvl4": {
      "selector": ".content__default h3",
      "strip_chars": "#"
    },
    "lvl5": {
      "selector": ".content__default h4",
      "strip_chars": "#"
    },
    // selector treści strony
    "text": ".content__default p, .content__default li",
    // język tekstu
    "lang": {
      "selector": "/html/@lang",
      "type": "xpath",
      "global": true
    }
  },
  "custom_settings": {
    "attributesForFaceting": [
      "lang"
    ]
  }
}

Zdefiniujmy jeszcze plik “.env” z potrzebnymi zmiennymi środowiskowymi potrzebnych do działania:

ALGOLIA_APPLICATION_ID=PASTE_HERE_YOUR_AGLOLIA_APPLICATION_ID
ALGOLIA_WRITE_API_KEY=PASTE_HERE_YOUR_ALGOLIA_WRITE_API_KEY
ALGOLIA_SITE_URL=https://developers.eventstore.com/
ALGOLIA_INDEX_NAME=Documentation

Algolia ma kilka typów kluczów do API aby móc zadbać o bezpieczeństwo. Do searcha byśmy używali readonly, tutaj jednak wrzucamy zawartość, więc użyjemy klucza do zapisu.

Zdefiniujmy sobie zatem pomocniczy skrypt “scrape.sh” , który będziemy odpalać do indeksowania (jeśli używasz Windowsa to aby odpalić bez zmian potrzebujesz Windows Subsystem for Linux):

if [ -f .env ]; then
    export $(xargs < .env)
fi

docker run \
    -e APPLICATION_ID=$(printenv ALGOLIA_APPLICATION_ID) \
    -e API_KEY=$(printenv ALGOLIA_WRITE_API_KEY) \
    -e CONFIG="$(cat config.json | jq '.start_urls=[env.ALGOLIA_SITE_URL]' | jq '.index_name=env.ALGOLIA_INDEX_NAME' | jq -r tostring)" \
    algolia/docsearch-scraper:v1.13.0

To co tutaj robimy to odpalamy przygotowanego przez Algolię obraz Dockera. Zanim to zrobimy to zaczytujemy zmienne środowiskowe z pliku “.env”. Obraz ten potrzebuje do startu kluczy do serwisu (APPLICATION_ID oraz API_KEY). Dodatkowo zaczytujemy treść naszego pliku konfiguracyjnego:

  • cat config.json - pobiera tresć,
  • jq ‘.starturls=[env.ALGOLIASITE_URL]’ - podmienia URL do strony wartością ze zmiennej środowiskowej,
  • jq ‘.indexname=env.ALGOLIAINDEX_NAME’ - podmienia nazwę indeksu,
  • jq -r tostring zamienia JSON na spłaszczony string.

Po co takie kombinacje? Bo docelowo nie chcielibyśmy musieć odpalać tego ręcznie. Lepiej, żeby się to działo automatycznie po wdrożeniu strony. Jak to zrobić? Np. przez GitHub Actions. Możemy zdefiniować sobie nowy workflow - np. jako algolia-scraper.yml:

name: algolia-scraper
on:
  push:
    branches:
      - master
  check_suite:
    types: [completed]
  workflow_dispatch:
jobs:
  scrape:
    runs-on: ubuntu-latest
    steps:
      - name: check out code 🛎
        uses: actions/checkout@v2
      - name: scrape the site 🧽
        env:
          ALGOLIA_APPLICATION_ID: ${{ secrets.ALGOLIA_APPLICATION_ID }}
          ALGOLIA_WRITE_API_KEY: ${{ secrets.ALGOLIA_WRITE_API_KEY }}
          ALGOLIA_SITE_URL: ${{ secrets.ALGOLIA_SITE_URL }}
          ALGOLIA_INDEX_NAME: ${{ secrets.ALGOLIA_INDEX_NAME }}
        run: |
          cd .algolia
          touch .env
          ./scrape.sh

Dzięki temu możemy podmienić sobie po prostu wartości zmiennych w zmiennych dla repozytorium bez zmieniania kodu skryptów. Smaczki? Trigger jest uruchamiany po merge do mastera oraz po check_suite. Dokumentację hostujemy przy pomocy Netlify. Mamy skonfigurowany build na Netlify, że jak zostanie wdrożona strona to puści nam własnie zdarzenie zwrotne do PRa o typie check_suite z wartością completed. To po to, żeby wiedzieć, żeby odpalać indeksowanie gdy wiemy, że już strona się wdrożyła. On push master na wszelki wypadek.

Dlaczego touch .env? Bo dla builda ten plik nie będzie istniał. Wartości zmiennych mamy zdefiniowane powyżej na podstawie “sekretów” repozytorium. Nigdy nie powinniśmy wrzucać tego typu zmiennych do repo. Plik .env dla wygody pracy lokalnej.

Jeśli chcesz zobaczyć PR, gdzie to wdrożyłem to zerknij do repo dokumentacji: https://github.com/EventStore/documentation/pull/306.

Efekty wyszukiwania możesz sprawdzić na https://developers.eventstore.com/. Tak wiem, popup jest brzydki, ale jest “good enough”.

Jeśli chcesz się pobawić Algolią, to ma również wersję darmową (bodajże do 10 000 wyszukiwań miesięcznie).

Mam nadzieję, że mój wpis Cię zaciekawił. Chciałem Ci pokazać, że czasem warto użyć gotowych rzeczy i kiedy ja się na takie decyduję, plus przykład jak można sobie zautomatyzować pracę z narzędziami.

Daj znać co o tym sądzisz!

Pozdrawiam Oskar

P.S. Więcej o Docker i GitHub Action wrzuciłem w moim repo z przykładami o NodeJS - https://github.com/oskardudycz/EventSourcing.NodeJS#continuous-delivery---build-docker-image-and-publish-to-docker-hub-and-github-container-registry

A oprócz tego jak co tydzień nowe Architecture Weekly: https://github.com/oskardudycz/ArchitectureWeekly#29th-march-2021

  • © Oskar Dudycz 2019-2020