Solr のドキュメントにpriceというフィールドがあり、そのフィールドがファセットされているとします。ファセットを値の範囲として取得したい (例: 0-100、100-500、500-1000 など)。どうやってするの?
事前に範囲を指定することはできますが、ドキュメントの値に基づいて範囲 (たとえば 5 つの値) を自動的に計算できるかどうかも知りたいですか?
最初の質問に答えるために、汎用ファセット クエリ サポートを使用してファセット範囲を取得できます。例を次に示します。
http://localhost:8983/solr/select?q=video&rows=0&facet=true&facet.query=price:[*+TO+500]&facet.query=price:[500+TO+*]
2 番目の質問 (自動的にファセット範囲を提案する) については、まだ実装されていません。Solr に最適なファセット範囲を「推測」させるよりも、この種のクエリをアプリケーションに実装するのが最適であると主張する人もいます。
このトピックに関するいくつかの議論を以下に示します。
私は、製品の価格帯について適切な動的ファセットを計算する方法を考え出しました。このソリューションには、ドキュメントの前処理とクエリ結果の後処理が含まれますが、Solr へのクエリは 1 つしか必要なく、1.4 のような古いバージョンの Solr でも動作するはずです。
まず、ドキュメントを送信する前に、価格を最も近い「ナイス ラウンド ファセット境界」に切り上げ、「rounded_price」フィールドに格納します。ユーザーは、ファセットが「247-483」ではなく「250-500」のように見えることを好みます。また、丸めは、数百万ではなく数百の価格ファセットを返すことも意味します。少し努力すれば、次のコードを一般化して、任意の価格スケールで適切に丸めることができます。
public static decimal RoundPrice(decimal price)
{
if (price < 25)
return Math.Ceiling(price);
else if (price < 100)
return Math.Ceiling(price / 5) * 5;
else if (price < 250)
return Math.Ceiling(price / 10) * 10;
else if (price < 1000)
return Math.Ceiling(price / 25) * 25;
else if (price < 2500)
return Math.Ceiling(price / 100) * 100;
else if (price < 10000)
return Math.Ceiling(price / 250) * 250;
else if (price < 25000)
return Math.Ceiling(price / 1000) * 1000;
else if (price < 100000)
return Math.Ceiling(price / 2500) * 2500;
else
return Math.Ceiling(price / 5000) * 5000;
}
許容価格は、1、2、3、...、24、25、30、35、...、95、100、110、...、240、250、275、300、325、...、975、1000 などです。
次に、クエリを送信するときに、価格で並べ替えられた丸められた価格のすべてのファセットを要求しますfacet.field=rounded_price
。丸めのおかげで、最大で数百のファセットが返されます。
第 3 に、結果が得られた後、ユーザーは数百のファセットではなく、3 から 7 のファセットのみを表示したいと考えています。したがって、隣接するファセットをいくつかの大きなファセット (「セグメント」と呼ばれる) に結合して、各セグメントでほぼ同数のドキュメントを取得しようとします。次のかなり複雑なコードはこれを行い、範囲クエリの実行に適した (開始、終了、カウント) のタプルを返します。価格が最も近い境界に切り上げられていれば、返されるカウントは正しいものになります。
public static List<Tuple<string, string, int>> CombinePriceFacets(int nSegments, ICollection<KeyValuePair<string, int>> prices)
{
var ranges = new List<Tuple<string, string, int>>();
int productCount = prices.Sum(p => p.Value);
int productsRemaining = productCount;
if (nSegments < 2)
return ranges;
int segmentSize = productCount / nSegments;
string start = "*";
string end = "0";
int count = 0;
int totalCount = 0;
int segmentIdx = 1;
foreach (KeyValuePair<string, int> price in prices)
{
end = price.Key;
count += price.Value;
totalCount += price.Value;
productsRemaining -= price.Value;
if (totalCount >= segmentSize * segmentIdx)
{
ranges.Add(new Tuple<string, string, int>(start, end, count));
start = end;
count = 0;
segmentIdx += 1;
}
if (segmentIdx == nSegments)
{
ranges.Add(new Tuple<string, string, int>(start, "*", count + productsRemaining));
break;
}
}
return ranges;
}
4 番目に、("250","500",38) が結果のセグメントの 1 つであるとします。ユーザーがフィルターとして「250 ドルから 500 ドル」を選択した場合は、単純にフィルター クエリを実行します。fq=price:[250 TO 500]
より良い Solr 固有の回答があるかもしれませんが、私は単純な Lucene を使用しています。そこで、オリジナルをラップして populate aFilter
を作成します。次に、関心のある分野の を取得します。フィルターのビットセットでヒットを列挙し、ヒットごとに、フィールド キャッシュからフィールドの値を取得し、SortedSet に追加します。すべてのヒットを取得したら、セットのサイズを必要な範囲の数に分割します (ユーザー インターフェイスの関係者によると、5 から 7 が適切な数です)。単一値の制約ではなく、ファセットは次のようになります。これらの各サブセットの下限と上限を持つ範囲クエリになります。FilteredQuery
Query
FieldCache
少数の値には特殊なケースのロジックを使用することをお勧めします。明らかに、4 つの個別の値しかない場合、それらから 5 つの範囲を絞り込むことは意味がありません。特定のしきい値 (たとえば、範囲の理想的な数の 3 倍) を下回ると、範囲ではなくファセットを通常どおりに表示します。
solrファセット範囲を使用できます
http://wiki.apache.org/solr/SimpleFacetParameters#Facet_by_Range