【问题标题】:Elasticsearch, Filter documents based on different radius for different geopoint fieldElasticsearch,根据不同地理点字段的不同半径过滤文档
【发布时间】:2023-03-04 08:38:01
【问题描述】:

我有类似这样的 ES 文档,我有一个带有类型字段的位置数组。

{
  "type": "A/B/C",
  "locations1": [
    {
      "lat": 19.0179332,
      "lon": 72.868069
    },
    {
      "lat": 18.4421771,
      "lon": 73.8585108
    }
  ]
}

类型值确定适用于该位置的距离。

假设A类的查询允许距离为10km,B类为100km,C类为1000km。 给定位置 L,我想找到所有满足给定位置的文档距离标准的文档,并且最终结果应该按距离排序。

我无法弄清楚如何为此使用动态半径。是否有可能或者我需要像这样更改我的文档结构?

编辑:

我也在考虑像这样破坏文档位置

  "locationsTypeA": [
    {
      "lat": 19.0179332,
      "lon": 72.868069
    },
    {
      "lat": 18.4421771,
      "lon": 73.8585108
    }
  ],
  "locationsTypeB": [
    {
      "lat": 19.0179332,
      "lon": 72.868069
    },
    {
      "lat": 18.4421771,
      "lon": 73.8585108
    }
  ],
  "locationsTypeC": [
    {
      "lat": 19.0179332,
      "lon": 72.868069
    },
    {
      "lat": 18.4421771,
      "lon": 73.8585108
    }
  ]
}

然后我可以使用查询

  "query": {
    "bool": {
      "should": [
        {
          "geo_distance": {
            "distance": "10km",
            "locationsTypeA": {
              "lat": 12.5,
              "lon": 18.2
            }
          }
        },
        {
          "geo_distance": {
            "distance": "100km",
            "locationsTypeB": {
              "lat": 12.5,
              "lon": 18.2
            }
          }
        },
        {
          "geo_distance": {
            "distance": "1000km",
            "locationsTypeC": {
              "lat": 12.5,
              "lon": 18.2
            }
          }
        }
      ]
    }
  }
}

【问题讨论】:

  • 您的查询是什么样的?
  • 由于我是 Es 新手,我想不出如何为此创建查询,通常我将其作为过滤器 "geo_distance": { "distance": "200km", "_geoloc": { "lat": lat, "lon": lon } } 但在这种情况下半径是动态的,不能也可以在文档中找到任何方法来做到这一点。

标签: elasticsearch


【解决方案1】:

使用第一个文档结构和映射如下:

PUT geoindex
{
  "mappings": {
    "properties": {
      "locations": {
        "type": "geo_point"
      }
    }
  }
}

让我们将浦那和孟买之间的一个随机点作为相对的原点,我们将使用arcDistance function 执行脚本地理查询:

GET geoindex/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "script": {
            "script": {
              "source": """
                def type = doc['type.keyword'].value;
                def dynamic_distance;
                if (type == "A") {
                  dynamic_distance = 10e3;
                } else if (type == "B") {
                  dynamic_distance = 100e3;
                } else if (type == "C") {
                  dynamic_distance = 1000e3;
                }

                def distance_in_m = doc['locations'].arcDistance(
                  params.origin.lat,
                  params.origin.lon
                );
                
                return distance_in_m < dynamic_distance
              """,
              "params": {
                "origin": {
                  "lat": 18.81531,
                  "lon": 73.49029
                }
              }
            }
          }
        }
      ]
    }
  },
  "sort": [
    {
      "_geo_distance": {
        "locations": {
          "lat": 18.81531,
          "lon": 73.49029
        },
        "order": "asc"
      }
    }
  ]
}

【讨论】:

  • 谢谢,乔。这很棒,它看起来确实很复杂,但解决了我的问题。关于你的分裂点,我已经用我想的方法更新了这个问题,请看看。
  • 不客气!至于您的编辑:是的,这也可以,但是当且仅当您也将这些字段 嵌套 时。如果您有对象数组,则它们的属性会变平,并且您会失去它们之间的连接。
  • 我正在浏览这个link,它说如果是位置数组,我在上面添加的查询会迭代并检查数组中的所有位置,如果有匹配则返回true。就我而言,我只需要满足一个条件,A、B 或 C 类型的位置都应该满足标准。我解释错了吗?如果您可以指向一个链接,那就太好了,关于扁平化数组中的对象,
  • 我的错——geo_points 不需要嵌套。如official docs 中所述,我将它们与数组内的常规键值对象混淆了。我将编辑我的答案。
【解决方案2】:

我做了类似但不太复杂的方法

代码如下:

{
            query: {
              bool: {
                must: [
                  {
                    match: {
                      companyName: {
                        query: req.text
                      }
                    }
                  },
                  {
                    script: {
                      script: {
                        params: {
                          lat: parseFloat(req.lat),
                          lon: parseFloat(req.lon)
                        },
                        source: "doc['location'].arcDistance(params.lat, params.lon) / 1000 < doc['searchRadius'].value",
                        lang: "painless"
                      }
                    }
                  }
                ]
              }
            },
            sort: [
                {
                    _geo_distance: {
                        location: {
                            lat: parseFloat(req.lat),
                            lon: parseFloat(req.lon)
                        },
                        order: "asc",
                        unit:"km"
                    }
                }
            ],

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多