在 Elasticsearch 中不使用脚本就能找到匹配的位置/距离?

Find matching locations / distances without using scripting in Elasticsearch?

我正在使用 Elasticsearch 来存储用户位置和他们在寻找其他用户时的距离偏好。这存储在 location geo_point 和 distance 整数中。

例如,索引包含这些文档:

当位于 [0,0] 的 Carlos 在 100 米范围内搜索时,我需要向 return Alice 查询,但 not Bob(因为 Bob 只想要50m以内的用户,100m以内的Carlos)。

换句话说,我想 return 所有文档 D 这样 D.reach 包含 Carlos.location 并且 Carlos.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。是否有其他方法可以实现此目的?

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

{
    "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 范围相交。试一试。

感谢 Val 的回答为我指明了正确的方向,我使用了以下解决方案。

文档看起来像这样,包含 geo_point 的用户位置和 geo_shape 的到达范围。

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

查询然后包含两个过滤器;一个用于匹配 Carlos 在用户范围内的位置,另一个用于匹配用户在 Carlos 范围内的位置。

{
    "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 的性能更高。