在 Elasticsearch 中不使用脚本就能找到匹配的位置/距离?
Find matching locations / distances without using scripting in Elasticsearch?
我正在使用 Elasticsearch 来存储用户位置和他们在寻找其他用户时的距离偏好。这存储在 location
geo_point 和 distance
整数中。
例如,索引包含这些文档:
- Alice,位于[0,100],正在寻找100米内的用户;
- Bob,位于[100,0],正在寻找50米以内的用户。
当位于 [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
的圆圈,而不是(或除了)存储离散值 location
和 distance
。在您的映射中,它看起来像这样:
{
"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
的性能更高。
我正在使用 Elasticsearch 来存储用户位置和他们在寻找其他用户时的距离偏好。这存储在 location
geo_point 和 distance
整数中。
例如,索引包含这些文档:
- Alice,位于[0,100],正在寻找100米内的用户;
- Bob,位于[100,0],正在寻找50米以内的用户。
当位于 [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
的圆圈,而不是(或除了)存储离散值 location
和 distance
。在您的映射中,它看起来像这样:
{
"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
的性能更高。