在Elasticsearch中不使用脚本来查找匹配的位置/距离?

时间:2015-09-16 13:44:05

标签: performance groovy elasticsearch geospatial

我正在使用Elasticsearch在查找其他用户时存储用户位置及其距离偏好。它存储在location geo_point和distance整数中。

例如,索引包含以下文档:

  • Alice,位于[0,100]并在100米范围内寻找用户;
  • Bob,位于[100,0],正在寻找50米范围内的用户。

当位于[0,0]的Carlos在100米范围内搜索时,我需要我的查询返回Alice,但不是 Bob(因为Bob只需要50米以内的用户,而Carlos距离100米)。

换句话说,我想返回所有文档D,以便D.reach包含Carlos.locationCarlos.reach包含D.location

据我所知,唯一的方法是将距离与脚本进行比较,如下所示:

{
    "filter": {
        "script": {
            "script": "min(doc['distance'].value, distance) >= doc['location'].arcDistance(lat, lon)",
            "params": {
                "distance": 100,
                "lat": 0,
                "lon": 0
            }
        }
    }
}

但是,如果可能的话我会rather avoid scripting。有没有其他方法可以实现这一目标?

2 个答案:

答案 0 :(得分:0)

值得调查的另一种方法是使用geo_shape circle。因此,除了(或除了)存储locationdistance的离散值之外,您还可以将这两个值的组合存储为表示用户的reach的圆圈。在您的映射中,它看起来像这样:

{
    "properties": {
        "reach": {
            "type": "geo_shape",
            "tree": "quadtree",
            "precision": "10cm"
        }
    }
}

然后,当您为文档编制索引时,您可以像这样指定reach圈:

{
    "name": "Alice",
    "reach" : {
        "type" : "circle",
        "coordinates" : [0.0, 100.0],    <---- Alice's current location field
        "radius" : "100m"                <---- Alice's current distance field
    }
}
{
    "name": "Bob",
    "reach" : {
        "type" : "circle",
        "coordinates" : [100.0, 0.0],    <---- Bob's current location field
        "radius" : "50m"                 <---- Bob's current distance field
    }
}

此时,您的所有用户都会geo_shape与他们相关联,代表他们的覆盖面。现在,您可以释放ES地理查询和过滤器的强大功能,以便查找交叉点或您拥有的内容,例如使用geo_shape filter。我们的想法是过滤另一个geo_shape,代表搜索其他用户的用户的覆盖面(例如上面的Carlos)

{
    "query":{
        "filtered": {
            "filter": {
                "geo_shape": {
                    "location": {
                        "shape": {
                            "type": "circle",
                            "coordinates" : [0.0, 0.0]   <--- Carlos location
                            "radius": "100m"             <--- Carlos reach
                        }
                    }
                }
            }
        }
    }
}

上述查询将查找其覆盖范围与Carlos相交的所有文档(即用户)。过滤器中指定的到达范围。试一试。

答案 1 :(得分:0)

由于Val的答案指向了正确的方向,我使用了以下解决方案。

文档看起来像这样,包含用户&#39;地址为geo_point,并以geo_shape为目标。

{
    "name": "Alice",
    "location" : [1,0],
    "reach" : {
        "type": "shape",
        "coordinates": [1,0],
        "radius": 100
    }
}

然后查询包含两个过滤器;一个匹配卡洛斯&#39;用户内部的位置&#39;到达,另一个用于匹配用户在卡洛斯内部的位置&#39;达到。

{
    "filter": {
        "and" : [
            {
                "geo_shape": {
                    "preferences.reach": {
                        "shape": {
                            "type": "Point",
                            "coordinates": Carlos.location
                        }
                    }
                }
            },
            {
                "geo_distance": {
                    "distance": Carlos.distance,
                    "user.location" : Carlos.location
                }
            }
        ]
    }
}

这可以通过两个geo_shape来完成,但geo_point更高效。