検索リニューアル仕様

目的

本ドキュメントは、検索リニューアルにおける検索クエリ、検索対象カラム、検索条件、スコアリング、Traversal All 表示制御、および検証用パラメータの仕様を整理したものです。

本検索では、入力されたキーワードに対して複数の検索条件を組み合わせ、OpenSearch の _score をもとに検索結果を並べます。

さらに、作品・キャラクター・人物・会社ごとの人気指標や放映日などを補助スコアとして加算します。

このドキュメントは、デモページの各入力項目を調整しながら検索品質を検証するためのマニュアルでもあります。


OpenSearch 公式ドキュメントリンク

各パラメータの詳細仕様は、必要に応じて OpenSearch 公式ドキュメントを参照してください。


検索モード

traversal

画面右上にある検索ボックスによる検索に利用されます。

検索対象に all が指定された場合、複数カテゴリを横断して検索します。

対象カテゴリは以下です。

Traversal All では、全体スコア順のみで並べるのではなく、カテゴリごとの偏りを抑えるための表示制御を行います。

詳細は Traversal All を参照してください。

advanced

各画面にある詳細検索機能に利用されます。

advanced では検索対象に all は指定できません。
特定の index / category に対して検索します。


検索対象

検索対象は主にタイトル・名称系フィールドです。

title 系

anime / manga など、タイトルを持つデータでは以下を対象にします。

name 系

character / people / company など、名称を持つデータでは以下のような name 系フィールドを対象にします。

実際にどの field を使うかは index 定義に依存します。
デモページの条件別表示では、index ごとに本検索と同じ検索対象 field を使っているかを確認してください。

raw フィールド

完全一致や前方一致では、analyzer の影響を抑えた .raw フィールドを利用します。

対象例:

.raw は主に以下の用途で利用します。

例:

ngram / edge_ngram フィールド

部分一致や入力途中の検索では、ngram 系フィールドを利用します。

対象例:


キーワード正規化

3.x 検索では、基本的に query_string 用のサニタイズは行いません。

行うこと

行わないこと

理由は、3.x 検索で主に使うクエリが以下であり、query_string 構文として解釈されないためです。

たとえば、C++ を検索する場合、検索語は c++ のまま扱います。
c\+\+ にすると、実データの token と一致しなくなる可能性があります。


検索ロジック概要

検索ロジックは、複数の検索条件を組み合わせてスコアを算出します。

検索条件ごとに boost を設定でき、boost が高い条件ほど検索結果への影響が大きくなります。

また、boost0 の条件は検索クエリに含めません。
これにより、画面から検索条件をON/OFFしながら比較検証できます。

デモページでの基本的な見方

デモページでは、各検索条件を boost でON/OFFしながら検索結果を比較します。


検索条件

term

入力されたテキストと完全一致するタイトルを検索します。

主に .raw フィールドに対して利用します。

例:

検索語: seitokai ni mo ana wa aru!
対象:   title.original.raw

完全一致したタイトルを強く上げたい場合に利用します。

デモページでの調整

公式ドキュメント: term query

prefix

入力されたテキストから始まるタイトルを前方一致で検索します。

例:

検索語: seitoka
ヒット例: Seitokai ni mo Ana wa Aru!

.raw フィールドに対する prefix は、記号を含むタイトルにも比較的強いです。

例:

検索語: c++
ヒット例: C++ で始まるタイトル

ただし、prefix はフィールド先頭からの一致です。
タイトル途中に含まれる文字列は拾えません。

デモページでの調整

公式ドキュメント: prefix query

phrase_prefix

入力されたテキストをフレーズ先頭一致で検索します。

例:

検索語: subarashii seka
ヒット例: Kono Subarashii Sekai ni Shukufuku wo!

特徴:

デモページでの調整

ngram

入力されたテキストを N-gram 検索します。

入力されたテキストとタイトルを、それぞれ連続する文字列に分割し、部分的に一致するものを検索します。

例:

検索語: ras
ヒット例: Kono Subarashii Sekai ni Shukufuku wo!

ngram はタイトル途中の部分一致に強い一方で、広く拾いすぎる可能性があります。
そのため、通常は低めの boost で fallback 的に利用します。

minimum_should_match:ngram

minimum_should_match:ngram は、ngram で細かく分割された検索語のうち、どの程度一致していれば候補として残すかを調整する項目です。

言い換えると、ngram が広く拾いすぎるときに、少し厳しめにするための絞り込み設定です。

例:

検索語: seitokai
ngram token: sei, eit, ito, tok, oka, kai ...

このとき minimum_should_match を指定しないと、一部の token が一致しただけでも候補に残ることがあります。
minimum_should_match:ngram = 75% のようにすると、より多くの token が一致したものだけが残りやすくなります。

おすすめの使い方:

公式ドキュメント: minimum_should_match

edge_ngram

入力されたテキストを、タイトルの先頭からの N-gram として検索します。

例:

検索語: kono
ヒット例: Kono Subarashii Sekai ni Shukufuku wo!

edge_ngram は入力補完や先頭一致に向いています。
prefix と役割が近いため、比較検証しながら採用有無や boost を調整します。

minimum_should_match:edge_ngram

minimum_should_match:edge_ngram は、edge_ngram 条件で生成された token のうち、どの程度一致していれば候補として残すかを調整する項目です。

より実用的には、入力途中検索で候補が多すぎるときに、候補を少し絞るための設定です。

例:

検索語: fullmetal alch

edge_ngram は先頭一致に強いため、入力途中の候補を多く拾います。
候補が多すぎる場合は minimum_should_match:edge_ngram = 75% のようにして、より多くの token が一致した候補を優先できます。

おすすめの使い方:

公式ドキュメント: minimum_should_match

match_phrase

入力されたテキストを、語順を保ったフレーズとして検索します。

例:

検索語: fullmetal alchemist

語順が一致しているタイトルを評価します。

slop

slop を指定すると、語の間にどの程度のズレを許容するかを調整できます。

例:

slop = 0
  完全に連続した語順のみ

slop = 2
  語の間に多少のズレを許容

デモページでの調整

公式ドキュメント: match_phrase query

match_phrase_prefix

match_phrase に近い検索ですが、最後の語だけ前方一致として扱います。

例:

検索語: fullmetal alch
ヒット例: Fullmetal Alchemist

入力途中のフレーズ検索に向いています。

slop

match_phrase_prefix にも slop を指定できます。
語順のズレをどの程度許容するかを検証できます。

max_expansions

最後の語を prefix 展開するときに、最大でいくつの候補語へ展開するかを指定します。

値を大きくすると recall は上がりますが、検索負荷やノイズが増える可能性があります。

デモページでの調整

公式ドキュメント: match_phrase_prefix query

match

入力されたテキストを通常の全文検索として検索します。

現在の検証では、基本的に operator: and を利用します。

つまり、入力された語がすべて含まれるものを検索します。

例:

検索語: fullmetal alchemist
条件: fullmetal と alchemist の両方を含む

minimum_should_match

minimum_should_match を指定すると、すべての語を必須にせず、指定割合または指定数以上の語が一致すればヒットさせることができます。

例:

minimum_should_match = 75%

この場合、長い検索語で一部の語が一致しなくても候補に残りやすくなります。

UI上は以下のように切り替えます。

これにより、空欄に戻すだけで AND 検索へ戻せます。

デモページでの調整

公式ドキュメント: match query / minimum_should_match

match_bool_prefix

入力された語を解析し、最後の語だけ prefix として扱う検索です。

例:

検索語: fullmetal alch

以下のような動きになります。

fullmetal: 通常一致
alch: prefix 一致

phrase_prefix より語順制約が緩く、入力補完系の検索として比較対象になります。

fuzziness

fuzziness を指定すると、タイプミスをある程度許容できます。

例:

AUTO
1
2

ただし、短い検索語や固有名詞ではノイズが増える可能性があるため、低い boost で検証することを推奨します。

fuzziness min length

fuzziness min length は、検索語が一定以上の長さの場合だけ fuzziness を有効にするための設定です。

おすすめの使い方:

公式ドキュメント: match_bool_prefix query


boost

各検索条件には boost を設定できます。

boost は検索条件の重要度を調整する値です。

例:

term の boost を大きくする
  → 完全一致をより上位に出しやすくする

ngram の boost を小さくする
  → 部分一致によるノイズを抑える

boost が 0 の場合

boost が 0 の検索条件は、クエリに含めません。

例:

boost:term = 0

この場合、term クエリは生成されません。

これにより、検索条件のON/OFFを画面から簡単に切り替えられます。


field_boost

検索対象フィールドごとに重みを調整できます。

対象例:

例:

title.original = 1.0
title.en = 1.0
title.alt = 0.5
title.ja = 1.0
title_localized.title = 0.8

field_boost0 のフィールドは検索対象から外します。

これにより、たとえば title.alt のような別名・synonyms 系フィールドを弱めに扱えます。

デモページでの調整


dis_max

デモページでは、全体に対して dis_max を適用するオプションがあります。

dis_max は、複数の検索条件に一致した場合に、最も高いスコアを主に採用するための仕組みです。

通常の bool.should では、複数条件に一致するとスコアが合算されます。
一方、dis_max では最高スコアを中心に評価し、必要に応じて tie_breaker によって他の一致も少しだけ加点できます。

使いどころ

term / prefix / ngram / edge_ngram などに同時に一致した場合、スコアが過剰に積み上がることがあります。

そのような場合に dis_max を使うことで、過剰加点を抑えられます。

公式ドキュメント: dis_max query


スコア計算

OpenSearch の _score は、検索条件に基づくテキストスコアと、補助的なスコアリング関数を組み合わせて算出します。

現在の基本方針は以下です。

final_score = text_query_score + function_score_sum

text_query_score

検索条件ごとのスコアです。

例:

これらは基本的に bool.should で組み合わされます。
複数条件に一致した場合、それぞれのスコアが加算されます。

ただし、dis_max を有効にした場合は、最高スコアを中心に評価されます。

function_score_sum

検索結果に対して、人気度や日付による補助スコアを加算します。

利用する代表的な指標は以下です。

公式ドキュメント: function_score query


スコアリング指標

num_list_users

anime / manga におけるリスト登録者数です。

値が大きくなりやすいため、log2p などで圧縮してスコアに反映します。

主に anime / manga のスコアリングに利用します。

num_favorites

character / people / company におけるお気に入り数です。

anime / manga における num_list_users と同様に、人気指標としてスコアリングに利用します。

主に以下のカテゴリで利用します。

popularity

カテゴリごとに正規化された人気スコアです。

0〜1の値を取り、1に近いほど人気が高いことを表します。

基準はカテゴリごとに異なります。

popularity はすでに 0〜1 に正規化されているため、基本的には modifier: none で扱います。

start_date

anime / manga などの放映日・開始日をもとにしたスコアです。

0〜1の値に正規化して加算します。

start_date は null 許可のため、exists filter を付けたうえで script_score として扱います。

start_date / popularity グラフ

デモページでは、start_datepopularity のスコア関数をグラフで確認できます。

これにより、入力した boost や curve 設定が、どの程度スコアに影響するかを視覚的に確認できます。


traversal / advanced のスコアリング

traversal

Traversal は横断検索で使われるため、カテゴリ間で意味が大きくずれない指標を使うことが重要です。

代表的には popularity を利用します。

Traversal All ではカテゴリをまたぐ msearch を行うため、カテゴリごとに異なるフィールドを強く使いすぎると比較が難しくなる場合があります。

advanced

Advanced は単一カテゴリに対する検索なので、カテゴリごとに適した指標を使います。

anime / manga

主な補助スコア:

character / people / company

主な補助スコア:


Traversal All

Traversal All 検索では、検索結果が特定のカテゴリに偏りすぎないように、カテゴリごとに表示できる件数の上限を決めています。

たとえば最大10件表示する場合、anime / manga / character / people / company からそれぞれ2〜3件ずつ出すように調整されます。

そのため、全体スコア順だけで並べるのではなく、カテゴリごとの候補をバランスよく混ぜた結果になります。

caps

カテゴリごとの表示上限を cap と呼びます。

対象例:

cap を調整することで、Traversal All におけるカテゴリごとの表示件数を制御できます。

traversalCategoryWeight

Traversal All では、カテゴリごとの重みを調整できます。

対象例:

この値により、カテゴリ代表スコアを調整し、カテゴリの並び順や表示されやすさを検証できます。


記号検索の注意点

C++!+! のような記号を含む検索では、analyzer によって token が生成されない場合があります。

例:

en_analyzer        → tokens: []
tri_gram_analyzer  → tokens: []
raw_analyzer       → token: !+!

この場合、通常の match / ngram ではヒットせず、.raw に対する term / prefix でのみヒットする可能性があります。

現状で拾いやすいケース

タイトル全体が !+! と一致
  → term on .raw

タイトルが !+! で始まる
  → prefix on .raw

拾いにくいケース

タイトル途中に !+! を含む

この場合、現状の ngram analyzer が記号を token 化しない場合はヒットしにくくなります。

対応するには、記号や punctuation を保持する ngram field の追加が必要です。

デバッグ表示

デモページでは、記号検索の切り分け用に以下を表示します。

これにより、+ がスペース化されていないか、\+ のように不要な escape が入っていないかを確認できます。


条件別表示

matched_queries

各検索条件には _name を付け、検索結果に matched_queries として表示できるようにしています。

これにより、検索結果ごとにどの条件が発火したかを確認できます。

例:

条件別スコア表示

デモページでは、条件別スコアの概算を表示できます。

例:

txt 68.70 · members 23.20 · pop 30.00 · date 23.77

これにより、テキスト一致・人気指標・日付補正のどれが順位に効いているかを確認できます。

注意点

matched_queries や条件別スコア表示は、検索品質の検証を補助するための表示です。

特に Traversal All や msearch を利用する場合、本検索と条件別再スコアリングの対象 field が完全に一致しないと、chip 表示と実際のヒット条件がずれることがあります。


検証時の推奨観点

検索品質を評価するときは、以下を分けて確認します。

1. 検索条件の発火確認

各検索条件に _name を付けることで、検索結果ごとにどの条件が一致したかを確認できます。

確認例:

2. スコア内訳の確認

OpenSearch の _explain を利用すると、上位結果のスコア内訳を確認できます。

特に以下を見ると、調整がしやすくなります。

3. Traversal All 表示制御との切り分け

Traversal All では、検索スコアだけでなくカテゴリごとの cap や weight による表示制御が入ります。

そのため、評価時には以下を分けて見る必要があります。

4. ngram / edge_ngram のノイズ確認

ngram / edge_ngram は便利ですが、候補を広く拾います。

以下を比較しながら調整します。


推奨初期値の考え方

実際の最適値は検索ログや期待結果セットを見ながら調整します。
ただし、初期値としては以下の考え方が参考になります。

検索条件の boost

term                 高
match_phrase         中〜高
match_phrase_prefix  中
phrase_prefix        中
edge_ngram           中〜低
prefix               中〜低
match                中〜低
match_bool_prefix    中〜低
ngram                低

スコアリング系

num_list_users       log圧縮して利用
num_favorites        log圧縮して利用
popularity           0..1 のため modifier none
start_date           0..1 に正規化して weight 調整

minimum_should_match

match:
  長い検索語で候補が少ないときに 75% などを試す

ngram:
  部分一致のノイズが多いときに 60%〜75% を試す

edge_ngram:
  入力補完候補が広がりすぎるときに 60%〜75% を試す

fuzziness

短い検索語:
  基本的に無効

長い検索語:
  AUTO を低 boost で検証

fuzziness min length:
  5〜6文字程度から開始

まとめ

本検索ロジックは、以下の方針で構成されています。

  1. 完全一致を強く評価する
  2. フレーズ一致や入力途中検索を評価する
  3. ngram によって部分一致の recall を確保する
  4. 人気度や日付を補助スコアとして加算する
  5. Traversal All ではカテゴリ偏りを抑える表示制御を行う
  6. boost や slop などのパラメータを変更しながら検索品質を検証する
  7. matched_queries や条件別スコア表示で、どの条件が順位に効いているかを確認する

最終的な検索品質は、boost 値だけでなく、どの検索条件を採用するか、カテゴリごとの表示制御をどうするか、記号検索をどこまで対応するかによって決まります。


追記: 最新デモページの入力項目補足

この章は、既存の説明に対する追記です。既存仕様の本文は変更せず、最新版デモページで設定できる項目のうち、本文だけでは分かりにくいものを補足します。

score boost 系

デモページでは、テキスト一致以外の補助スコアを調整するために以下を入力できます。

boost:score

boost:score は、カテゴリごとの主要な人気指標に対する重みです。

利用される field はカテゴリによって異なります。

anime / manga では、リスト登録者数を人気指標として扱います。
character / people / company では、お気に入り登録数を人気指標として扱います。

boost:popuilarity

boost:popuilarity は、0〜1に正規化された popularity に対する重みです。

popularity はカテゴリごとの cap によって正規化されます。

boost:start_date

boost:start_date は、start_date による日付補正の重みです。

start_date は主に anime / manga の放映日・開始日補正として利用します。
null / missing の場合は加点しません。


追記: popularity cap と traversal cap の違い

デモページには、同じ cap という言葉を使う項目が複数あります。
意味が異なるため、区別して扱います。

popularity cap

popularity cap は、人気指標を 0〜1 に正規化するための閾値です。

入力項目は以下です。

たとえば cap:character = 10000 の場合、num_favorites が 10,000 件以上の character は popularity = 1 として扱います。
5,000 件であれば popularity = 0.5 のように扱います。

カテゴリごとに元データの分布が異なるため、同じ閾値を使わず、カテゴリ別に調整します。

代表的な考え方は以下です。

traversal cap

Traversal All の cap は、カテゴリごとの表示件数上限です。

こちらは popularity の正規化ではなく、all検索の表示バランスを調整するために使います。

たとえば、Traversal All の表示上限が30件の場合に、anime / manga / character / people / company をどの程度ずつ表示するかを制御します。

使い分け

popularity cap:
  人気スコアを作るための閾値

traversal cap:
  all検索でカテゴリごとに何件表示するかの上限

追記: popularity 関数グラフの見方

デモページの popularity 関数グラフは、各カテゴリの元指標が popularity に変換される様子を確認するためのものです。

たとえば cap:character = 10000 の場合、グラフでは以下のような変換を確認できます。

num_favorites = 0      → popularity = 0
num_favorites = 5000   → popularity = 0.5
num_favorites = 10000  → popularity = 1
num_favorites > 10000  → popularity = 1

このグラフを見ることで、cap が低すぎて多くのデータが 1 に張り付いていないか、逆に高すぎて人気補正がほとんど効いていないかを確認できます。


追記: start_date 関数グラフの見方

デモページの start_date 関数グラフは、放映日・開始日がどの程度スコアに反映されるかを確認するためのものです。

基本的な考え方は以下です。

date_func, date_center_days, date_scale_days を変更すると、日付補正の効き方を比較できます。


追記: プリセットJSON互換

デモページでは、以前のプリセットJSONで使っていた一部のキーも読み取れるようにしています。

現在のUI名と旧キーの対応は以下です。

旧キー 現在の扱い
match_phrase_slop slop_match_phrase として扱う
match_phrase_prefix_slop slop_match_phrase_prefix として扱う
max_expansions max_expansions:phrase_prefix / max_expansions:match_phrase_prefix の既定値として扱う
field_title_original field_boost:title.original として扱う
field_title_en field_boost:title.en として扱う
field_title_alt field_boost:title.alt として扱う
field_title_ja field_boost:title.ja として扱う
field_title_localized field_boost:title_localized.title として扱う
pop_cap_anime cap:anime として扱う
pop_cap_manga cap:manga として扱う
pop_cap_character cap:character として扱う
pop_cap_person cap:people として扱う
pop_cap_company cap:company として扱う

この互換対応により、過去に保存した検証条件をそのまま貼り付けても、現在のUI項目へ可能な範囲で反映できます。

ただし、新旧で意味が変わった項目がある場合は、現在のUI上の値を優先して確認してください。


追記: 旧プリセット例に含まれる主な項目

過去の検証条件には、以下のような項目が含まれていることがあります。

{
  "boost_term": "30",
  "boost_prefix": "10",
  "boost_phrase_prefix": "1",
  "boost_ngram": "0",
  "boost_edge_ngram": "0",
  "boost_match_phrase": "1",
  "match_phrase_slop": "0",
  "boost_match_phrase_prefix": "1",
  "match_phrase_prefix_slop": "0",
  "boost_match": "1",
  "boost_match_bool_prefix": "0",
  "minimum_should_match": "20%",
  "max_expansions": "0",
  "fuzziness": "",
  "field_title_original": "1",
  "field_title_en": "1",
  "field_title_alt": "1",
  "field_title_ja": "1",
  "field_title_localized": "1",
  "boost_popuilarity": "30",
  "boost_start_date": "30",
  "date_func": "gauss",
  "date_center_days": "0",
  "date_scale_days": "365",
  "pop_func": "linear",
  "pop_cap_anime": "300000",
  "pop_cap_manga": "45000",
  "pop_cap_character": "10000",
  "pop_cap_person": "5000",
  "pop_cap_company": "1000"
}

これらの項目は、現在のUIに合わせて読み替えながら利用します。


追記: 既存説明に対する注意

本文中の cap という表現は、文脈によって以下のどちらかを指します。

READMEを読む際は、この2つを混同しないようにしてください。

また、character / people / company の人気指標は num_favorites を使うため、anime / manga の num_list_users とは値の分布が異なります。
そのため、cap:character, cap:people, cap:company は anime / manga とは別に調整する必要があります。