15

假设我在 Solr 中有一个名为price的文档字段,并且我对该字段进行了分面。我想将方面作为值范围(例如:0-100、100-500、500-1000 等)。怎么做?

我可以事先指定范围,但我也想知道是否可以根据文档中的值自动计算范围(比如 5 个值)?

4

4 回答 4

14

要回答您的第一个问题,您可以使用通用构面查询支持来获取构面范围。是一个例子:

http://localhost:8983/solr/select?q=video&rows=0&facet=true&facet.query=price:[*+TO+500]&facet.query=price:[500+TO+*]

至于您的第二个问题(自动建议方面范围),尚未实施。有人认为这种查询最好在您的应用程序上实现,而不是让 Solr “猜测”最佳方面范围。

以下是有关该主题的一些讨论:

于 2008-10-04T15:04:56.923 回答
7

我已经研究出如何计算产品价格范围的合理动态方面。该解决方案涉及一些文档的预处理和一些查询结果的后处理,但它只需要对 Solr 进行一次查询,甚至应该适用于 Solr 1.4 等旧版本。

提交前四舍五入价格

首先,在提交文档之前,将价格四舍五入到最近的“nice round facet boundary”,并将其存储在“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 to $500”作为过滤器,只需进行过滤器查询fq=price:[250 TO 500]

于 2012-07-04T13:14:58.640 回答
4

可能会有更好的 Solr 特定答案,但我使用的是直接的 Lucene,因为你没有得到太多的牵引力,所以我会尝试一下。在那里,我将创建一个填充 aFilterFilteredQuery包装原始Query. 然后我会得到一个FieldCache感兴趣的领域。枚举过滤器的 bitset 中的命中,对于每个命中,您从字段缓存中获取字段的值,并将其添加到 SortedSet。当你得到所有的命中后,将集合的大小分成你想要的范围数(根据用户界面的人来说,五到七是一个很好的数字),而不是单值约束,你的方面将是一个范围查询,其中每个子集的下限和上限。

我建议对少量值使用一些特殊情况的逻辑;显然,如果您只有四个不同的值,那么尝试对它们进行 5 次范围改进是没有意义的。低于某个阈值(例如 3*您的理想范围数),您只会正常显示方面而不是范围。

于 2008-08-29T05:53:19.837 回答
4

您可以使用 solr 方面范围

http://wiki.apache.org/solr/SimpleFacetParameters#Facet_by_Range

于 2012-05-15T10:42:39.767 回答