子ドキュメント内での検索時の遅さ

トピック作成者:ks-solruserml-bot (2024/07/26 22:50 投稿)
7
CloseClose

(The bot translated the original post https://lists.apache.org/thread/7oc43wpkcgsh2wqwdq52bkvcqszd9bcg into Japanese and reposted it under Apache License 2.0. The copyright of posted content is held by the original poster.)

私たちは子ドキュメント内での検索時にパフォーマンスの問題に直面しています。この問題を説明するために、データモデルの非常に簡略化した抜粋を提供します。

私たちは図書館向けの検索エンジンを作成しています。ユーザーに提供したいのは「作品」です。作品の例としては、「ハリー・ポッターと炎のゴブレット」があります。各作品には複数の「形態」がある可能性があります。例えば、作品には書籍版、オーディオブック、場合によっては電子書籍が存在します。もちろん、作品レベルにはプロパティ(作成者、タイトル、主題など)があり、形態レベルには他のプロパティ(出版年、資料の種類など)があります。

これを親ドキュメントと子ドキュメントでモデル化し、それに基づいて検索エンジンを構築しました。この検索エンジンは、作品レベルでの作成者、タイトル、主題などの検索が可能ですが、ユーザーは特定の年のものや電子書籍として利用可能なものだけを検索することもできます。

Solrには約2800万の作品と4100万の形態があり、形態は子ドキュメントとしてインデックスされています(多くの作品には1つの形態しかありません)。

ユーザーが作品レベルで検索する限り、パフォーマンスは問題ありません。しかし、ユーザーが形態レベルで検索すると、パフォーマンスが悪化します。例えば、作成者で検索すると、検索は200ミリ秒未満で実行され、30件程度のヒットが得られます。もし資料の種類でクエリを追加すると({!parent which:'doc_type:work'}materialType:"book" のような構文)、検索には数秒かかります。この場合、フィルタリングをランキングの一部として扱いたいので、フィルタクエリに入れることは問題になります。

まず作品レベルでの検索を評価し、その後で子要件に一致する形態のフィルタリングを行う方法があるかどうかを考えています。作品レベルのプロパティでの検索を先に行い、IDのみを取得してから、形態レベルの要件に基づいたフル検索とIDでのフィルタクエリを追加する方法を試すことができますが、他により良い方法があるかどうか気になります。

また、デノーマライズについても調べました(https://blog.innoventsolutions.com/innovent-solutions-blog/2018/05/avoid-the-parentchild-trap-tips-and-tricks-for-denormalizing-solr-data.html)。少数の子フィールドに対しては役立ちますが、実際のモデルにはここで言及したもの以上のプロパティがあるため、いくつかの複雑さも伴います。

よろしくお願いします、

/Noah

返信投稿者:ks-solruserml-bot (2024/07/26 22:50 投稿)

こんにちは、Noahさん。

いくつかの注意点があります。クエリの実行時間は結果の数に依存します。あるクエリが他のクエリより遅い場合、その理由を列挙されたドキュメントの数が多いせいにすることができます。
debugQuery 出力でクエリがどのように解析されているかを確認してください。クエリパーサには多くのトリックや落とし穴があります。例えば、なぜ which の後にコロンを置いたのか、Solrにどのように入力したのか、その解釈がどうなっているのかは不明です。
どのバージョンのSolr/Luceneを使用していますか?以前はLuceneに二相イテレーションがなく、冗長な列挙が発生することがありました。

期待通りに動作するのが普通です。仮説を確認するには、{!parent ..}..work_id:123 を intersect させることで、fq+ を使用して確認できます。それにより瞬時に結果が得られるはずです。

すべてが正しい場合、大規模なインデックスを実行していて、多くのシャードに分割する必要があるかもしれません。

よろしくお願いします、
Mikhail Khludnev

返信投稿者:ks-solruserml-bot (2024/07/26 22:51 投稿)

ご返信ありがとうございます。より具体的な例と測定値を提供いたします:

私たちは Solr 9.0.1 を使用しています。

以下のクエリを Solr に送信しています(親クエリの後にコロンではなくイコールがあることをお詫び申し上げます):

{
  "query": "flotte huse",
  "filter": [
    "{!parent which='doc_type:work'}(pid.material_type:("lydbog" "artikel"))",
    "doc_type:work"
  ],
  "fields": "work.workid",
  "offset": 0,
  "limit": 10,
  "params": {
    "defType": "edismax",
    "qf": [
      "work.creator^100",
      "work.creator_fuzzy^0.001",
      "work.series^75",
      "work.subject_bibdk",
      "work.subject_fuzzy^0.001",
      "work.title^100",
      "work.title_fuzzy^0.001"
    ],
    "pf": [
      "work.creator^200",
      "work.fictive_character",
      "work.series^175",
      "work.title^1000"
    ],
    "pf2": [
      "work.creator^200",
      "work.fictive_character",
      "work.series^175",
      "work.title^1000"
    ],
    "pf3": [
      "work.creator^200",
      "work.fictive_character",
      "work.series^175",
      "work.title^1000"
    ],
    "mm": "2<80%",
    "mm.autoRelax": "true",
    "ps": 5,
    "ps2": 5,
    "ps3": 5
  }
}

このクエリは 21 件の workid を取得し、20 秒以上かかります。もしフィルタの最初の部分(親クエリを含む部分)を削除すると、33 件の workid が 200 ミリ秒未満で取得されます。フィルタの最初の部分があるかないかに関わらず(フィルタキャッシュが使用されないように新しい例を挙げた場合でも)、パフォーマンスに差はありません。

ドキュメントの返却件数には依存しないようです。

再度ご協力いただきありがとうございます。非常に感謝しています。

返信投稿者:ks-solruserml-bot (2024/07/26 22:51 投稿)

親クエリは実際にどのように解析されましたか? debugQuery=true の出力で確認できますか?

よろしくお願いします、
Mikhail Khludnev

返信投稿者:ks-solruserml-bot (2024/07/26 22:51 投稿)

ちょっと待ってください。

Noahさん、子クエリだけのパフォーマンスはどうですか?
q=pid.material_type:("lydbog" "artikel")
クエリの実行時間(qtime)はどのくらいで、どのように解析されていますか?

よろしくお願いします、
Mikhail Khludnev

返信投稿者:ks-solruserml-bot (2024/07/26 22:52 投稿)

再度ご意見ありがとうございます、ミハイル。!parent which の使い方や debugOutput についてもっと調べる必要があります。

「どのように解析されているか」を確認するために、debug 出力のどの部分を見れば良いか、もう少し詳しく教えていただけますか?その出力は Solr のソースコード以外にどこかで文書化されていますか?

よろしくお願いします、

/Noah

返信投稿者:ks-solruserml-bot (2024/07/26 22:52 投稿)

次のように見えます:

"debug": {
  ...
  "parsedquery": "name:some",
  "parsedquery_toString": "name:some",
  ...
  "QParser": "LuceneQParser",
  "filter_queries": ["features:here"],
  "parsed_filter_queries": ["features:here"],
}

よろしくお願いします、
Mikhail Khludnev

返信投稿者:ks-solruserml-bot (2024/07/26 22:52 投稿)

スキーマをデノーマライズ(フラット化)することを検討すると良いでしょう。これにより、物事がはるかにシンプルになります。作業レベルのフィールドに対するプレフィルタは、マニフェストレベルのオブジェクト内の作業フィールドのフィルタに過ぎないので、簡単で迅速ですし、フィルタキャッシュにキャッシュされます。シャーディングも必要ならば簡単に行えるでしょう。

多くのフィールドについては心配する必要はありません。我々は150k ドキュメントの小規模なインデックスで、1ドキュメントあたり30,000フィールドを持っていました。各クエリで検索やランキングに使用されるフィールドはほんの数個でしたが、非常に速かったです。

作業レベルの更新があると、すべてのマニフェストレベルのドキュメントを更新する必要がありますが、それは頻繁に発生するものではないはずです。

wunder
Walter Underwood
wunder@wunderwood.org
http://observer.wunderwood.org/ (私のブログ)

トピックへ返信するには、ログインが必要です。

KandaSearch

Copyright © 2006-2024 RONDHUIT Co, Ltd. All Rights Reserved.

投稿の削除

この投稿を削除します。よろしいですか?