現在ラズパイでdokuwikiによるローカルWikiを作っている。ただ、やはりMarkdownで記述したり、タグをつけたりして体験を良くしたい、ということを感じていた。そこで探していたら、 Wiki.js というOSSプロジェクトを見つけた。結構未完成な部分があるものの、UIなどが良かったりして、Docker Hubにラズパイ対応のARMイメージが用意されていたりするなど、他のOSS含めて色々試した結果、総合的には良さげな気がするので移行を検討している

が、いかんせんメインの開発者さんが英語圏の方である以上、英語圏のユーザに最適化されており、特に検索に関してはPostgresの利用モジュールがpg_trgmで日本語が使えないなど、日本人にとっては中々致命的な問題がある

Postgresを使えればラズパイのスペックから言うと一番嬉しい。開発者さんとしてはVer3を制作中で、PGroogaへの移行やknexというモジュールを使いたくないなど前向きな意向があるようなのだけれど、スター数が多いとは言え、あくまで基本大部分は個人の作者さんが制作されている為、いつになるかも含めて、ちょっとどうなるか分からない

そこで、ElasticSearchを使いつつ、日本語対応する方法の実験について記載しておく

方法としては、通常であればkuromojiによる形態素解析で分割されたトークンを使う方法が一般的だけれど、個人的な目的としては「適当なキーワードを入れたら確実に一致するものを取り上げたい」し、できるだけ標準のイメージのみで完結させたいこともあり、アナライザにN-Gramを用いる

これは一種のユーザエクスペリエンスの問題で、「京都」と入力したら「京都」がピックされるのが一般的な感覚だが、検索で「東京都」もちゃんと拾って欲しい、というのが今回の目的になる

方法

まだラズパイではなくてPC上でのシミュレートなのだが、恐らく以下のようになる

  • 一度Wiki.js側からwikiのインデックスを構築してしまい、それを適用し、Wiki.js側のデータベースに登録させる
    • これ移行、Wiki.jsの設定には一切触らない
  • POSTMANを使って(Kibanaを入れていればそれに越したことはないが、これも300MB程度メモリを食うので避けたい)以後ElasticのAPIに対して作業を行う
  • DELETE /wiki/でインデックスを完全に消してしまう

その後、PUT /wiki/でリクエストボディのjsonで以下のような定義を設定し投げる

{
    "mappings": {
        "properties": {
            "content": {
                "type": "text"
            },
            "description": {
                "type": "text",
                "boost": 3.0
            },
            "locale": {
                "type": "keyword"
            },
            "path": {
                "type": "text"
            },
            "suggest": {
                "type": "completion",
                "analyzer": "ngram_analyzer",
                "preserve_separators": true,
                "preserve_position_increments": true,
                "max_input_length": 50
            },
            "tags": {
                "type": "text",
                "boost": 8.0
            },
            "title": {
                "type": "text",
                "boost": 10.0
            }
        }
    },
    "settings": {
        "analysis": {
            "analyzer": {
                "ngram_analyzer": {
                    "tokenizer": "ngram_tokenizer"
                }
            },
            "tokenizer": {
                "ngram_tokenizer": {
                    "token_chars": [
                        "letter",
                        "digit"
                    ],
                    "min_gram": "2",
                    "type": "ngram",
                    "max_gram": "3"
                }
            }
        }
    }
}

最後にGET /wiki/を試し、インデックスの設定を確認すれば良い

ぼちぼち調べたらそこそこ期待通りの結果が得られたので、これでまあまあ良さそう

問題はアプリ側からのインデックスリビルドは上記の設定を完全に初期化してしまうので、記事を1つ以上作成したあとの対応はできないということ。…ということで、ElasticのデータとPostgresのデータはボリュームマウントした対応を取って、復元可能な状態を維持しないといけなさそう(Elastic Searchに詳しくないので、できるかも。リインデックスという手法があるらしい)

ソース上では、engine.jsという部分が上記の設定処理を担当しているので、魔改造するという手もありかもしれないけど、アプリの更新とかに対応しづらくて大変かもしれない