Elasticsearch,按地理距离和分数排序聚合

wos*_*tom 6 distance aggregation elasticsearch

我的映射如下:

PUT places
{
  "mappings": {
    "test": {
      "properties": {
        "id_product": { "type": "keyword" },
        "id_product_unique": { "type": "integer" },
        "location": { "type": "geo_point" },
        "suggest": {
          "type": "text"
        },
        "active": {"type": "boolean"}
      }
    }
  }
}

POST places/test
{
   "id_product" : "A",
   "id_product_unique": 1,
   "location": {
      "lat": 1.378446,
      "lon": 103.763427
   },
   "suggest": ["coke","zero"],
   "active": true
}

POST places/test
{
   "id_product" : "A",
   "id_product_unique": 2,
   "location": {
      "lat": 1.878446,
      "lon": 108.763427
   },
   "suggest": ["coke","zero"],
   "active": true
}

POST places/test
{
   "id_product" : "B",
   "id_product_unique": 3,
   "location": {
      "lat": 1.478446,
      "lon": 104.763427
   },
   "suggest": ["coke"],
   "active": true
}

POST places/test
{
   "id_product" : "C",
   "id_product_unique": 4,
   "location": {
      "lat": 1.218446,
      "lon": 102.763427
   },
   "suggest": ["coke","light"],
   "active": true
}
Run Code Online (Sandbox Code Playgroud)

在我的例子中,有2罐可乐零("id_product_unique" = 12),1罐可乐("id_product_unique" = 3)和一罐可乐灯("id_product_unique" = 4)

所有这些罐都在不同的位置.

" id_product"不是唯一的,因为完全相同的"焦炭罐"可以在两个不同的位置(ex "id_product_unique" = 12)出售.

只有" id_product_unique"和"位置"从"可乐罐"变为另一个(2个相同的"焦炭罐"具有相同的字段"建议"和" id_product"但不是相同的" id_product_unique"和" location").

我的目标是从给定的GPS位置搜索产品,并通过id_product(最近的一个)显示唯一的结果:

POST /places/_search?size=0
{
  "aggs" : {
    "group-by-type" : {
      "terms" : { "field" : "id_product"},
      "aggs": {
        "min-distance": {
          "top_hits": {
            "sort": {
              "_script": { 
                "type": "number",
                "script": {
                  "source": "def x = doc['location'].lat; def y = doc['location'].lon; return Math.abs(x-1.178446) + Math.abs(y-101.763427)",
                  "lang": "painless"
                },
                "order": "asc"
              }
            },
            "size" : 1
          }
        }
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

从这个结果列表中我现在想应用一个应该查询并按计算得分重新排序我的结果列表.我尝试了以下方法:

POST /places/_search?size=0
{
  "query" : {
    "bool": {
      "filter": {"term" : { "active" : "true" }},
      "should": [
        {"match" : { "suggest" : "coke" }},
        {"match" : { "suggest" : "light" }}
      ]
    }
  },
  "aggs" : {
    "group-by-type" : {
      "terms" : { "field" : "id_product"},
      "aggs": {
        "min-distance": {
          "top_hits": {
            "sort": {
              "_script": { 
                "type": "number",
                "script": {
                  "source": "def x = doc['location'].lat; def y = doc['location'].lon; return Math.abs(x-1.178446) + Math.abs(y-101.763427)",
                  "lang": "painless"
                },
                "order": "asc"
              }
            },
            "size" : 1
          }
        }
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

但我无法想象如何用doc分数替换距离排序分数.

任何帮助都会很棒.

wos*_*tom 0

我设法通过添加新的聚合“max_score”来做到这一点:

"max_score": {
  "max": {
    "script": {
      "lang": "painless",
      "source": "_score"
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

并按 max_score.value desc 排序:

"order": {"max_score.value": "desc"}
Run Code Online (Sandbox Code Playgroud)

我的最终查询如下:

POST /places/_search?size=0
{
  "query" : {
    "bool": {
      "filter": {"term" : { "active" : "true" }},
      "should": [
        {"match" : { "suggest" : "coke" }},
        {"match" : { "suggest" : "light" }}
      ]
    }
  },
  "aggs" : {
    "group-by-type" : {
      "terms" : {
        "field" : "id_product",
          "order": {"max_score.value": "desc"}
      },
      "aggs": {
        "min-distance": {
          "top_hits": {
            "sort": {
              "_script": { 
                "type": "number",
                "script": {
                  "source": "def x = doc['location'].lat; def y = doc['location'].lon; return Math.abs(x-1.178446) + Math.abs(y-101.763427)",
                  "lang": "painless"
                },
                "order": "asc"
              }
            },
            "size" : 1
          }
        },
        "max_score": {
          "max": {
            "script": {
              "lang": "painless",
              "inline": "_score"
            }
          }
        }
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

回答:

{
  "took": 3,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 4,
    "max_score": 0,
    "hits": []
  },
  "aggregations": {
    "group-by-type": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 0,
      "buckets": [
        {
          "key": "C",
          "doc_count": 1,
          "max_score": {
            "value": 1.0300811529159546
          },
          "min-distance": {
            "hits": {
              "total": 1,
              "max_score": null,
              "hits": [
                {
                  "_index": "places",
                  "_type": "test",
                  "_id": "VhJdOmIBKhzTB9xcDvfk",
                  "_score": null,
                  "_source": {
                    "id_product": "C",
                    "id_product_unique": 4,
                    "location": {
                      "lat": 1.218446,
                      "lon": 102.763427
                    },
                    "suggest": [
                      "coke",
                      "light"
                    ],
                    "active": true
                  },
                  "sort": [
                    1.0399999646503995
                  ]
                }
              ]
            }
          }
        },
        {
          "key": "A",
          "doc_count": 2,
          "max_score": {
            "value": 0.28768208622932434
          },
          "min-distance": {
            "hits": {
              "total": 2,
              "max_score": null,
              "hits": [
                {
                  "_index": "places",
                  "_type": "test",
                  "_id": "UhJcOmIBKhzTB9xc6ve-",
                  "_score": null,
                  "_source": {
                    "id_product": "A",
                    "id_product_unique": 1,
                    "location": {
                      "lat": 1.378446,
                      "lon": 103.763427
                    },
                    "suggest": [
                      "coke",
                      "zero"
                    ],
                    "active": true
                  },
                  "sort": [
                    2.1999999592114756
                  ]
                }
              ]
            }
          }
        },
        {
          "key": "B",
          "doc_count": 1,
          "max_score": {
            "value": 0.1596570909023285
          },
          "min-distance": {
            "hits": {
              "total": 1,
              "max_score": null,
              "hits": [
                {
                  "_index": "places",
                  "_type": "test",
                  "_id": "VRJcOmIBKhzTB9xc_vc0",
                  "_score": null,
                  "_source": {
                    "id_product": "B",
                    "id_product_unique": 3,
                    "location": {
                      "lat": 1.478446,
                      "lon": 104.763427
                    },
                    "suggest": [
                      "coke"
                    ],
                    "active": true
                  },
                  "sort": [
                    3.2999999020282695
                  ]
                }
              ]
            }
          }
        }
      ]
    }
  }
}
Run Code Online (Sandbox Code Playgroud)