こんにちは。検索エンジンチームの加藤 宏脩です。
先日、検索エンジンチームでLIFULLが利用しているSolrのバージョンを7.xから8.xにバージョンアップしました。 今回のSolrバージョンアップから自社で制作した性能テスト、回帰テストツールを導入したおかげか 大きい障害はなく無事にリリースできました。 リリース後は検索精度、パフォーマンスも向上しておりほっとしているところです。
8.xへの移行時はいくつか問題がありましたが、中でもbqパラメータのNegative Boostの廃止対応がたいへんでした。 Negative Boostの廃止対応が必要なことはバージョンアップ時のテスト中に気付き、簡単な対応だと思っていたのですが 並び順を維持させようとすると速度劣化が激しくなったりと対応がかなり難航するということがありました。 この記事では、Solr 8.xへのバージョンアップ作業の障害となったNegative Boostの廃止をどのように 対応したのか話をさせていただければと思います。
bqとは
Solrのパラメータの一つで、条件にマッチしたデータを重みづけして優先順位を上げたり下げたりする機能です。
bqについてのドキュメント https://solr.apache.org/guide/8_8/the-dismax-query-parser.html#bq-boost-query-parameter
Negative Boostとは
ここでいうNegative Boostとは条件にマッチしたらマイナス方向の重み付けをすることを指しています。
例1
# hogeの値が5のデータを-100の重み付けをする bq=hoge:5^-100
Negative Boost廃止についてのチケット https://issues.apache.org/jira/browse/LUCENE-7996
どう変更したか
LIFULLでの利用例
擬似的なスキーマ
name | type | multiValued | indexed | description |
---|---|---|---|---|
multiValuesParam1 | pint | true | true | multiValues型のパラメータ1つ目 |
multiValuesParam2 | pint | true | true | multiValues型のパラメータ2つ目 |
boolParams1 | pint | false | true | bool型のパラメータ1つめ。0がtrue、1がfalse |
boolParams2 | pint | false | true | bool型のパラメータ1つめ。0がtrue、1がfalse |
例2のようにmultiValuesParam[1 or 2]
に一致して、boolParams[1 or 2]
がtrueだったときにマイナスの重み付けをしていました。
例2
n=複数個の数字 bq=((multiValuesParam1:(n) AND boolParams1: 1) OR (multiValuesParam2:(n) AND boolParams2: 1))^=-100
(multiValuesParam1:(n) AND boolParams1: 1)
の条件を1とし、
(multiValuesParam2:(n) AND boolParams2: 1)
の条件を2とした場合
優先順位は、以下のようになります。
(1と2の条件どちらも一致しない、 1と2のどちらかの条件に一致しない) > 1と2のどちらの条件も一致する
変更案
結論から言うと、現行の重み付けに一致させる案は本番環境に耐えうる速度にはならなかったので3番目の案を採用して少し優先順位を変更するようにしました。
1. 条件の反転
例2の条件に当てはまらないデータを重み付けをするようにしました。
クエリは例3のようにn
以外のデータに絞り込みつつ、
それだけではパラメータが空だったときの条件が抜けてしまうため例4のような条件を加えるようにしました。
例3
multiValuesParam1:[* TO (n - 1)] OR multiValuesParam1:[(n + 1) TO *]
例4
*:* AND -multiValuesParam1:[* TO *]
結果は、優先順位は一致するが速度が非常に遅くなってしまいました。 原因はおそらく検索する範囲が広くなりすぎてしまったことによるところが大きいと思います。 またNOT条件はキャッシュができないので、二回目以降のアクセスも速度が遅くなってしまうという問題がありました。
2. bfで代用
次に例2の条件をbfで代用するように試してみました。
bfパラメータはスコアが0以下にならなければマイナスの重み付けが可能です。
全件に100の重み付けをしたあとに、例2の条件をbfで実装するようにしました。
bfにはwhere in
的な便利な構文はないので例5のような実装をつなげるようにしました。
例5
or(eq(multiValuesParam1, n), eq(multiValuesParam1, m)...etc)
結果は、優先順位は一致するがmultiValuesParam[1 or 2]
に指定する値が多くなると速度が遅くなってしまいました。
また、Solrには指定できる句の数に上限があるため、指定する値が多すぎるとエラーが変えるようになってしまう問題がありました。
3. NOTで計算する量を絞って計算にかかるコストを減らす(できるだけ理想に近い順番を編みだす)
反転のところでも記述したようにbqだけで解決しようとするとNOTの使用は避けられないので、 NOTで計算する量を限界まで減らすようにしました。
方針としては、例2の条件に当てはまらないデータをプラスの重みづけしていくようにしました。
実装は簡単に説明すると、例6のように前段で弾けるものは先にtrueを返すようにして
NOTの条件に到達するときは対になるmultiValuesParam[1 or 2]
が一致かつboolParams[1 or 2]
が1のデータだけが残っているようにします。
例6
( ( (multiValuesParam1:(n) AND boolParams1: 0) OR (multiValuesParam2:(n) AND boolParams2: 0) ...etc ) OR ([ここでできるだけ計算量を減らす] AND -(multiValuesParam1:n OR multiValuesParam2:n)) )^=100
結果、優先順の前後は同じで中間部が多少入れ替わるというようになりましたが、速度は元の実装と変わらないくらいになりました。 このやり方だと、NOTの前の条件を詰め込める環境であればさらに速度を早くできそうです。
まとめ
今回は、Solr versionの8.xへのバージョンアップに伴うnegative boostの廃止の対応をしました。 私自身Solrに投げらているクエリを大きく変えるというのはチームにジョインしてから初めてで良い経験になりました。 複雑な条件でNegative Boostを使用している方の助けになれば幸いです。(少数だとは思いますが。。)
検索エンジンチームでは今回のような、バージョンアップ作業をブロックしてしまうような問題をいち早く発見して解決できるようにするため、 Solrの新バージョンのリリースを検知し回帰テスト、性能テストを自動実行するしくみの導入を進めています。
また運用の自動化などで作った時間で、感動を届ける検索を実現することに注力しています。 カジュアル面談もやっていますので、一緒に「感動を届ける検索エンジンを実現する」、ひいてはLIFULLが目指している「あらゆるLIFEを、FULLに。」することに興味がある方はぜひお話しましょう! ここまで読んでいただきありがとうございました。