カテゴリー: テクノロジー

【Solr】インデックス作成時に言語判定してフィールドを振り分ける

はじめに

Solrでインデックスしたいドキュメントが日本語だけと限らない場合があります。日本語の場合なら形態素解析ベースのトークナイザーを使いたいが、英語の場合は空白区切りベースのトークナイザーを使いたい。そういうときに使える言語判定の仕組みをSolrは備えています。

言語判定とスキーマ定義の方針

以下のような簡単なドキュメントを考えます。

[
    {
        "id" : "1",
        "text" : "Solr 8.4 からパッケージ管理機能が追加されました。リファレンスによると、ここでいうパッケージは1つまたは複数のプラグインを1つにまとめたものという意味のようです。Solr におけるパッケージ管理について調べました。",
    },
    {
        "id" : "2",
        "text" : "Starting in Solr 8.4, package management features have been added. According to the reference, package here means one or several plug-ins in one package. We looked at package management in Solr.",
    }
]

“text”が本文のフィールドで、ここに日本語の文章が入るかもしれないし、英語の文章が入るかもしれないという設定です。

日本語の場合は形態素解析を行う text_ja というフィールドタイプ、英語の場合は空白区切りベースの text_en というフィールドタイプにします。以下のようなスキーマ定義のイメージです。

<field name="text_ja"      type="text_ja"    indexed="true" stored="true"/>
<field name="text_en"      type="text_en"    indexed="true" stored="true"/>

言語判定の設定

以下を solrconfig.xml に追加して必要なライブラリを読み込みます。

<lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-langid-.*\.jar" />
<lib dir="${solr.install.dir:../../../..}/contrib/langid/lib/" regex=".*\.jar" />

LangDetectLanguageIdentifierUpdateProcessor を使う updateRequestProcessorChain を設定します。

 <updateRequestProcessorChain name="langdet">
    <processor class="org.apache.solr.update.processor.LangDetectLanguageIdentifierUpdateProcessorFactory" name="langdet">
      <lst name="defaults">
        <str name="langid.fl">text</str>
        <str name="langid.langField">language</str>
        <str name="langid.whitelist">ja,en</str>
	<str name="langid.map">true</str>
        <str name="langid.fallback">other</str>
      </lst>
    </processor>
    <processor class="solr.LogUpdateProcessorFactory" />
    <processor class="solr.RunUpdateProcessorFactory" />
  </updateRequestProcessorChain>

上で設定した langdet チェインを update リクエストのときに使うように設定します。

  <initParams path="/update/**">
    <lst name="defaults">
      <str name="update.chain">langdet</str>
    </lst>
  </initParams>

langid パラメータの意味

langid.fl

言語判定処理の対象とするフィールド名。上の設定では text フィールドを対象としています。

langid.langField

言語判定結果の文字列(jaとかenなど)を格納するフィールド。上の設定では language フィールドを対象としています。

langid.whitelist

言語判定の対象とする言語名。上の設定では日本語と英語を対象としています。

langid.map

言語判定結果をフィールドにマッピングするかどうか。マッピングする場合はtrueを指定します。デフォルトでは {元のフィールド名}_{言語名} というフィールドにマッピングされます。上の設定では日本語判定の場合は text_ja フィールドに、英語判定の場合は text_en フィールドにマッピングされます。

langid.fallback

langid.whitelist で指定した言語にあてはまらない場合のマッピング先を決定するための文字列。上の設定では other を指定しているので、日本語でも英語でもない場合は text_other フィールドにマッピングされます。

スキーマ定義

言語判定の設定に基づいてフィールドを定義します。

<field name="text_ja"      type="text_ja"    indexed="true" stored="true"/>
<field name="text_en"      type="text_en"    indexed="true" stored="true"/>
<field name="text_other"      type="text_ngram"    indexed="true" stored="true"/>
<field name="language"  type="string"  indexed="true" stored="true"/>

updateリクエストで送られてきた text フィールドの内容を言語判定して、日本語なら形態素解析して text_ja フィールドに、英語なら StandardTokenizer を通して text_en フィールド に、それ以外なら ngram 分割して text_other フィールドに格納されます。

動作確認

上記の設定でコレクションを作成してサンプルドキュメントを投入してみます。

$ cd server/solr/configsets
$ cp -r _default langdet
[langdet/conf/{solrconfig.xml,managed-scheme}を編集
$ (cd langdet/conf && zip -r - *) |curl --user user:pass -X POST --header "Content-Type:application/octet-stream" --data-binary @- "http://localhost:8983/solr/admin/configs?action=UPLOAD&name=langdet"
$ curl --user user:pass 'http://localhost:8983/solr/admin/collections?action=CREATE&name=langdet&numShards=1&replicationFactor=1&collection.configName=langdet&wt=json'
$ cat ~/sample.json 
[
    {
        "id" : "1",
        "text" : "Solr 8.4 からパッケージ管理機能が追加されました。リファレンスによると、ここでいうパッケージは1つまたは複数のプラグインを1つにまとめたものという意味のようです。Solr におけるパッケージ管理について調べました。",
    },
    {
        "id" : "2",
        "text" : "Starting in Solr 8.4, package management features have been added. According to the reference, package here means one or several plug-ins in one package. We looked at package management in Solr.",
    }
]
$ bin/post -u user:pass -c langdet ~/sample.json

最近のSolrでは、認証を設定しないとsolrconfig.xml内にlibディレクティブでライブラリを追加できないので注意が必要です。

$ curl -s -u user:pass "http://localhost:8983/solr/wikipedia_langdet/select?omitHeader=true&q=*%3A*"
{
  "response":{"numFound":2,"start":0,"numFoundExact":true,"docs":[
      {
        "id":"1",
        "language":"ja",
        "text_ja":"Solr 8.4 からパッケージ管理機能が追加されました。リファレンスによると、ここでいうパッケージは1つまたは複数のプラグインを1つにまとめたものという意味のようです。Solr におけるパッケージ管理について調べました。",
        "_version_":1682039285845327872},
      {
        "id":"2",
        "language":"en",
        "text_en":"Starting in Solr 8.4, package management features have been added. According to the reference, package here means one or several plug-ins in one package. We looked at package management in Solr.",
        "_version_":1682039286023585792}]
  }}

正しく言語判定されて、それぞれのフィールドに振り分けされました。


PCをキッチンと考える

こんにちは。開発担当のマットです。

私はプログラミングが好きでパソコンをほぼ毎日触っていますが、パソコンを買う時はどんなパソコンを買えばいいのか悩みます。スペックが複雑でどれを買えば1番良いのか見るだけではすぐにわからないからです。

パソコンに詳しくない方はもっと困るのではないかと思います。

でもパソコンに慣れていない方でもパソコンをキッチンに例えて考えると、それぞれのスペックの意味がわかりやすくなるので、この記事でその考え方を紹介したいと思います。

日常に見慣れているキッチンの光景
日常に見慣れているキッチンの光景

CPU

CPUはコンロです。

CPUのスペックで、重要のはクロックスピードだと思います。(だいたい、「GHz」で表記されるもの)
CPUのスピードが速いほど、パソコンの処理が速い。

料理は弱火で温めるより強火で温める方が速いのと同じです。

ガスコンロです。強火で温めると早く終わります。
ガスコンロです。強火で温めると料理が早く終わります。

なお、購入する時、デュアルコア(2コア)とクアッドコア(4コア)の言葉もよく見ることがあります。(最近は6コアも8コアもあります)
これは、コンロのバーナー(火が出るところ)に例えたら良いと思います。

1つのバーナーしかなければ、作りたい料理を1つずつ調理しなければいけません。
複数のバーナーがあれば、同時に複数の鍋を温めることができるので、その分調理が早く終わります。
しかし、調理と同じく複数のバーナーに分けれるレシピ(プログラム)もあれば、そうでもないものもあります。
使っているレシピ(プログラム)が、複数のバーナーを使えるようになっている場合(コンピューター用語では「スレッディング」と言います)、複数のコアを持った方がいいです。そうではない場合、複数のコアがあっても変わりません。

プラグラムがどれほどスレッディングに対応しているかは開発者がそのプログラムのコードを書く時に決めます。何かのオプションをONに切り替えるものではありません。

*上記でクロックスピードが速いほど処理が速いと言いましたが、厳密にいうと1部の処理を省略できるCPUもありますので、場合によってはクロックスピードが遅いCPUは速いCPUに勝つことはありえます。複雑ですね。

容量

容量(だいたい、「TB」や「GB」で表記されるもの)は冷蔵庫と考えていいと思います。
パソコンは使っているうちに多くの画像ファイル、音楽ファイル、アプリケーションなど段々といっぱいになってきます。
しかし容量が大きいほどパソコンに保存できるものが多くなります。

いっぱいになってしまうと、何かを捨てないといけません。
いっぱいになってしまうと、何かを捨てないといけません。

この容量の媒体はディスクと言います。ディスクは2種類あり、SSDとHDです。

簡単にいうと、SSDの方が最新型の冷蔵庫です。中にある具材(データ)が引き出しやすく、多くのファイル、多くのデータを頻繁に使う方には速くていいと思います。しかし、その分値段が高くなります。HDとSSD、両方入っているパソコンもあります。

メモリー

メモリーは「まな板」です。パワフルなコンロと、大きい冷蔵庫があっても、具材を調理する場所が必要ですよね。

例えば、グリーンサラダを作る時、レタス、トマト、キュウリなどを1つずつ冷蔵庫から出して、準備しないといけません。
しかし、まな板が小さいとスペース不足で同時に材料を切るこができず一時的にその切った材料を冷蔵庫(ディスク)に戻さないといけません。この手間で調理の効率が悪くなります。

手元に多くのものを置けると、仕事が楽になります
手元に多くのものを置けると、仕事が楽になります

なお、レシピ(プログラム)によって、まな板のスペースが足りないと具材がこぼれてしまって、調理が大失敗に終わる場合もあります(メモリー不足エラー)。

メモリーは容量の1種類(まな板も冷蔵庫も具材を置くところ)なので、これもGBで表記されます。

まとめ

もちろん、たくさんの技術的な情報を省略しているため完璧な例えではないですが、上記のようにパソコンの部分をキッチンの部分と思って想像するとなんとなくわかりやすくなると思います。

少しでもパソコンの購入に悩んでいる方の力になれたら嬉しいです。


PrometheusとAlertmanagerでSolrの異常を通知する

はじめに

前回の記事では Prometheus と Grafana による可視化を採り上げました。今回はさらに Alertmanager を組み合わせてメール通知を試してみました。

前回の続きで Solr と Prometheus と prometheus-exporter が動いている前提です。

Alertmanager のインストールと起動

$ wget https://github.com/prometheus/alertmanager/releases/download/v0.21.0/alertmanager-0.21.0.linux-amd64.tar.gz
$ tar zxf alertmanager-0.21.0.linux-amd64.tar.gz
$ cd alertmanager-0.21.0.linux-amd64
$ ./alertmanager --config.file=alertmanager.yml

今回はメール通知の動作確認用の alertmanager.yml を作成しました。メール以外にも Slack や Pushover など、様々な通知手段が用意されています。

global:
  resolve_timeout: 5m
  smtp_from: 'solr-alert@example.com'
  smtp_smarthost: 'localhost:25'

route:
  receiver: 'mail_test'

receivers:
- name: 'mail_test'
  email_configs:
    - to: 'alert-receiver@example.com'
      require_tls: false

Prometheus の設定

prometheus.yml でルールファイルを設定

rule_files:
  - "alert_rules.yml"

alert_rules.yml の内容

Solr の Ping API が正常応答しなかったら通知する設定です。

groups:
- name: sample_alert
  rules:
  - alert: solr_test_alert
    expr: absent(solr_ping) == 1
    for: 1m
    labels:
      severity: test
      test_type: SampleAlert
    annotations:
      summary: 'Solr Ping Error'

試してみる

正常な状態では、Prometheus の Alerts のページは以下のような表示です。

ここで Solr のプロセスを落としてみます。しばらく待つと異常を検知してPending状態に入ります。

このまま回復しなければメール通知されます。

受信したメールの内容です。

おわりに

簡単な例でメール通知できるところまで設定してみました。

Prometheus と Alertmanager の組み合わせでは、promQL による条件の記述や振り分けルールの設定などを使ってかなり複雑なこともできるようになっており、Solr 自体の監視だけでなく、インデックスの内容に基づいたアラートも可能です。


Solrのprometheus-exporterにメトリクスを追加する

はじめに

prometheus-exporter に付属の設定ファイル(contrib/prometheus-exporter/conf/solr-exporter-config.xml)には様々なメトリクスの設定例が記載されており、これとGrafanaを組み合わせれば、かなり便利なダッシュボードが作れます。

この記事では、デフォルトの設定に無いメトリクスを自分で追加する場合の設定方法を試してみました。

solr-exporter-config.xmlの内容

SolrのどのAPIを使うかに応じて4つのセクションが用意されています。

  • <ping> PingRequestHandler へのリクエストを利用します
  • <metrics> Metrics APIを利用します
  • <collections> Collections APIを利用します
  • <search> 検索リクエストを利用します

たとえば <ping> セクションは以下のように定義されています。

    <ping>
      <lst name="request">
        <lst name="query">
          <str name="path">/admin/ping/</str>
        </lst>
        <arr name="jsonQueries">
          <str>                                                                                                         
            . as $object | $object |                                                                                    
            (if $object.status == "OK" then 1.0 else 0.0 end) as $value |                                               
            {                                                                                                           
              name         : "solr_ping",                                                                               
              type         : "GAUGE",                                                                                   
              help         : "See following URL: https://lucene.apache.org/solr/guide/ping.html",                       
              label_names  : [],                                                                                        
              label_values : [],                                                                                        
              value        : $value                                                                                     
            }                                                                                                           
          </str>
        </arr>
      </lst>
    </ping>

name=”path” でリクエスト先のパスを指定しています。

ping リクエストの応答をどのように加工するのかを指定しているのが、name=”jsonQueries” の下の str タグに囲まれた領域です。jq クエリが使われているので jq コマンドで動作確認できます。

$ curl --user solr:SolrRocks -s 'http://localhost:8983/solr/test_shard1_replica_n1/admin/ping/' |jq '            . as $object | $object |
>             (if $object.status == "OK" then 1.0 else 0.0 end) as $value |
>             {
>               name         : "solr_ping",
>               type         : "GAUGE",
>               help         : "See following URL: https://lucene.apache.org/solr/guide/ping.html",
>               label_names  : [],
>               label_values : [],
>               value        : $value
>             }
> '
{
  "name": "solr_ping",
  "type": "GAUGE",
  "help": "See following URL: https://lucene.apache.org/solr/guide/ping.html",
  "label_names": [],
  "label_values": [],
  "value": 1
}

searchメトリクスを追加してみる

単純な例ですが、全件検索(‘*:*’による検索)のヒット件数をグラフにしてみます。

solr-exporter-config2.xml に以下を追加します。

    <search>
      <lst name="request">
        <lst name="query">
          <str name="collection">wikipedia</str>
          <str name="path">/select</str>
          <lst name="params">
            <str name="q">*:*</str>
            <str name="start">0</str>
            <str name="rows">0</str>
          </lst>
        </lst>
        <arr name="jsonQueries">
          <str>
            .response.numFound as $value |
            {
              name         : "solr_num_wikipedia_entry",
              type         : "GAUGE",
              help         : "number of wikipedia entry",
              label_names  : [],
              label_values : [],
              value        : $value
            }
          </str>
        </arr>
      </lst>
    </search>

ここで指定した内容は、実際には以下のような検索リクエストになります。

$ curl --user solr:SolrRocks -s 'http://localhost:8983/solr/wikipedia/select?q=*:*&start=0&rows=0'
{
  "responseHeader":{
    "zkConnected":true,
    "status":0,
    "QTime":0,
    "params":{
      "q":"*:*",
      "start":"0",
      "rows":"0"}},
  "response":{"numFound":2264810,"start":0,"numFoundExact":true,"docs":[]
  }}

jq クエリによる加工結果は以下の通りです。

$ curl --user solr:SolrRocks -s 'http://localhost:8983/solr/wikipedia/select?q=*:*&start=0&rows=0' |jq '.response.numFound as $value |
>             {
>               name         : "solr_num_wikipedia_entry",
>               type         : "GAUGE",
>               help         : "number of wikipedia entry",
>               label_names  : [],
>               label_values : [],
>               value        : $value
>             }
> '
{
  "name": "solr_num_wikipedia_entry",
  "type": "GAUGE",
  "help": "number of wikipedia entry",
  "label_names": [],
  "label_values": [],
  "value": 2264810
}

prometheus-exporter によって以下のような Prometheus フォーマットに変換されます。

$ curl -s http://localhost:9854/|grep solr_num_wikipedia_entry
# HELP solr_num_wikipedia_entry number of wikipedia entry
# TYPE solr_num_wikipedia_entry gauge
solr_num_wikipedia_entry{zk_host="localhost:9983",} 2264810.0

ここまで来れば、グラフ化するのは簡単です。下の画像は、データインポートハンドラでWikipediaの日本語全記事を投入したときの、記事数の変化をグラフ化したものです。


Macでスタイル(書式)なしペーストを簡単に実行したい

こんにちわ。
リエです。

PCでお仕事しているとショートカットキーをよく使います。
その中でも軍を抜いて使うのが「command + C(コピー)」と「command + V(ペースト)」です。


スタイル(書式)*以下スタイル なしでペーストしてほしいのですが、Macで「command + V」ペーストをすると、コピー元のスタイルを引き継いでしまうことがあります。

これが非常に嫌なのです!!!
なんでスタイルなしでペーストしてくれないのかといつも思っていました。

もちろんスタイルなしでペーストする方法はあります。
それは「ペーストしてスタイルを合わせる」を実行するのですが、ショートカットキーが「command + shift + option + V」とかなり多い。指つるわ。

もっと簡単な方法はないかなとググると素敵な記事発見。
素敵記事:https://decoy284.net/mac-command-v-paste-and-match-style/#:~:text=%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E7%92%B0%E5%A2%83%E8%A8%AD%E5%AE%9A%E3%82%92%E9%96%8B%E3%81%8D,%E3%81%A7%E8%B2%BC%E3%82%8A%E4%BB%98%E3%81%91%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%80%82

設定方法は以下です。
※私はめんどくさがりなので、画像無しでの説明です。
素敵記事は画像ありで説明してくださっているので、画像ありで設定方法を見たい方はそちらをご参照ください。
1.システム環境設定を開き、キーボードを選択。
2.ショートカットタブのアプリケーションに移動し、「+」をクリック。
3.メニュータイトルに「ペーストしてスタイルを合わせる」を入力、キーボードショートカットで「command + V」を押し、追加をクリック。


たったの3ステップで完了です。
これでスタイルなしでペーストしてくれるようになり、小さなストレスが無くなりました。

記事作成者さんありがとうございました👏