如何评估弹性搜索NEST中的字段的重要性?

时间:2017-07-09 09:59:16

标签: c# .net elasticsearch nest

我试图查看Boosting和“功能评分查询”,但要么不了解如何将它们用于我的目的,要么没有找到使用什么技术来实现我的目标。

TL; DR 用户告诉我他对我产品的不同领域/方面的偏好,我希望弹性搜索能够将最符合他喜好的产品返回给我。 这可能吗?

我有一个课程,其中有很多字段作为数字给出。 e.g:

public class Product
{
   public double? Weight { get; set; }
   public int? Price { get; set; }
   public double? Size { get; set; }
}

搜索将基于(在运行时决定的)一系列优先级/分数。 e.g。

Weight: 0 negative
Price: 5 negative
Size: 8 positive

这些分数(在0到10之间归一化)意味着该用户不关心产品的重量,他关心价格,并且他希望它与该领域的价值负相关(例如他希望价格低,但“仅”,重要性为10分中的10分。对于这个用户来说最重要的是尺寸,这对于“大”非常重要。

对于这个例子,我想在我的所有产品之间进行搜索,但是对于大尺寸的产品给予更高的分数,并使价格更低是“中等”重要的,而不是关心重量。

这样的查询怎么样?

P.S。任何链接到NEST /弹性搜索的文档/指南将不胜感激。我还没有找到有用的官方文档。

编辑: 让我重新说一下: 用户告诉我产品的不同方面有多重要。例如价格,重量和尺寸。对于一些用户来说,低重量是非常重要的(即他们得分低重量= 10的重要性),对其他用户来说价格非常重要,而对某些用户来说重量很重要。对于其中一些都不重要,对我产品的某些领域来说很重要。

在用户评估了我产品各个方面的重要性后,我需要搜索最符合用户偏好的产品。

因此,如果用户认为重量和价格是最重要的,我希望弹性产品具有非常低的重量和价格,而不关心尺寸。

示例: 在弹性我有4个产品:(重量= W,尺寸= S,价格= P)

P1: W=200, S=40, P=2500
P2: W=50, S=10, P=2000
P3: W=400, S=45, P=4000
P4: W=200, S=45, P=3000

低重量/价格=好,高尺寸=好

如果用户得分:

Weight=10, Price=0, Size=5

结果应该是它返回前X个结果,排序(使用弹性搜索中的得分系统?)如下:P2,P4,P1,P3(因为低价是最重要的,其次是大尺寸,价格无关紧要)

如果用户得分:

Weight=5, Price=3, Size=8

结果应该是它返回前X个结果,排序如下:P4,P3,P1,P2(因为高/大尺寸是最重要的,其次是低重量,价格不太重要)

1 个答案:

答案 0 :(得分:2)

首先,我不确定你知道你想要做什么,你的定义使用好或坏的词,这是定义一个程序的工资。 这是一个简单的程序,可以像你要求的那样做

var index = "product";
            var type = "product";

            var db = new ElasticClient(new Uri("http://localhost:9200"));

            await db.DeleteIndexAsync(index);

            //I am using dynamic data but you can use your class it's easear as well
            await db.IndexAsync(new 
            {
                name = "P1", W=200, S=40, P=2500
            }, i=>i.Index(index).Type(type));

            await db.IndexAsync(new 
            {
                name = "P2", W=50, S=10, P=2000
            }, i=>i.Index(index).Type(type));

            await db.IndexAsync(new 
            {
                name = "P3", W=400, S=100, P=1000
            }, i=>i.Index(index).Type(type));

            await db.IndexAsync(new 
            {
                name = "P4", W=200, S=45, P=3000
            }, i=>i.Index(index).Type(type));

            await Task.Delay(1000);

            //I think there needs to be some sort of normalizations on fields this is a max base normalization so we can use 
            var max = await db.SearchAsync<dynamic>(s =>
               s.Size(0)
               .Index(index)
               .Type(type)
               .Aggregations(aggr =>
                   aggr
                   .Min("maxWeight", f => f.Field("w"))
                   .Max("maxPrice", f => f.Field("s"))
                   .Max("maxSize", f => f.Field("p"))));

            // This is to calculate the factors the max value is to normalize multivariable data so all the values be on scale from 0-1
            //The max value will allways be 1 and the othhers will be a precentage of the max value this will only work for none negative values
            // You can use some other way of normalizing but this depends on the data.
            var paramsData1 = new
            {
                Weight = (10 - 5) / max.Aggs.Max("maxWeight").Value,
                Price = 3 / max.Aggs.Max("maxPrice").Value,
                Size = 8 / max.Aggs.Max("maxSize").Value
            };

            // The first query is based on busting the fields based on factors entered
            var items = await db.SearchAsync<dynamic>(s =>
                s.Index(index)
                .Type(type)
                .Query(q => q.FunctionScore(fs =>
                    fs.Functions(ff =>
                        ff.FieldValueFactor(fvf => fvf.Field("w").Factor(paramsData1.Weight))
                        .FieldValueFactor(fvf => fvf.Field("s").Factor(paramsData1.Size))
                        .FieldValueFactor(fvf => fvf.Field("p").Factor(paramsData1.Price)))
                    .BoostMode(FunctionBoostMode.Sum))));

            System.Console.WriteLine("______________________________");
            foreach (var item in items.Hits)
            {
                System.Console.WriteLine($"Name:{item.Source.name};S:{item.Source.s};W:{item.Source.w};P:{item.Source.p};");
            }


            var paramsData2 = new
            {
                //this is to reverse the data since from what I can tell lower is better
                Weight =(10 - 10) / max.Aggs.Max("maxWeight").Value,
                Price = 0 / max.Aggs.Max("maxPrice").Value,
                Size = 5 / max.Aggs.Max("maxSize").Value
            };

            //You can write you own score function and by hand if needed and do some sort of calculation.
            var itemsScript = await db.SearchAsync<dynamic>(s =>
                s.Index(index)
                .Type(type)
                .Query(q => q.FunctionScore(fs => fs.Functions(ff =>
                    ff.ScriptScore(
                    ss =>
                        ss.Script(script => script.Params(p =>
                            p.Add("Weight", paramsData2.Weight)
                            .Add("Price", paramsData2.Price)
                            .Add("Size", paramsData2.Weight))
                            .Inline("params.Weight * doc['w'].value + params.Price * doc['p'].value + params.Size * doc['s'].value")))))));

            System.Console.WriteLine("______________________________");
            foreach (var item in itemsScript.Hits)
            {
                System.Console.WriteLine($"Name:{item.Source.name};S:{item.Source.s};W:{item.Source.w};P:{item.Source.p};");
            }

但这只是一个开始Factor analysis是一个自学的研究领域。 以下是脚本和功能评分的一些链接,我希望它有所帮助。 https://www.elastic.co/guide/en/elasticsearch/painless/5.5/painless-examples.html https://www.elastic.co/guide/en/elasticsearch/guide/current/script-score.html https://www.elastic.co/guide/en/elasticsearch/guide/current/scoring-theory.html#scoring-theory https://jontai.me/blog/2013/01/advanced-scoring-in-elasticsearch/(In这个语法已经过时但逻辑仍然存在) https://qbox.io/blog/optimizing-search-results-in-elasticsearch-with-scoring-and-boosting