JSON Facet API

JSON ファセッティングと分析

JSON ファセッティングは、Solr の従来のファセッティングと同様の機能を提供しますが、使いやすさを重視しています。従来のファセッティングよりもいくつかの利点があります。

  • 複雑またはネストされたファセットのプログラマチックな構築が容易

  • JSON によって提供されるネストと構造により、ファセットは従来のファセッティング API のフラットな名前空間よりも読みやすく理解しやすくなります。

  • メトリクスと分析のファーストクラスサポート

  • より標準化されたレスポンス形式により、クライアントがレスポンスを解析して使用しやすくなります。

ファセット検索とは、データを収集し、そのデータに関するメトリクスを計算することです。

ファセットには主に 2 つのタイプがあります。

  • データを複数の**バケット**に分割または分類するファセット(ドメイン)

  • 特定のバケット(通常はメトリクス、統計、または分析関数)に対してデータを計算するファセット

バケット化ファセットの例

これは、cat フィールド(カテゴリの略)に基づいてドキュメントをバケットに分割し、上位 3 つのバケットを返すバケット化ファセットの例です。

curl

curl https://127.0.0.1:8983/solr/techproducts/query -d '
{
  "query": "*:*",
  "facet": {
    "categories" : {
      "type": "terms",
      "field": "cat",
      "limit": 3
    }
  }
}'

SolrJ

final TermsFacetMap categoryFacet = new TermsFacetMap("cat").setLimit(3);
final JsonQueryRequest request =
    new JsonQueryRequest().setQuery("*:*").withFacet("categories", categoryFacet);
QueryResponse queryResponse = request.process(solrClient, COLLECTION_NAME);

以下のレスポンスは、32 個のドキュメントがデフォルトのルートドメインに一致することを示しています。12 個のドキュメントはcat:electronicsを持ち、4 個のドキュメントはcat:currencyを持ち、など。

[...]
  "facets":{
    "count":32,
    "categories":{
      "buckets":[{
          "val":"electronics",
          "count":12},
        {
          "val":"currency",
          "count":4},
        {
          "val":"memory",
          "count":3},
      ]
    }
  }

統計ファセットの例

統計(aggregationまたはanalyticとも呼ばれる)ファセットは、クエリ結果自体に加えて、クエリ結果から導き出された情報を表示するのに役立ちます。たとえば、統計ファセットは、メモリを探しているeコマースサイトのユーザーにコンテキストを提供するために使用できます。以下の例では、平均価格(およびその他の統計)を計算し、ユーザーがカート内のメモリスティックが適切な価格であるかどうかを判断できるようにします。

curl

curl https://127.0.0.1:8983/solr/techproducts/query -d '
q=memory&
fq=inStock:true&
json.facet={
  "avg_price" : "avg(price)",
  "num_suppliers" : "unique(manu_exact)",
  "median_weight" : "percentile(weight,50)"
}'

SolrJ

final JsonQueryRequest request =
    new JsonQueryRequest()
        .setQuery("memory")
        .withFilter("inStock:true")
        .withStatFacet("avg_price", "avg(price)")
        .withStatFacet("min_manufacturedate_dt", "min(manufacturedate_dt)")
        .withStatFacet("num_suppliers", "unique(manu_exact)")
        .withStatFacet("median_weight", "percentile(weight,50)");
QueryResponse queryResponse = request.process(solrClient, COLLECTION_NAME);

上記のファセットリクエストへのレスポンスは、ルートドメインに一致するドキュメント(`inStock:true` を持つ "memory" を含むドキュメント)で始まり、facetsブロック内に要求された統計が続きます。

 "facets" : {
    "count" : 4,
    "avg_price" : 109.9950008392334,
    "num_suppliers" : 3,
    "median_weight" : 352.0
  }

ファセットの種類

バケット化ファセットには 4 つの異なるタイプがあり、2 つの異なる方法で動作します。

  • "terms" と "range" ファセットは複数のバケットを生成し、ドメイン内の各ドキュメントをこれらのバケットの 1 つ(または複数)に割り当てます。

  • "query" と "heatmap" ファセットは常に単一のバケットを生成し、ドメイン内のすべてのドキュメントがそれに属します。

これらの各ファセットタイプについては、以下で詳しく説明します。

Terms ファセット

terms ファセットは、フィールドの一意の値に基づいてドメインをバケット化します。

curl

curl https://127.0.0.1:8983/solr/techproducts/query -d '
{
  "query": "*:*",
  "facet": {
    categories:{
      "type": "terms",
      "field" : "cat",
      "limit" : 5
    }
  }
}'

SolrJ

final TermsFacetMap categoryFacet = new TermsFacetMap("cat").setLimit(5);
final JsonQueryRequest request =
    new JsonQueryRequest().setQuery("*:*").withFacet("categories", categoryFacet);
QueryResponse queryResponse = request.process(solrClient, COLLECTION_NAME);
パラメータ 説明

field

ファセットを作成するフィールド名。

offset

ページングに使用され、最初の N 個のバケットをスキップします。デフォルトは 0 です。

limit

返されるバケット数を制限します。デフォルトは 10 です。

sort

生成されたバケットのソート方法を指定します。

countはドキュメント数を指定し、indexはバケット値のインデックス(自然)順でソートします。バケットに含まれるファセット関数/統計でソートすることもできます。デフォルトはcount descです。このパラメータは、sort:{count:desc}のように JSON でも指定できます。ソート順序は「asc」または「desc」のいずれかです。

overrequest

分散検索中にシャードから内部的に要求する、limitを超えるバケット数。

個々のシャードで上位の用語が大きく異なる場合、値を大きくすると、返される最終的な「上位用語」の精度を向上させることができます。

デフォルトの-1では、指定された他のオプションに基づいてヒューリスティックが適用されます。

refine(絞り込み)

trueの場合、分散ファセットの絞り込みを有効にします。これは、初期の内部結果にそれらのバケットが含まれていないシャードから、最終結果に必要なバケットを取得する第2フェーズを使用するため、すべてのシャードがこのファセットとそのサブファセットのすべての返されたバケットに寄与します。これにより、返されたバケットのカウントと統計が正確になります。

overrefine(過剰絞り込み)

分散検索中に、どのバケットを絞り込むかを決定する際に、内部的に考慮するlimitを超えるバケット数。

個々のシャードで上位の用語が大きく異なる場合、値を大きくすると、返される最終的な「上位用語」の精度を向上させることができます。また、現在のsortオプションにより、絞り込みによってソート済みリストの下位に用語が移動される可能性があります(例:sort:"count asc")。

デフォルトの-1では、指定された他のオプションに基づいてヒューリスティックが適用されます。

mincount(最小カウント)

この数以上のカウントを持つバケットのみを返します。デフォルトは1です。

missing(欠損値)

フィールドに値がないドキュメントによって定義される特別な「欠損値」バケットを返すかどうかを指定するブール値。デフォルトはfalseです。

numBuckets(バケット数)

ブール値。trueの場合、レスポンスに「numBuckets」を追加します。これは、(返されたバケット数ではなく)ファセットのバケット数を表す整数です。デフォルトはfalseです。

allBuckets(すべてのバケット)

ブール値。trueの場合、すべてのバケットの集合を表す「allBuckets」バケットをレスポンスに追加します。多値フィールドの場合、これはドメイン内のすべてのドキュメントのバケットとは異なります。なぜなら、単一のドキュメントが複数のバケットに属することができるからです。デフォルトはfalseです。

prefix(プレフィックス)

指定されたプレフィックスで始まる用語のバケットのみを生成します。

facet(ファセット)

返される各バケットに対して計算される集計、メトリック、またはネストされたファセット。

method(メソッド)

このパラメータは、使用するファセットアルゴリズムを示します。

  • dv DocValues、序数配列に収集

  • uif UnInvertedField、序数配列に収集

  • dvhash DocValues、ハッシュに収集 - 高カーディナリティフィールドでの効率を向上させます

  • enum TermsEnum を使用して DocSet と交差(ストリーム可能)

  • stream 現在、enumと同等です。sort: index asc および allBucketsnumBucketsmissing が無効になっているインデックス付きの非ポイントフィールドに使用されます。

  • smart フィールドの種類に最適なメソッドを選択します(デフォルト)。

prelim_sort(予備ソート)

sortパラメータが高コストの場合、上位バケットの初期収集中に使用する最終的なsortの近似値を指定するためのオプションのパラメータです。

クエリファセット

クエリファセットは、ドメインと指定されたクエリに一致するドキュメントの単一のバケットを生成します。

curl

curl https://127.0.0.1:8983/solr/techproducts/query -d '
{
  "query": "*:*",
  "facet": {
    "high_popularity": {
      "type": "query",
      "q": "popularity:[8 TO 10]"
    }
  }
}'

SolrJ

QueryFacetMap queryFacet = new QueryFacetMap("popularity:[8 TO 10]");
final JsonQueryRequest request =
    new JsonQueryRequest().setQuery("*:*").withFacet("high_popularity", queryFacet);
QueryResponse queryResponse = request.process(solrClient, COLLECTION_NAME);

ユーザーは、サブファセット(「バケット化」ファセットまたはメトリックのいずれか)も指定できます。

curl

curl https://127.0.0.1:8983/solr/techproducts/query -d '
{
  "query": "*:*",
  "facet": {
    "high_popularity": {
      "type": "query",
      "q": "popularity:[8 TO 10]",
      "facet" : {
        "average_price" : "avg(price)"
      }
    }
  }
}'

SolrJ

QueryFacetMap queryFacet =
    new QueryFacetMap("popularity:[8 TO 10]").withStatSubFacet("average_price", "avg(price)");
final JsonQueryRequest request =
    new JsonQueryRequest().setQuery("*:*").withFacet("high_popularity", queryFacet);
QueryResponse queryResponse = request.process(solrClient, COLLECTION_NAME);

レスポンス例

"high_popularity" : {
  "count" : 36,
  "average_price" : 36.75
}

範囲ファセット

範囲ファセットは、日付または数値フィールドにわたる複数のバケットを生成します。

curl

curl https://127.0.0.1:8983/solr/techproducts/query -d '
{
  "query": "*:*",
  "facet": {
    "prices": {
      "type": "range",
      "field": "price",
      "start": 0,
      "end": 100,
      "gap": 20
    }
  }
}'

SolrJ

RangeFacetMap rangeFacet = new RangeFacetMap("price", 0.0, 100.0, 20.0);
final JsonQueryRequest request =
    new JsonQueryRequest().setQuery("*:*").withFacet("prices", rangeFacet);
QueryResponse queryResponse = request.process(solrClient, COLLECTION_NAME);

上記の範囲ファセットからの出力は、次のような形式になります。

"prices":{
  "buckets":[
    {
      "val":0.0,  // the bucket value represents the start of each range.  This bucket covers 0-20
      "count":5},
    {
      "val":20.0,
      "count":0},
    {
      "val":40.0,
      "count":0},
    {
      "val":60.0,
      "count":1},
    {
      "val":80.0,
      "count":1}
  ]
}

範囲ファセットパラメータ

範囲ファセットのパラメータ名とセマンティクスは、facet.range クエリパラメータスタイルのファセットとほぼ同じです。たとえば、ここの「start」は、facet.range コマンドの「facet.range.start」に対応します。

パラメータ 説明

field

範囲バケットを生成する数値フィールドまたは日付フィールド。

start(開始)

範囲の下限。

end(終了)

範囲の上限。

gap(間隔)

生成される各範囲バケットのサイズ。

hardend(ハードエンド)

ブール値。true の場合、最後のバケットは「gap」よりも狭くても「end」で終了します。false の場合、最後のバケットは「gap」の幅になり、「end」を超える可能性があります。

other(その他)

このパラメータは、startendの間の各範囲の制約のカウントに加えて、次についてもカウントを計算することを示します。

  • 「before」最初の範囲の下限よりも小さいフィールド値を持つすべてのレコード

  • 「after」最後の範囲の上限よりも大きいフィールド値を持つすべてのレコード

  • 「between」すべての範囲の開始と終了の境界の間のフィールド値を持つすべてのレコード

  • 「none」これらの情報の計算を行わない

  • 「all」before、between、after のショートカット

include(含む)

デフォルトでは、startendの間の範囲ファセッティングを計算するために使用される範囲は、下限を含み、上限を含みません。「before」範囲は排他的であり、「after」範囲は包括的です。このデフォルト(以下の「lower」と同等)は、境界で重複カウントが発生しません。includeパラメータは、以下のオプションの任意の組み合わせにすることができます。

  • 「lower」すべての gap ベースの範囲は下限を含みます

  • 「upper」すべての gap ベースの範囲は上限を含みます

  • 「edge」対応する upper/lower オプションが指定されていない場合でも、最初と最後の gap 範囲はエッジ境界(つまり、最初の範囲の下限、最後の範囲の上限)を含みます。

  • 「outer」最初または最後の範囲が既にこれらの境界を含んでいる場合でも、「before」と「after」の範囲は境界を含みます。

  • 「all」lower、upper、edge、outer の略記

facet(ファセット)

返される各バケットに対して計算される集計、メトリック、またはネストされたファセット。

ranges(範囲)

任意の範囲のリスト。指定されている場合、startgapendではなく、指定された範囲でファセットを計算します。startendgapを使用すると、範囲またはバケットの幅は常に固定されます。可変範囲幅で範囲ファセッティングを計算する必要がある場合は、rangesを指定する必要があります。

  • rangesと一緒にstartend、またはgapを指定することは許可されておらず、リクエストは失敗します。

  • 範囲ファセットでrangesが指定されている場合、hardendincludeotherパラメータは無視されます。

任意の範囲を参照してください。

任意の範囲

任意の範囲は、範囲バケットが計算されるfrom値とto値で構成されます。この範囲は2つの構文で指定できます。

パラメータ 説明

from(開始)

範囲の下限。指定されていない場合は*がデフォルトです。

to(終了)

範囲の上限。指定されていない場合は*がデフォルトです。

inclusive_from(開始値を含む)

ブール値。true の場合、下限fromを含みます。デフォルトはtrueです。

inclusive_to(終了値を含む)

ブール値。true の場合、上限toを含みます。デフォルトはfalseです。

range(範囲)

範囲は文字列として指定されます。これはfacet.intervalとセマンティック的に似ています。

  • rangeが指定されている場合、範囲内の上記のすべてのパラメータfromtoなどは無視されます。

  • rangeは常に(または[で始まり、)または]で終わります。

    • ( - 下限を除外

    • [ - 下限を含む

    • ) - 上限を除外

    • ] - 上限を含む

たとえば、範囲(5,10]では、5は除外され、10は含まれます。

ranges を使用した other

rangesが指定されている場合、otherパラメータは無視されますが、rangesを使用して同じ動作を実現する方法があります。

  • before - これは[*,some_val)または単にto値を指定することと同等です。

  • after - これは(som_val, *]または単にfrom値を指定することと同等です。

  • between - これはそれぞれfromtoとしてstartendを指定することと同等です。

ranges を使用した include

rangesが指定されている場合、includeパラメータは無視されますが、rangesを使用して同じ動作を実現する方法があります。lowerupperouteredgeはすべて、inclusive_toinclusive_fromの組み合わせを使用して実現できます。

rangesを使用した範囲ファセット

curl https://127.0.0.1:8983/solr/techproducts/query -d '
{
  "query": "*:*",
  "facet": {
    "prices": {
      "type": "range",
      "field": "price",
      "ranges": [
        {
          "from": 0,
          "to": 20,
          "inclusive_from": true,
          "inclusive_to": false
        },
        {
          "range": "[40,100)"
        }
      ]
    }
  }
}'

上記の範囲ファセットからの出力は、次のような形式になります。

{
  "prices": {
    "buckets": [
      {
        "val": "[0,20)",
        "count": 5
      },
      {
        "val": "[40,100)",
        "count": 2
      }
    ]
  }
}
rangeが指定されている場合、リクエスト内のその値がレスポンスのキーとして使用されます。そうでない場合、キーはfromtoinclusive_toinclusive_fromを使用して生成されます。現在、カスタムkeyはサポートされていません。

ヒートマップファセット

heatmapファセットは、各グリッドセルに空間データを持つドキュメントのファセットカウントの2Dグリッドを生成します。

この機能は、主にリファレンスガイドの空間セクションで説明されています。主要なパラメータは、heatmapを指定するtypeと、空間RPTフィールドを示すfieldです。残りのパラメータ名は、"facet.heatmap."プレフィックスがないものの、facet.heatmap クエリパラメータスタイルのファセッティングを反映した同じ名前とセマンティクスを使用します。たとえば、ここのgeomは、facet.heatmap コマンドのfacet.heatmap.geomに対応します。

ドメインをバケットに分割する他のファセットとは異なり、heatmapファセットは現在ネストされたファセットをサポートしていません。

curl

curl https://127.0.0.1:8983/solr/spatialdata/query -d '
{
  "query": "*:*",
  "facet": {
    "locations": {
      "type": "heatmap",
      "field": "location_srpt",
      "geom": "[\"50 20\" TO \"180 90\"]",
      "gridLevel": 4
    }
  }
}'

SolrJ

final JsonQueryRequest request =
    new JsonQueryRequest()
        .setQuery("*:*")
        .setLimit(0)
        .withFacet(
            "locations",
            new HeatmapFacetMap("location_srpt")
                .setHeatmapFormat(HeatmapFacetMap.HeatmapFormat.INTS2D)
                .setRegionQuery("[\"50 20\" TO \"180 90\"]")
                .setGridLevel(4));

ファセットレスポンスは次のようになります。

{
  "facets": {
    "locations":{
      "gridLevel":1,
      "columns":6,
      "rows":4,
      "minX":-180.0,
      "maxX":90.0,
      "minY":-90.0,
      "maxY":90.0,
      "counts_ints2D":[[68,1270,459,5359,39456,1713],[123,10472,13620,7777,18376,6239],[88,6,3898,989,1314,255],[0,0,30,1,0,1]]
    }
  }
}

統計ファセット関数

これまでに説明したすべてのファセットとは異なり、集計関数(ファセット関数分析関数、またはメトリックとも呼ばれる)はデータをバケットに分割しません。代わりに、ドメイン内のすべてのドキュメントに対して何かを計算します。

集計 説明

sum(合計)

sum(sales)

数値の合計

avg(平均)

avg(popularity)

数値の平均

min(最小値)

min(salary)

最小値

max(最大値)

max(mul(price,popularity))

最大値

missing(欠損値)

missing(author)

指定されたフィールドまたは関数に値を持たないドキュメントの数

countvals(値数)

countvals(author)

指定されたフィールドまたは関数の値の数

unique(ユニーク数)

unique(author)

指定されたフィールドの一意の値の数。100 を超える値の場合、正確な推定値は得られません。

uniqueBlock(ユニークブロック数)

uniqueBlock(_root_) または uniqueBlock($fldref)(ここで fldref=_root_

ブロック結合ブロックの数をカウントするためだけの、より小さなフットプリントを持つものと同じです。指定されたフィールドはブロック全体で一意である必要があり、単一値の文字列フィールドのみがサポートされます。DocValues をお勧めします。

uniqueBlock({!v=type:parent}) または uniqueBlock({!v=$qryref})(ここで qryref=type:parent

上記と同じですが、ヒットを集計するために指定されたクエリのビットセットを使用します。

hll(ハイパーログログ)

hll(author)

ハイパーログログアルゴリズムによる分散カーディナリティ推定

percentile(パーセンタイル)

percentile(salary,50,75,99,99.9)

t-digest アルゴリズムによるパーセンタイル推定。このメトリックでソートする場合、リストされている最初のパーセンタイルがソート値として使用されます。

sumsq(二乗和)

sumsq(rent)

フィールドまたは関数の二乗の合計

variance(分散)

variance(rent)

数値フィールドまたは関数の分散

stddev(標準偏差)

stddev(rent)

フィールドまたは関数の標準偏差

relatedness(関連性)

relatedness('popularity:[100 TO *]','inStock:true')

ドメイン内の文書とフォアグラウンド集合との関連性スコアを、バックグラウンド集合(両方ともクエリとして定義)を基準に計算する関数です。これは主にセマンティックナレッジグラフを構築する場合に使用されます。

avgなどの数値集計関数は、任意の数値フィールド、またはavg(div(popularity,price))などの複数の数値フィールドのネストされた関数に対して使用できます。

集計関数を要求する最も一般的な方法は、計算したい式を含む単純な文字列として指定することです。

curl

curl https://127.0.0.1:8983/solr/techproducts/query -d '
{
  "query": "*:*",
  "filter": [
    "price:[1.0 TO *]",
    "popularity:[0 TO 10]"
  ],
  "facet": {
    "avg_value": "avg(div(popularity,price))"
  }
}'

SolrJ

final JsonQueryRequest request =
    new JsonQueryRequest()
        .setQuery("*:*")
        .withFilter("price:[1.0 TO *]")
        .withFilter("popularity:[0 TO 10]")
        .withStatFacet("min_manu_id_s", "min(manu_id_s)")
        .withStatFacet("avg_value", "avg(div(popularity,price))");
QueryResponse queryResponse = request.process(solrClient, COLLECTION_NAME);

ローカルパラメータを指定できる拡張形式があります。これらは、relatedness()などの特殊な集計で明示的に使用される場合がありますが、(グローバル)リクエストパラメータを使用する必要なく、集計式を読みやすくするためにパラメータ参照として使用することもできます。

curl

curl https://127.0.0.1:8983/solr/techproducts/query -d '
{
  "query": "*:*",
  "filter": [
    "price:[1.0 TO *]",
    "popularity:[0 TO 10]"
  ],
  "facet": {
    "avg_value" : {
      "type": "func",
      "func": "avg(div($numer,$denom))",
      "numer": "mul(popularity,3.0)",
      "denom": "price"
    }
  }
}'

SolrJ

final Map<String, Object> expandedStatFacet = new HashMap<>();
expandedStatFacet.put("type", "func");
expandedStatFacet.put("func", "avg(div($numer,$denom))");
expandedStatFacet.put("numer", "mul(popularity,3.0)");
expandedStatFacet.put("denom", "price");
final JsonQueryRequest request =
    new JsonQueryRequest()
        .setQuery("*:*")
        .withFilter("price:[1.0 TO *]")
        .withFilter("popularity:[0 TO 10]")
        .withFacet("avg_value", expandedStatFacet);
QueryResponse queryResponse = request.process(solrClient, COLLECTION_NAME);

ネストされたファセット

ネストされたファセット、またはサブファセットを使用すると、ドメインをバケットに分割する任意のファセットコマンド(つまり、termsrangequery)の下にファセットコマンドをネストできます。これらのサブファセットは、親のバケット内のすべての文書の集合によって定義されるドメインに対して評価されます。

構文はトップレベルのファセットと同じです。親ファセットのファセットコマンドブロックにfacetコマンドを追加するだけです。技術的には、メインクエリとフィルターによって定義されたドメインを持つ単一のファセットバケットから始まるため、すべてのファセットコマンドは実際にはサブファセットです。

ネストされたファセットの例

カテゴリフィールドcatに関する単純なネストされていないtermsファセットから始めましょう。

curl

curl https://127.0.0.1:8983/solr/techproducts/query -d '
{
  "query": "*:*",
  "facet": {
    "categories": {
      "type": "terms",
      "field": "cat",
      "limit": 3
    }
  }
}'

SolrJ

final TermsFacetMap categoryFacet = new TermsFacetMap("cat").setLimit(3);
final JsonQueryRequest request =
    new JsonQueryRequest().setQuery("*:*").withFacet("categories", categoryFacet);
QueryResponse queryResponse = request.process(solrClient, COLLECTION_NAME);

上記のファセットのレスポンスには、トップカテゴリと、各カテゴリバケットに分類される文書数が表示されます。ネストされたファセットを使用すると、文書のバケットごとに追加情報を収集できます。たとえば、以下のネストされたファセットを使用すると、トップカテゴリと、各カテゴリにおける主要なメーカーを見つけることができます。

curl

curl https://127.0.0.1:8983/solr/techproducts/query -d '
{
  "query": "*:*",
  "facet": {
    "categories": {
      "type": "terms",
      "field": "cat",
      "limit": 3,
      "facet": {
        "top_manufacturer": {
          "type": "terms",
          "field": "manu_id_s",
          "limit": 1
        }
      }
    }
  }
}'

SolrJ

final TermsFacetMap topCategoriesFacet = new TermsFacetMap("cat").setLimit(3);
final TermsFacetMap topManufacturerFacet = new TermsFacetMap("manu_id_s").setLimit(1);
topCategoriesFacet.withSubFacet("top_manufacturers", topManufacturerFacet);
final JsonQueryRequest request =
    new JsonQueryRequest().setQuery("*:*").withFacet("categories", topCategoriesFacet);
QueryResponse queryResponse = request.process(solrClient, COLLECTION_NAME);

レスポンスは次のようになります。

"facets":{
    "count":32,
    "categories":{
      "buckets":[{
          "val":"electronics",
          "count":12,
          "top_manufacturer":{
            "buckets":[{
                "val":"corsair",
                "count":3}]}},
        {
          "val":"currency",
          "count":4,
          "top_manufacturer":{
            "buckets":[{
                "val":"boa",
                "count":1}]}}]}}

ネストされた関数によるファセットのソート

フィールドまたはtermsファセットのデフォルトのソートは、バケットカウントの降順です。オプションで、各バケットに表示される任意のファセット関数で昇順または降順にsortできます。

curl

curl https://127.0.0.1:8983/solr/techproducts/query -d '
{
  "query": "*:*",
  "facet": {
    "categories":{
      "type" : "terms",     // terms facet creates a bucket for each indexed term in the field
      "field" : "cat",
      "limit": 3,
      "sort" : "avg_price desc",
      "facet" : {
        "avg_price" : "avg(price)",
      }
    }
  }
}'

SolrJ

final TermsFacetMap topCategoriesFacet =
    new TermsFacetMap("cat")
        .setLimit(3)
        .withStatSubFacet("avg_price", "avg(price)")
        .setSort("avg_price desc");
final JsonQueryRequest request =
    new JsonQueryRequest().setQuery("*:*").withFacet("categories", topCategoriesFacet);
QueryResponse queryResponse = request.process(solrClient, COLLECTION_NAME);

場合によっては、必要なsortが、すべてのバケットに対して計算コストの高い集計関数になることがあります。prelim_sortオプションを使用して、sortの近似値を指定し、トップ候補を決定するためにバケットを最初にランク付けできます(limitoverrequestに基づきます)。トップ候補のバケットが絞り込まれてから、実際のsortが使用されます。

{
  categories:{
    type : terms,
    field : cat,
    refine: true,
    limit: 10,
    overrequest: 100,
    prelim_sort: "sales_rank desc",
    sort : "prod_quality desc",
    facet : {
      prod_quality : "avg(div(prod(rating,sales_rank),prod(num_returns,price)))"
      sales_rank : "sum(sales_rank)"
    }
  }
}

ドメインの変更

上記のように、ファセットは文書の「ドメイン」に基づいてバケットまたは統計を計算します。

  • デフォルトでは、トップレベルのファセットは、メインクエリと一致するすべての文書の集合をドメインとして使用します。

  • ネストされた「サブファセット」は、親ファセットの各バケットに対して、そのバケット内のすべての文書を含むドメインを使用して計算されます。

このデフォルトの動作に加えて、ドメインを広げたり、狭めたり、完全に変更したりすることもできます。JSONファセッティングAPIは、domainプロパティを使用してドメインの変更をサポートしています。これについては、JSONファセッティングドメインの変更で詳しく説明されています。

特殊な統計ファセット関数

ほとんどの統計ファセット関数(avgsumsqなど)を使用すると、文書のグループに対して数学計算を実行できます。ただし、一部の関数はより複雑で、独自の解説が必要です。これらについては、以下のセクションで詳しく説明します。

uniqueBlock()とブロック結合カウント

コレクションにネストされた文書が含まれている場合、親文書を検索し、影響を受けるすべての子文書に対して統計を計算する場合(またはその逆)、blockChildrenおよびblockParentドメインの変更が役立つ場合があります。しかし、現在のドメインに存在するすべてのブロックのだけを知る必要がある場合は、より効率的なオプションとしてuniqueBlock()集計関数があります。

複数のSKUを持つ製品があるとします。各色について製品をカウントしたいとします。

{
  "id": "1", "type": "product", "name": "Solr T-Shirt",
  "_childDocuments_": [
    { "id": "11", "type": "SKU", "color": "Red",  "size": "L" },
    { "id": "12", "type": "SKU", "color": "Blue", "size": "L" },
    { "id": "13", "type": "SKU", "color": "Red",  "size": "M" }
  ]
},
{
  "id": "2", "type": "product", "name": "Solr T-Shirt",
  "_childDocuments_": [
    { "id": "21", "type": "SKU", "color": "Blue", "size": "S" }
  ]
}

一連のSKU文書に対して検索を実行する場合、色に関するファセットを要求し、ネストされた統計ですべての「ブロック」(別名:製品)をカウントできます。

color: {
  type: terms,
  field: color,
  limit: -1,
  facet: {
    productsCount: "uniqueBlock(_root_)"
      // or "uniqueBlock({!v=type:product})"
  }
}

そして、次の結果が得られます。

color:{
   buckets:[
      { val:Blue, count:2, productsCount:2 },
      { val:Red, count:2, productsCount:1 }
   ]
}

_root_は、親文書を参照するためにLuceneによって各子文書に追加される内部フィールドであることに注意してください。集計uniqueBlock(_root_)は機能的にunique(_root_)と同等ですが、ネストされた文書のブロック構造に対して最適化されています。上記の例のように、uniqueBlock計算にはlimit: -1を定義することをお勧めします。これは、limitパラメータのデフォルト値が10であるのに対し、uniqueBlock-1の方がはるかに高速であるためです。

relatedness()とセマンティックナレッジグラフ

relatedness(…​)統計関数は、「セマンティックナレッジグラフ」を構成するアドホックな関係を見つける目的で、文書の集合をフォアグラウンドとバックグラウンドの文書集合を基準にスコア付けすることを可能にします。

セマンティックナレッジグラフの中心は、逆インデックスと相補的な非逆インデックスを活用して、ノード(用語)とエッジ(複数の用語/ノードの交差する投稿リスト内の文書)を表すことです。これにより、各ノードのペアとその対応するエッジの間に間接参照レイヤーが提供され、エッジが基礎となるコーパス統計から動的にマテリアライズすることが可能になります。その結果、ノードの任意の組み合わせで、他のノードへのエッジがマテリアライズされ、スコア付けされて、ノード間の潜在的な関係を明らかにすることができます。

relatedness(…​)関数は、関数パラメータでクエリとして指定された「フォアグラウンド」と「バックグラウンド」の文書集合を基準に、これらの関係を「スコア付け」するために使用されます。

ほとんどの集計関数とは異なり、relatedness(…​)関数は、ネストされたファセットでどのように使用されているかを認識しています。これは、現在のバケットを定義するクエリを親/祖先バケットとは独立して評価し、フォアグラウンドクエリによって定義された「フォアグラウンド集合」と祖先バケットを組み合わせたものと、それらの文書を交差させます。次に、その結果をバックグラウンドクエリによって排他的に定義された「バックグラウンド集合」に対して行われた同様の交差と比較して、バックグラウンド集合を基準に、現在のバケットとフォアグラウンド集合の間に正の相関または負の相関があるかどうかを確認します。

allBucketsコンテキストでのrelatedness(…​)のセマンティクスは現在未定義です。したがって、allBuckets:trueも指定するファセットリクエストに対してrelatedness(…​)統計を指定することはできますが、allBucketsバケット自体には関連性計算が含まれません。
バックグラウンド集合を*:*またはフォアグラウンドクエリの他のスーパーセットとして定義することが非常に一般的ですが、厳密に必須ではありません。relatedness(…​)関数は、直交するフォアグラウンド/バックグラウンドクエリに対する文書集合の統計的関連性を比較するために使用できます。

relatedness()オプション

relatedness()集計を指定するために拡張type:func構文を使用する場合、オプションのmin_popularity(浮動小数点数)オプションを使用して、relatednessスコアが有効になるために満たす必要があるforeground_popularitybackground_popularity値の下限を指定できます。このmin_popularityが満たされない場合、relatednessスコアは-Infinityになります。

relatedness()ドメイン相関を計算するためのデフォルトの実装は、計算されるファセットの種類によって異なります。一般的なドメイン相関は、各バケット関連クエリに対してDocSetを選択的に取得し(filterCacheを参照)、"フォアグラウンド"と"バックグラウンド"の集合とのDocSetの交差を計算することによって、用語ごとに計算されます。termsファセット(特に高カーディナリティフィールド上)では、このアプローチはfilterCacheの激しい処理につながる可能性があります。したがって、termsファセット上のrelatedness()は、可能な限り、すべての複数のドメインを一度にスキャンしてファセットカウントを直接収集するアプローチ(filterCacheにアクセスしない)をデフォルトで使用します。この「単一スキャン」の収集は、拡張type:func構文のsweep_collectionオプションをtrue(デフォルト)またはfalse(スキャン収集を無効にする)に設定することで明示的に制御できます。

低カーディナリティフィールド上のrelatedness()統計に対してスキャン収集を無効にすると、予想される使用パターンに対してfilterCacheが関連フィールドの各値のエントリを十分に収容できる場合、パフォーマンスの向上が得られる可能性があります。妥当なヒューリスティックは、1,000未満のカーディナリティのフィールドがスキャンの無効化から恩恵を受ける可能性があることです。このヒューリスティックは、デフォルトの動作を決定するために使用されていません。特に、非スキャン収集は、システム全体に悪影響を与える可能性があるため、filterCacheの激しい処理を簡単に引き起こす可能性があるからです。
{ "type": "func",
  "func": "relatedness($fore,$back)",
  "min_popularity": 0.001,
}

これは、フォアグラウンドとバックグラウンドのクエリが互いに素である場合に、relatedness()で降順ソートを使用する場合に特に役立ち、「トップバケット」が両方の集合に関連していることを保証します。

relatedness(…​)でソートする場合、prelim_sort: "count desc"オプションを追加することで、リクエストをはるかに迅速に処理できます。overrequestを増やすと、トップバケットの精度を向上させることができます。

セマンティックナレッジグラフの例

サンプル文書
curl -sS -X POST 'https://127.0.0.1:8983/solr/gettingstarted/update?commit=true' -d '[
{"id":"01",age:15,"state":"AZ","hobbies":["soccer","painting","cycling"]},
{"id":"02",age:22,"state":"AZ","hobbies":["swimming","darts","cycling"]},
{"id":"03",age:27,"state":"AZ","hobbies":["swimming","frisbee","painting"]},
{"id":"04",age:33,"state":"AZ","hobbies":["darts"]},
{"id":"05",age:42,"state":"AZ","hobbies":["swimming","golf","painting"]},
{"id":"06",age:54,"state":"AZ","hobbies":["swimming","golf"]},
{"id":"07",age:67,"state":"AZ","hobbies":["golf","painting"]},
{"id":"08",age:71,"state":"AZ","hobbies":["painting"]},
{"id":"09",age:14,"state":"CO","hobbies":["soccer","frisbee","skiing","swimming","skating"]},
{"id":"10",age:23,"state":"CO","hobbies":["skiing","darts","cycling","swimming"]},
{"id":"11",age:26,"state":"CO","hobbies":["skiing","golf"]},
{"id":"12",age:35,"state":"CO","hobbies":["golf","frisbee","painting","skiing"]},
{"id":"13",age:47,"state":"CO","hobbies":["skiing","darts","painting","skating"]},
{"id":"14",age:51,"state":"CO","hobbies":["skiing","golf"]},
{"id":"15",age:64,"state":"CO","hobbies":["skating","cycling"]},
{"id":"16",age:73,"state":"CO","hobbies":["painting"]},
]'
クエリ例
curl -sS -X POST https://127.0.0.1:8983/solr/gettingstarted/query -d 'rows=0&q=*:*
&back=*:*                                  (1)
&fore=age:[35 TO *]                        (2)
&json.facet={
  hobby : {
    type : terms,
    field : hobbies,
    limit : 5,
    sort : { r1: desc },                   (3)
    facet : {
      r1 : "relatedness($fore,$back)",     (4)
      location : {
        type : terms,
        field : state,
        limit : 2,
        sort : { r2: desc },               (3)
        facet : {
          r2 : "relatedness($fore,$back)"  (4)
        }
      }
    }
  }
}'
1 コレクション全体を「バックグラウンド集合」として使用します。
2 「age >= 35」のクエリを使用して、(初期)「フォアグラウンド集合」を定義します。
3 トップレベルのhobbiesファセットとstateのサブファセットの両方で、relatedness(…​)値でソートします。
4 relatedness(…​)関数の両方の呼び出しで、パラメータ変数を使用して、事前に定義されたforebackクエリを参照します。
ファセットレスポンス
"facets":{
  "count":16,
  "hobby":{
    "buckets":[{
        "val":"golf",
        "count":6,                                (1)
        "r1":{
          "relatedness":0.01225,
          "foreground_popularity":0.3125,         (2)
          "background_popularity":0.375},         (3)
        "location":{
          "buckets":[{
              "val":"az",
              "count":3,
              "r2":{
                "relatedness":0.00496,            (4)
                "foreground_popularity":0.1875,   (6)
                "background_popularity":0.5}},    (7)
            {
              "val":"co",
              "count":3,
              "r2":{
                "relatedness":-0.00496,           (5)
                "foreground_popularity":0.125,
                "background_popularity":0.5}}]}},
      {
        "val":"painting",
        "count":8,                                (1)
        "r1":{
          "relatedness":0.01097,
          "foreground_popularity":0.375,
          "background_popularity":0.5},
        "location":{
          "buckets":[{
            ...
1 hobbies:golfの総ファセットcounthobbies:paintingよりも低いですが、relatednessスコアは高くなっています。これは、バックグラウンド集合(コレクション全体)を基準に、ゴルフはフォアグラウンド集合(35歳以上の人)とペイントよりも強い相関関係を持っていることを示しています。
2 age:[35 TO *]hobbies:golfの両方に一致する文書数は、バックグラウンド集合の総文書数の31.25%です。
3 バックグラウンド集合の文書の37.5%がhobbies:golfに一致します。
4 アリゾナ州(AZ)は、バックグラウンド集合と比較して、ネストされたフォアグラウンド集合(35歳以上のゴルファー)と正の関連相関関係があります。つまり、「アリゾナ州の人々は、全国全体と比べて、統計的に『35歳以上のゴルファー』である可能性が高い」ということです。
5 コロラド州(CO)は、ネストされたフォアグラウンド集合と負の相関関係があります。つまり、「コロラド州の人々は、全国全体と比べて、統計的に『35歳以上のゴルファー』である可能性が低い」ということです。
6 age:[35 TO *]hobbies:golfstate:AZのすべてに一致する文書数は、バックグラウンド集合の総文書数の18.75%です。
7 バックグラウンド集合の文書の50%がstate:AZに一致します。