我试图查看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(因为高/大尺寸是最重要的,其次是低重量,价格不太重要)
答案 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