Mastodonで日本語検索を行う場合の設定

この記事はMastodonの内部の挙動にまで言及したものであり、わかりにくいと思いますので、初心者サーバー管理者向けの記事をどなたか代わりに書いてもらえると嬉しい感じです。

また、この記事ではMastodonのソースコードに直接手を入れる方法を案内しております。はっきり言ってこれしか方法がない。

この記事はv4.2.0に向けて開発中のMastodonについてを書いた記事であり、4.2.0のMastodonでは挙動が異なる可能性があることに留意してください。

ElasticSearch 7xとsudachiの組み合わせを前提としています。kuromojiをお使いの方にも若干は参考になるかもしれませんが、別の記事を見てください。


4.2.0での検索機能の改善

これは多くの人にとって衝撃的なコミットでしょう。

以前の記事にも書きましたが、Mastodonは確かに全文検索をサポートしています(ハッシュタグ検索とは別の機能です)。しかし全文検索ができる範囲は、「自分がお気に入り登録したもの」「自分がブーストしたもの」「自分が返信したもの」などと、「自分が何らかのリアクションを行った投稿」に限られていました。

上記のコミットにより、アカウントにIndexableという設定が追加されました。この設定を有効にしたアカウントの公開投稿は、誰でも自由に検索できるようになりました。

自分が見かけたことのない投稿も検索結果に引っかかるようになったのです。これはかなり画期的な変更でしょう。

Mastodonで日本語検索を行う場合の問題点

そもそもElasticSearchは、入ってきた文章を解析して、単語を取り出します。その単語で検索できるようにします。
例えば

「おそらにちょうちょがとんでいるよぴょん」

をみなさんの自然な感覚で解析すると

「おそら/に/ちょうちょ/が/とんで/いる/よ/ぴょん」

になります。
これね、「おそら」で検索すると出てきますが、「そ」で検索しても出てこないというのが意図した挙動になります。
デフォルトのElasticSearchでは日本語に対応していないので、こういう区切り方はできません。

デフォルトのMastodonでは、特に設定を変更していない場合、このような区切り方になります。

「お/そ/ら/に/ち/ょ/う/ち/ょ/が/と/ん/で/い/る/よ/ぴ/ょ/ん」

1文字ずつばらばらに取ってデータベースに保存してしまっているのです。
なので、特定の単語を検索する時はダブルクオートで囲ったほうが便利だったりします。
これをいい感じに区切ってくれるのが、みなさんご存知、sudachiというプラグインです。

先行記事について

実は先行してこの記事と同じ内容を紹介しているページが複数存在します。
あえて具体例は挙げませんが、それらを参照してソースコードを変更する場合、留意しなければいけない点があります。

それらの記事には、Indexableを有効にしていないアカウントの投稿まで自由に検索できる変更が追加されています。中には未収載投稿まで自由に検索できるパターンもあります。これはあすかがnote記事で散々書いているMastodonの設計思想を破壊するものであり、場合によっては他のサーバーからの信頼を損ねる危険な変更になりかねないことを把握しなければいけません。

Mastodonはプライバシーを重視するためそのような投稿を意図的に検索できないようにしています。さらに4.2.0でIndexableという設定が追加されたことにより、利用者による検索機能とプライバシーの両立への意識が高まり、「自分の投稿を自由に検索しないで欲しい」というニーズの知名度が上がります。そのような状況下で、上述したような記事の変更を安易に適用するのは危険であると考えます。

設定変更

すでに間違った設定でMastodon・ElasticSearchサーバーをたてて動かし始めちゃった人は、投稿を最初から全部ElasticSearchに登録し直す作業が必須になります。任意ではありません。必須です。そうでないと古い投稿がちゃんと検索できなくなり、雑多な検索結果となります。

まずElasticSearchにSudachiをインストールし、辞書(system_full.dic)を「/etc/elasticsearch/sudachi」ディレクトリに入れます。
そのあと「/etc/elasticsearch/sudachi/config.json」に以下を記述します。

{
        "systemDict": "system_full.dic"
}

そのあとは、Mastodonのソースを直接いじります。gitの操作は各自調べてください。
「app/chewy/statuses_index.rb」「app/chewy/public_statuses_index.rb」の2つのファイルにおいて該当箇所を修正し、Sudachiの設定を追加します。2ファイルどちらも修正内容は同じです。
これはcontentの動作を変更するものですが、新たに日本語解析処理が追加されます。

  {
    filter: {
      english_stop: {
        type: 'stop',
        stopwords: '_english_',
      },

      english_stemmer: {
        type: 'stemmer',
        language: 'english',
      },

      english_possessive_stemmer: {
        type: 'stemmer',
        language: 'possessive_english',
      },

      my_posfilter: {
        type: 'sudachi_part_of_speech',
        stoptags: [
          '助詞',
          '助動詞',
          '補助記号,句点',
          '補助記号,読点',
        ],
      },
    },
    analyzer: {
      content: {
        tokenizer: 'sudachi_tokenizer',
        filter: %w(
          english_possessive_stemmer
          lowercase
          asciifolding
          cjk_width
          english_stop
          english_stemmer
          my_posfilter
          sudachi_normalizedform
        ),
      },
    },
    tokenizer: {
      sudachi_tokenizer: {
        resources_path: '/etc/elasticsearch/sudachi',
        split_mode: 'A',
        type: 'sudachi_tokenizer',
        discard_punctuation: 'true',
      },
    },
  }

これはMastodonのソースコードの変更を最低限にする一番の方法です。

他にもっといい方法があります。日本語の文章ではsudachiを使う、英語の文章では使わない、といった設定があります。それは各自の好みに応じてカスタマイズということになります。

ただしanalyzerに項目を追加する場合、Mastodonソースコードの別の場所も修正する必要が出ます(今回の修正では不要です)。
「content」というanalyzerはMastodonが投稿本文に対してデフォルトで使っているanalyzerです。これの設定を変更するのが一番影響が小さいです。
新しいanalyzerを追加する場合、Mastodonが利用するanalyzerも一緒に変えなければいけません。ただし、どの投稿やどのような状況でどのanalyzerを使うのかの設定は多岐にわたり当記事では紹介できないので割愛します。

上記設定で「sudachi_tokenizer」の「split_mode」がAになっていますが、必要に応じてCにしてもらって大丈夫です。そのほかにも、sudachiの設定でこれやりたいってのがあれば変えちゃって構いません。

言い忘れました、上の2つのファイルを下へスクロールすると、このような記述が出てきます。(※「whitespace」は最新のコードでは別の文字列になってると思います)

field(:text, type: 'text', analyzer: 'whitespace', value: ->(status) { status.searchable_text }) { field(:stemmed, type: 'text', analyzer: 'content') }

これをこのように変更してください。さもないと、そもそもMastodonはsudachiを使ってくれません。
最初のanalyzerが「whitespace」から「content」になっています。これをやるならその後ろについてる「stemmed」設定も無駄になるので消したいところですが、ソースの別の場所も修正しなければいけなくなるので今回は紹介しません。

field(:text, type: 'text', analyzer: 'content', value: ->(status) { status.searchable_text }) { field(:stemmed, type: 'text', analyzer: 'content') }

これができればあとは以下のコマンドを実行して、既存の投稿を新しい設定に通してElasticSearchに保存します。

RAILS_ENV=production bin/tootctl search deploy

投稿本文を単語に区切るのは、検索の時にやっているわけではありません。投稿をElasticSearchに登録する時にやります。なので、新しい設定を既存の投稿に適用するには、まず既存の投稿をElasticSearchから全部消して最初から登録し直す作業が必要になります。これを行うのが上記コマンドです。

インデックスを作った後の設定変更が非推奨な理由

実はElasticSearchのインデックスの設定はKibanaでも行うことができます。あすかも実はソースを変更せずKibana経由で設定変更を行っていました。

ただ設定ミス(analyzerにsudachiを追加しただけで、Mastodonソースを修正してなかったため、sudachiは設定してたのに実際に使われてなかった)もあり正常に動作していませんでしたが、それは非推奨とする本質的理由ではありません。

tootctl search deploy

上記で紹介したこのコマンドは、インデックスを削除して、作り直します。その作り直す時に、上記ソースに記載の設定が使われます。つまりインデックスを作った後に変更した設定は上書きされたうえで新しい設定のままいろんな投稿が登録されるため、上記コマンドが事実上使えなくなります。

まとめ

Mastodonのソースに手を入れるのはgit管理とか考えるといろいろ面倒だから、設定ファイルを外部に出して任意で変更できる感じにしてくれ

あすかがソースを編集したがらなかったのも、kmyblue設立当初はソースを変更する予定などなく、gitリポジトリもMastodon公式ブランチに直接紐つけたかったってのが大きかったです。
バニラがこれのためだけにソース変更するとgit管理面倒なんですよね。

この記事が気に入ったらサポートをしてみませんか?