2

我需要在 Painless 脚本中计算弧距,但在这种情况下还没有找到访问地理 API 的方法,即:

  • 第一点作为参数传递给脚本 - 这意味着我只得到原始值
  • 第二点是从嵌套文档中读取的——这意味着我无法使用doc[myGeoField].valueAPI读取该字段

在这两种情况下,我都无法实例化一个org.elasticsearch.index.fielddata.ScriptDocValues.GeoPoints可以让我访问该方法的.arcDistance()方法。

我也无法使用Painlessorg.elasticsearch.common.geo.GeoUtils中未列入白名单的课程。

在脚本中计算弧距还有其他选择吗?

注意:由于此处无法解释的原因,我必须为此用例使用脚本 - 在查询/过滤器中执行此操作不是一种选择。

4

2 回答 2

2

我用inner_hits解决了这个问题,请参见下面的查询:

{
    "query": {
        "nested": {
            "path": "campi",
            "query": {
                "query_string": {
                    "query": "*"
                }
            },
            "inner_hits": {
                "script_fields": {
                    "distanceInMeters": {
                        "script": {
                            "inline": "!doc['campi.location'].empty ? doc['campi.location'].arcDistance(params.lat, params.lon) : 0",
                            "lang": "painless",
                            "params": {
                                "lon": -43.9207766,
                                "lat": -19.910621
                            }
                        }
                    }
                }
            }
        }
    }
}

下面是一个响应示例:

{
    "took": 42,
    "timed_out": false,
    "terminated_early": false,
    "_shards": {
        "total": 3,
        "successful": 3,
        "failed": 0
    },
    "hits": {
        "total": 42,
        "max_score": 1.0,
        "hits": [
            {
                "_index": "search",
                "_type": "baseCourse",
                "_id": "1813-67-14-2309",
                "_score": 1.0,
                "_source": {
                    "cityName": "Belo Horizonte",
                    "institutionName": "CENTRO DE EDUCAÇÃO SUPERIOR",
                    "campi": [
                        {
                            "address_zipCode": null,
                            "address_street": "Avenida Contorno, 6.475 - São Pedro",
                            "stateCode": "MG",
                            "id": 33,
                            "campus_name": "Unidade SEDE"
                        }
                    ],
                    "baseCourseName": "ADMINISTRAÇÃO"
                },
                "inner_hits": {
                    "campi": {
                        "hits": {
                            "total": 1,
                            "max_score": null,
                            "hits": [
                                {
                                    "_nested": {
                                        "field": "campi",
                                        "offset": 0
                                    },
                                    "_score": null,
                                    "fields": {
                                        "distanceInMeters": [
                                            3593.558492923913
                                        ]
                                    },
                                    "sort": [
                                        3593.558492923913
                                    ]
                                }
                            ]
                        }
                    }
                }
            }
        ]
    }
}
于 2018-10-24T01:03:12.457 回答
2

由于 Elastic 的设计选择不佳,您尝试做的事情似乎是不可能的。

我试图做同样的事情(仅使用 planeDistance),并最终在 Painless 中实现了 planeDistance。

似乎 arcDistance 和 planeDistance 等方法莫名其妙地仅在GeoPoints类中实现,而不是在GeoPoint中实现,尽管它更有意义。此外,这些方法仅适用于GeoPoints 列表中的第一个元素。

我真的希望有人能纠正我,尽管我对 Painless 的体验一直很痛苦。如果您不希望经常更改它,我建议您使用更成熟的东西,例如 Java 插件。Painless 调试起来很痛苦,完全没有工具,也没有任何文档。他们还弃用了 Groovy 和 Python,好像 Painless 是一个不错的替代品。

不过,这里是以英里为单位的距离代码,用于完成(planeDistance 的版本,如果您不需要非常准确的距离,请使用它):

double lat1 = location1.lat;
double lon1 = location1.lon;
double lat2 = location2.lat;
double lon2 = location2.lon;
double to_radian = 0.01745329251; /* PI / 180 */
double earth_radius_in_miles = 3959;

double x = (lon2 - lon1) * to_radian * Math.cos((lat2 + lat1) / 2.0 * to_radian);
double y = (lat2 - lat1) * to_radian;
double distanceInMiles = Math.sqrt(x * x + y * y) * earth_radius_in_miles;
于 2017-05-09T18:31:19.723 回答