ES 搜索22 (function_score 支持的衰减函数 linear、exp 和 gauss)

衰减函数 

很多变量都可以影响用户对于酒店的选择,像是用户可能希望酒店离市中心近一点,但是如果价格足够便宜,也愿意为了省钱,妥协选择一个更远的住处

如果我们只是使用一个 filter 排除所有市中心方圆 100 米以外的酒店,再用一个filter排除每晚价格超过100元的酒店,这种作法太过强硬,可能有一间房在 500米,但是超级便宜一晚只要10元,用户可能会因此愿意妥协住这间房

为了解决这个问题,因此function_score查询提供了一组 衰减函数 (decay functions), 让我们有能力在两个滑动标准(如地点和价格)之间权衡

origin

中心点 或字段可能的最佳值,落在原点 origin 上的文档评分 _score 为满分 1.0 。

scale

衰减率,即一个文档从原点 origin 下落时,评分 _score 改变的速度。(例如,每 £10 欧元或每 100 米)。

decay

从原点 origin 衰减到 scale 所得的评分 _score ,默认值为 0.5 。

offset

以原点 origin 为中心点,为其设置一个非零的偏移量 offset 覆盖一个范围,而不只是单个原点。在范围 -offset <= origin <= +offset 内的所有评分 _score 都是 1.0 。

origin : 中心点,或是字段可能的最佳值,落在原点(origin)上的文档评分_score为满分1.0,支持数值、时间 以及 "经纬度地理座标点"(最常用) 的字段

offset : 从 origin 为中心,为他设置一个偏移量offset覆盖一个范围,在此范围内所有的评分_score也都是和origin一样满分1.0

scale : 衰减率,即是一个文档从origin下落时,_score改变的速度

decay : 从 origin 衰减到 scale 所得的评分_score,默认为0.5 (一般不需要改变,这个参数使用默认的就好了)

function_score支持的衰减函数有三种,分别是 linear、exp 和 gauss

linear、exp、gauss三种衰减函数的差别只在于衰减曲线的形状,在DSL的语法上的用法完全一样

linear : 线性函数是条直线,一旦直线与横轴0香蕉,所有其他值的评分都是0

exp : 指数函数是先剧烈衰减然后变缓

guass(最常用) : 高斯函数则是钟形的,他的衰减速率是先缓慢,然后变快,最后又放缓

 

# 有下列3条数据

{ "language": "java", "like": 5 }
{ "language": "python", "like": 10 }
{ "language": "go", "like": 15 }


# GET 127.0.0.1/mytest/doc/_search

{
  "query": {
    "function_score": {
      "query": {
        "match_all": {}
      },
      "functions": [
        {
          "gauss": {
            "like": {
              # 如果不设置offset,offset默认为0  公式 : origin-offset <= value <= origin+offset
              # 范围在15-0 <= value <= 15+0的文档的评分_score都是满分1.0
              # 而在此范围之外,评分会开始衰减,衰减率由scale值(此处是5)和decay值(此处是默认值0.5)决定
              # 也就是说,在origin + offset + scale或是origin - offset - scale的点上,得到的分数仅有decay分
              "origin": "15",
              "scale": "5",
              "decay": "0.2"
            }
          }
        }
      ]
    }
  }
}


# 最后结果

"hits": [
    {
        "_score": 1,
        "_source": { "language": "go", "like": 15 }
    },
    {
        # 因为改变了decay=0.2,所以当位于 origin-offset-scale=10 的位置时,分数为decay,就是0.2
        "_score": 0.2, 
        "_source": { "language": "python", "like": 10 }
    },
    {
        "_score": 0.0016,
        "_source": { "language": "java", "like": 5 }
    }
]

 

案例2:

用户希望租一个离伦敦市中心近( { "lat": 51.50, "lon": 0.12} )且每晚不超过 £100 英镑的度假屋,而且与距离相比, 我们的用户对价格更为敏感,这样查询可以写成:

GET /_search
{
  "query": {
    "function_score": {
      "functions": [
        {
          "gauss": { 
            "location": {            1
              "origin": { "lat": 51.5, "lon": 0.12 },
              "offset": "2km",
              "scale":  "3km"
            }
          }
        },
        {
          "gauss": {
            "price": {               2
              "origin": "50",        3
              "offset": "50",
              "scale":  "20"
            }
          },
          "weight": 2                4
        }
      ]
    }
  }
}

1

location 字段以地理坐标点 geo_point 映射。

2

price 字段是数值。

3

参见 理解价格语句 ,理解 origin 为什么是 50 而不是 100 。

4

price 语句是 location 语句权重的两倍。

location 语句可以简单理解为:

  • 以伦敦市中作为原点 origin 。
  • 所有距原点 origin 2km 范围内的位置的评分是 1.0 。
  • 距中心 5km ( offset + scale )的位置的评分是 0.5 。

 

案例3:

地理坐标点(geo-point) 是指地球表面可以用经纬度描述的一个点。地理坐标点可以用来计算两个坐标位置间的距离,或者判断一个点是否在一个区域中。地理坐标点不能被动态映射(dynamic mapping)自动检测,而是需要显式声明对应字段类型为 geo_point 。

PUT /attractions
{
  "mappings": {
    "restaurant": {
      "properties": {
        "name": {
          "type": "string"
        },
        "location": {
          "type": "geo_point"
        }
      }
    }
  }
}

如上例,location 被声明为 geo_point 后,我们就可以索引包含了经纬度信息的文档了。经纬度信息的形式可以是字符串,数组或者对象。

PUT /attractions/restaurant/1
{
  "name":     "Chipotle Mexican Grill",
  "location": "40.715, -74.011"     <1>
}

PUT /attractions/restaurant/2
{
  "name":     "Pala Pizza",
  "location": {                     <2>
    "lat":     40.722,
    "lon":    -73.989
  }
}

PUT /attractions/restaurant/3
{
  "name":     "Mini Munchies Pizza",
  "location": [ -73.983, 40.719 ]   <3>
}

注意 : 
可能所有人都至少踩过一次这个坑:地理坐标点用字符串形式表示时是纬度在前,
经度在后(”latitude,longitude”),而数组形式表示时刚好相反,是经度在前,
纬度在后([longitude,latitude])。其实,在 Elasticesearch 内部,
不管字符串形式还是数组形式,都是纬度在前,经度在后。不过早期为了适配 GeoJSON 的格式规范,
调整了数组形式的表示方式。因此,在使用地理位置(geolocation)的路上就出现了这么一个“捕熊器”,
专坑那些不了解这个陷阱的使用者。
  • <1> 以半角逗号分割的字符串形式 “lat,lon”;
  • <2> 明确以 lat 和 lon 作为属性的对象;
  • <3> 数组形式表示 [lon,lat]。

 

通过地理坐标点过滤

有四种地理坐标点相关的过滤方式可以用来选中或者排除文档:

geo_bounding_box:: 
找出落在指定矩形框中的坐标点
geo_distance:: 
找出与指定位置在给定距离内的点
geo_distance_range:: 
找出与指定点距离在给定最小距离和最大距离之间的点
geo_polygon:: 
找出落在多边形中的点。这个过滤器使用代价很大。当你觉得自己需要使用它,最好先看看 geo-shapes

详细参考  : https://blog.csdn.net/u012332735/article/details/54971638

 

 

假设有一个用户希望租一个离市中心近一点的酒店,且每晚不超过100元的酒店,而且与距离相比,我们的用户对价格更敏感,那麽使用衰减函数guass查询如下

其中把price语句的origin点设为50是有原因的,由于价格的特性一定是越低越好,所以0~100元的所有价格的酒店都应该认为是比较好的,而100元以上的酒店就慢慢衰减

如果我们将price的origin点设置成100,那麽价格低于100元的酒店的评分反而会变低,这不是我们期望的结果,与其这样不如将origin和offset同时设成50,只让price大于100元时评分才会变低

虽然这样设置也会使得price小于0元的酒店评分降低没错,不过现实生活中价格不会有负数,因此就算price<0的评分会下降,也不会对我们的搜索结果造成影响(酒店的价格一定都是正的)

换句话说,其实只要把origin + offset的值设为100,origin或offset是什麽样的值都无所谓,只要能确保酒店价格在100元以上的酒店会衰减就好了

GET 127.0.0.1/mytest/doc/_search
{
    "query": {
        "function_score": {
            "functions": [
                //第一个gauss加强函数,决定距离的衰减率
                {
                    "gauss": {
                        "location": {
                            //origin点设成酒店的经纬度座标
                            "origin": {  
                                "lat": 51.5,
                                "lon": 0.12
                            },
                            //距离中心点2km以内都是满分1.0,2km外开始衰减
                            //衰减率
                            "offset": "2km", 
                            "scale": "3km"  
                        }
                    }
                },
                //第二个gauss加强函数,决定价格的衰减率,因为用户对价格更敏感,所以给了这个gauss加强函数2倍的权重
                {
                    "gauss": {
                        "price": {
                            "origin": "50", 
                            "offset": "50",
                            "scale": "20"
                        }
                    },
                    "weight": 2
                }
            ]
        }
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值