Как получить диапазоны фасет в результатах solr?
Предположим, что у меня есть поле, называемое ценой для документов в Solr, и у меня есть это поле. Я хочу получить грани в виде диапазонов значений (например: 0-100, 100-500, 500-1000 и т.д.). Как это сделать?
Я могу заранее задать диапазоны, но я также хочу знать, можно ли автоматически рассчитывать диапазоны (например, для 5 значений) на основе значений в документах?
Ответы
Ответ 1
Чтобы ответить на ваш первый вопрос, вы можете получить диапазоны фасет, используя общую поддержку запросов фасет. Здесь пример:
http://localhost:8983/solr/select?q=video&rows=0&facet=true&facet.query=price:[*+TO+500]&facet.query=price:[500+TO+*]
Что касается вашего второго вопроса (автоматически предлагающего диапазоны фасет), это еще не реализовано. Некоторые утверждают, что такой запрос будет лучше всего реализован в вашем приложении, а это позволит Solr "угадать" лучшие диапазоны фасет.
Вот несколько дискуссий по теме:
Ответ 2
Я разработал, как рассчитать разумные динамические грани для диапазонов цен на товары. Решение включает в себя некоторую предварительную обработку документов и некоторую пост-обработку результатов запроса, но для Solr требуется только один запрос, и он должен работать даже с старой версией Solr, как 1.4.
Завершить цены перед отправкой
Во-первых, перед отправкой документа округлить цену до ближайшей "красивой грани граничной грани" и сохраните ее в поле "rounded_price". Такие пользователи, как их грани, выглядят как "250-500", а не "247-483", а округление также означает, что вы получаете сотни ценовых граней, а не миллионы. С некоторыми усилиями следующий код может быть обобщен, чтобы округлить красиво в любой ценовой шкале:
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-7 фасетов, а не сотни фасетов. Итак, объедините смежные грани в несколько больших граней (называемых "сегментами" ), пытаясь получить примерно одинаковое количество документов в каждом сегменте. Следующий более сложный код делает это, возвращая кортежи (start, end, count), подходящие для выполнения запросов диапазона. Возвращенные счета будут корректными, если цены будут округлены до ближайшей границы:
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;
}
Отфильтровать результаты по выделенной грани
В-четвертых, предположим, что ( "250", "500", 38) был одним из полученных сегментов. Если пользователь выбирает "от $250 до $500" в качестве фильтра, просто выполните запрос фильтра fq=price:[250 TO 500]
Ответ 3
Может быть, лучший ответ Solr, но я работаю с прямой Lucene, и, поскольку вы не получаете большой тяги, я возьму удар. Там я создам заполнение Filter
с FilteredQuery
, обертывающим оригинал Query
. Тогда я получу FieldCache
для интересующего поля. Перечислите хиты в наборе бит фильтра, и для каждого удара вы получите значение поля из кеша поля и добавьте его в SortedSet. Когда у вас есть все хиты, разделите размер набора на количество диапазонов, которые вы хотите (пять-семь - это хорошее число в соответствии с парнями пользовательского интерфейса), а не однозначное ограничение, ваши грани будут быть запросом диапазона с нижней и верхней границами каждого из этих подмножеств.
Я бы рекомендовал использовать некоторую специальную логику для небольшого числа значений; очевидно, если у вас есть только четыре разных значения, нет смысла пытаться сделать из него 5 уточнений диапазона. Ниже определенного порога (скажем, 3 * ваше идеальное количество диапазонов) вы просто показываете грани, а не диапазоны.
Ответ 4
Вы можете использовать диапазоны грантов solr
http://wiki.apache.org/solr/SimpleFacetParameters#Facet_by_Range