結合クエリパーサー

結合クエリパーサーを使用すると、ドキュメント間の関係を正規化するクエリを実行できます。

Solrは、ユーザーが選択したサブクエリ(`v`パラメータ)を実行し、一致するドキュメントが対象のフィールド(`from`パラメータ)に持つすべての値を識別し、それらの値が2番目の対象のフィールド(`to`パラメータ)に含まれるドキュメントを返します。

実際には、これらのセマンティクスはSQLエンジンの「内部クエリ」によく似ています。例として、以下のSolrクエリを考えてみましょう。

/solr/techproducts/select?q={!join from=manu_id_s to=id}title:ipod

タイトルに「ipod」を含む製品を製造している各メーカーのドキュメントを返すこのクエリは、以下のSQLクエリと意味的に同じです。

SELECT *
FROM techproducts
WHERE id IN (
    SELECT manu_id_s
    FROM techproducts
    WHERE title='ipod'
  )

結合操作は用語ベースで行われるため、`from`フィールドと`to`フィールドは互換性のあるフィールドタイプを使用する必要があります。たとえば、`StrField`と`IntPointField`間の結合は機能しません。同様に、`LowerCaseFilterFactory`を使用する`StrField`と`TextField`間の結合は、文字列フィールドですでに小文字になっている値に対してのみ機能します。

パラメータ

このクエリパーサーは、次のパラメータを受け取ります。

from

必須

デフォルト:なし

`to`フィールドで検索する値を含むフィールドの名前。単一値または複数値にすることができますが、`to`フィールドで表されるフィールドと互換性のあるフィールドタイプが必要です。

to

必須

デフォルト:なし

値が`from`フィールドで見つかった値と照合されるフィールドの名前。単一値または複数値にすることができますが、`from`フィールドと互換性のあるフィールドタイプが必要です。

fromIndex

オプション

デフォルト:*説明を参照*

"from"クエリ(`v`パラメータ)を実行し、"from"値が収集されるインデックスの名前。リクエストを処理するコアと同じノードに配置する必要があります。このパラメータが定義されていない場合、デフォルト値は処理コアの値になります。詳細については、以下の単一シャードコレクション間の結合またはコレクション間の結合を参照してください。

score

オプション

デフォルト:*説明を参照*

"from"クエリのスコアに関する情報を返すようにSolrに指示します。このパラメータの値は、返される集計情報の種類を制御します。オプションには、`avg`(平均)、`max`(最大)、`min`(最小)、`total`(合計)、または`none`が含まれます。

`method`が指定されていないが`score`が指定されている場合、`dvWithScore`メソッドが使用されます。`method`が指定されていて`dvWithScore`でない場合、`score`値は無視されます。詳細については、以下の`method`パラメータのドキュメントを参照してください。

method

オプション

デフォルト:*説明を参照*

Solrが使用するクエリ実装を決定します。オプションは、`index`、`dvWithScore`、および`topLevelDV`に制限されています。

指定しない場合、デフォルト値は`index`です。ただし、`score`パラメータが存在する場合は`dvWithScore`にオーバーライドされます。各実装には独自のパフォーマンス特性があり、ユーザーはどの実装がユースケースに最適かを判断するために実験することをお勧めします。詳細とパフォーマンスのヒューリスティックは以下に示します。

index

`score`パラメータが指定されていない場合のデフォルトの`method`。リクエストを処理するために用語インデックス構造を使用します。パフォーマンスは、"from"フィールドのカーディナリティと転記数(用語出現回数)によって変化します。"from"フィールドのカーディナリティが低い場合、"to"側が多数のドキュメントを返す場合、または散発的なコミット後の速度低下を許容できない場合(これは`index`が回避する他のメソッドの欠点です)、このメソッドを検討してください。

dvWithScore

結果ドキュメントと一緒にオプションの「スコア」統計を返します。可能な場合はdocValues構造体を使用しますが、必要な場合はフィールドキャッシュにフォールバックします。フィールドキャッシュへの最初のアクセスは、コミット後の最初のリクエストを遅くし、JVMヒープに追加のスペースを占有するため、ほとんどの場合、docValuesが推奨されます。「from」フィールドで一致する値の数に比例してパフォーマンスは線形にスケールします。このメソッドは、スコア情報が必要な場合に使用しなければならず、「to」側のドキュメントの戻り数に関係なく、「from」クエリが一致するドキュメントが少ない場合にも検討する必要があります。

dvWithScoreと単一値の数値

dvWithScoreメソッドは、単一値の数値フィールドをサポートしていません。7.0より前のバージョンから移行するユーザーは、フィールドタイプを文字列に変更し、移行中にインデックスを再構築することをお勧めします。

topLevelDV

toフィールドとfromフィールドにdocValuesデータがある場合にのみ使用でき、現在数値フィールドはサポートしていません。結果を見つけるためにトップレベルのdocValuesデータ構造を使用します。これらのデータ構造は、fromフィールドで一致する値の数が増えるにつれて、他のメソッドよりも優れたパフォーマンスを発揮します。しかし、構築コストが高く、各コミット後に遅延的に作成する必要があるため、各コミット後の最初のクエリで顕著な速度低下が発生することがあります。頻繁にコミットを行い、ユースケースで静的なウォーミングクエリを許容できる場合は、solrconfig.xmlにウォーミングクエリを追加することを検討してください。これにより、この作業はコミット自体の一部として実行され、ユーザーリクエストに直接関連付けられなくなります。「from」クエリが多数のドキュメントと一致し、「to」の結果セットのサイズが小規模から中規模である場合、このメソッドを検討してください。ただし、散発的なコミット後の速度低下が許容できる場合に限ります。

単一シャードコレクション間の結合

fromIndexパラメータを指定して、別のコアまたは単一シャードコレクションのフィールドと結合することもできます。SolrCloudモードで実行している場合、fromIndexパラメータで指定されたコレクションは、単一シャードを持ち、結合先のコレクションにレプリカがあるすべてのSolrノードにレプリカを持つ必要があります。

オスカーを受賞した監督による映画をSolr結合クエリを使用してフィルタリングする例を考えてみましょう。具体的には、次のフィールドを持つ2つのコレクションがあるとします。

movies: id、title、director_id、…​

movie_directors: id、name、has_oscar、…​

movie_directorsコレクションのSolr結合を使用して、オスカーを受賞した監督による映画をフィルタリングするには、次のフィルタクエリをmoviesコレクションに送信します。

fq={!join from=id fromIndex=movie_directors to=director_id}has_oscar:true

フィルタのクエリ基準(has_oscar:true)は、fromIndexを使用して指定されたコレクションのフィールドに基づいていることに注意してください。結合クエリを使用してfromIndexコレクションからフィールドを返すことはできません。「to」コレクション(movies)の結果をフィルタリングするためにのみフィールドを使用できます。

次に、これらのコレクションをクラスタにどのようにデプロイする必要があるかを理解しましょう。moviesコレクションが4ノードのSolrCloudクラスタにデプロイされ、レプリケーション係数が2の2つのシャードがあるとします。具体的には、moviesコレクションには次の4つのノードにレプリカがあります。

ノード1: movies_shard1_replica1

ノード2: movies_shard1_replica2

ノード3: movies_shard2_replica1

ノード4: movies_shard2_replica2

moviesコレクションとのSolr結合クエリでmovie_directorsコレクションを使用するには、4つのノードそれぞれにレプリカが必要です。つまり、movie_directorsは1つのシャードとレプリケーション係数4を持つ必要があります。

ノード1: movie_directors_shard1_replica1

ノード2: movie_directors_shard1_replica2

ノード3: movie_directors_shard1_replica3

ノード4: movie_directors_shard1_replica4

クエリ時に、JoinQParsermovie_directorsコレクションのローカルレプリカにアクセスして結合を実行します。ローカルレプリカが使用できないかアクティブでない場合、クエリは失敗します。この時点で、単一シャードに制限されており、データが必要なすべてのノードにレプリケートする必要があるため、このアプローチは、fromコレクションとtoコレクションの間に1対多の関係がある小さなデータセットでより効果的に機能することが明らかです。さらに、「to」コレクションにレプリカを追加する場合、「from」コレクションにもレプリカを追加する必要があります。

詳細については、Erick Ericksonが結合のパフォーマンスに関するブログ記事「Solr and Joins」を書いています。

複数シャードコレクションの結合

以下の条件を満たすコレクションを結合することも可能です。

  1. 同じ数のシャードを持つ

  2. 両方のコレクションで同じrouter.nameを持つ

  3. router.fieldまたはuniqueKeyは、tofromパラメータで使用されるフィールドに対応する必要があります(詳細は以下を参照)

  4. コレクションのシャードがコロケートされている(以下の例を参照)

単純化のために、「多」から「1」へのfromの結合の例を使用します。ただし、同じアプローチは他の場合にも使用できます。

「to」コレクション 「from」コレクション

ノード1

products_shard1_replica1

skus_shard1_replica2

ノード2

products_shard1_replica2

skus_shard1_replica1

ノード3

products_shard2_replica1

skus_shard2_replica1

ノード4

products_shard2_replica2

skus_shard2_replica2

shardNが相手側のコレクションの対応するshardNに対応していることに注意してください。AffinityPlacementPlugin.withCollectionShardsを使用して、コレクションシャードを調整します。

コレクションルーティングでサポートされているオプションは次のとおりです。

router.name フィールド制約

暗黙的

fromtorouter.fieldと一致する

plainまたはcompositeId

fromtouniqueKeyまたはrouter.fieldと一致する

compositeId(デフォルト)

制約はcheckRouterField=falseで無効にできる

注:3番目のケースは、インデクサーがこの場合にsku.id=<product.id>!<sku_id>を割り当てると想定しています。 compositeIdロジックは、製品のskuを製品と同じ名前のシャードに配置します。また、checkRouterField=falseを有効にして、_route_疑似フィールドを介してすべてのドキュメントを手動で配置することもできます。

たとえば、skusコレクションにrouter.field=product_idがある場合、q={!join to=id from=product_id fromIndex=skus}color:redを使用して、赤い色のskuの製品を見つけることができます。

コレクション間結合

コレクション間結合フィルターは、リモートSolrコレクションに対してクエリを実行して、ローカルSolrコレクションに対するフィルタークエリとして使用される結合キーのセットを取得する、結合パーサーのメソッドです。

コレクション間結合クエリは、CrossCollectionQueryオブジェクトを作成します。CrossCollectionQueryは、最初にリモートSolrコレクションにクエリを実行し、結合キーのストリーミング式結果を取得します。結合キーがノードにストリーミングされると、ローカルインデックス内の一致するドキュメントのビットセットが構築されます。これにより、結合キーのフルセットをいつでもメモリに保持することを回避できます。このビットセットは、Solrフィルターキャッシュの通常の動作と同様に、正常に実行されるとフィルターキャッシュに挿入されます。

ローカルインデックスが結合キーフィールドに従ってシャーディングされている場合、コレクション間結合はハッシュ範囲クエリパーサーと呼ばれるセカンダリクエリパーサーを活用できます。ハッシュ範囲クエリパーサーは、指定された値の範囲にハッシュされるドキュメントのみを返す役割を担います。これにより、CrossCollectionQueryはリモートSolrコレクションにクエリを実行し、ローカルSolrコレクション内の特定のシャードと一致する結合キーのみを返すことができます。これには、シャードの数が増えてもネットワークトラフィックが増加しないという利点があり、スケーラビリティが大幅に向上します。

コレクション間結合クエリは、文字列型とポイント型の両方のフィールドで機能します。結合キーに使用されているフィールドは単一値でなければならず、docValuesが有効になっている必要があります。

上記の最適化を利用できるように、ローカルコレクションを結合キーでシャーディングすることをお勧めします。

コレクション間結合クエリは、一般にqパラメータの一部として使用しないでください。適切なキャッシュを確保するために、フィルタークエリ(fqパラメータ)として使用することを目的としています。

クエリ対象のリモートSolrコレクションには、docValuesが有効になっている結合キーの単一値フィールドが必要です。

リモートSolrコレクションには、特定のシャーディング要件はありません。

solrconfig.xmlでの結合クエリパーサーの定義

コレクション間結合には、solrconfig.xmlで指定できるいくつかの構成オプションがあります。

routerField

オプション

デフォルト:なし

ドキュメントが結合フィールドによってCompositeIDルーターを使用してシャードにルーティングされる場合、そのフィールド名をここで構成に指定する必要があります。これにより、パーサーは結果のHashRangeクエリを最適化できます。

allowSolrUrls

オプション

デフォルト:なし

指定されている場合、この文字列の配列は、solrUrlクエリパラメータに渡すことができる許可されたSolr URLを指定します。この構成がない場合、solrUrlパラメータは使用できません。この制限は、攻撃者がSolrを使用してネットワークを探索するのを防ぐために必要です。

  <queryParser name="join" class="org.apache.solr.search.JoinQParserPlugin">
    <str name="routerField">product_id_s</str>
    <arr name="allowSolrUrls">
      <str>http://othersolr.example.com:8983/solr</str>
    </arr>
  </queryParser>

コレクション間結合クエリパラメータ

fromIndex

必須

デフォルト:なし

結合キー値のセットを取得するためにクエリを実行する外部Solrコレクションの名前。

zkHost

オプション

デフォルト:なし

ZooKeeperへの接続に使用する接続文字列。zkHostsolrUrlはどちらもオプションのパラメータであり、多くても一方のみを指定する必要があります。zkHostsolrUrlも指定されていない場合、ローカルのZooKeeperクラスタが使用されます。

solrUrl

オプション

デフォルト:なし

クエリを実行する外部SolrノードのURL。 solrconfig.xmlallowSolrUrlsパラメータにリストされている許可されたURLと文字単位で完全に一致する必要があります。URLが一致しない場合、このパラメータは事実上無効になります。

from

必須

デフォルト:なし

外部コレクションの結合キーフィールド名。

to

オプション

デフォルト:なし

ローカルコレクションの結合キーフィールド名。

v

オプション

デフォルト:なし

ローカルパラメータとして代入されるクエリ。これは、リモートコレクション内のドキュメントと一致するクエリ文字列です。

routed

オプション

デフォルト:false

trueの場合、コレクション間結合クエリは各シャードのハッシュ範囲を使用して、そのシャードに取得する結合キーのセットを決定します。このパラメータは、コレクション間結合のパフォーマンスを向上させますが、ローカルコレクションがtoフィールドによってルーティングされているかどうかに依存します。このパラメータが指定されていない場合、コレクション間結合クエリは正しい値を自動的に決定しようとします。

ttl

オプション

デフォルト:3600

キャッシュ内のコレクション間結合クエリが有効とみなされる時間の長さ(秒単位)。コレクション間結合クエリはリモートコレクションの変更を認識しないため、リモートコレクションが更新された場合、キャッシュされたコレクション間結合クエリは不正確な結果を返す可能性があります。 ttl期間が経過すると、コレクション間結合クエリはリモートコレクションに対して結合を再実行します。

その他のパラメータ

通常のSolrクエリパラメータもローカルパラメータとして指定/渡すことができます。

コレクション間のクエリ例

https://127.0.0.1:8983/solr/localCollection/query?fl=id&q={!join method="crossCollection" fromIndex="otherCollection" from="fromField" to="toField" v="*:*"}