【问题标题】:Elasticsearch: Sort on different fields depending on typeElasticsearch:根据类型对不同的字段进行排序
【发布时间】:2016-04-24 05:12:20
【问题描述】:

我的索引中有两种类型(EventCity),我正在尝试按日期对它们进行排序。但是,每种类型的日期字段名称不同: 对于Event,值在updated_at 字段中,对于City,日期在其city_events 嵌套对象数组的嵌套对象之一的update_at 字段中(注意region_id 的过滤)。

我试过像这样指定排序数组中的每个字段:

  "sort": [
    {
      "city_events.updated_at": {
        "order": "desc",
        "nested_path": "city_events",
        "nested_filter": {
          "term": {
            "city_events.region_id": 1
          }
        }
      }
    },
    {
      "updated_at": "desc"
    }
  ]

但不幸的是,这并没有将这两种类型混合在一起。相反,它首先按嵌套的city_events.updated_at 字段对所有Cities 进行排序,然后将所有Events 附加到按其updated_at 字段排序的底部。如何将两者混合和排序?

作为替代解决方案,我尝试仅按嵌套的 city_events.updated_at 字段进行排序并指定 "missing": "updated_at",但是尽管两个字段的格式相同,但仍引发了 "number_format_exception" 错误:

{
  "error": {
    "root_cause": [
      {
        "type": "number_format_exception",
        "reason": "For input string: \"updated_at\""
      }
    ],
    "type": "search_phase_execution_exception",
    "reason": "all shards failed",
    "phase": "query_fetch",
    "grouped": true,
    "failed_shards": [
      {
        "shard": 0,
        "index": "events_1461095196252",
        "node": "sYQstSw_SN62ojmXgGjPlg",
        "reason": {
          "type": "number_format_exception",
          "reason": "For input string: \"updated_at\""
        }
      }
    ]
  },
  "status": 400
}

更新 1:基于下面的the answer by Andrei Stefan,我尝试开发一个 groovy 脚本,该脚本循环遍历每个City 文档的 city_events,选择具有匹配 region_id 的文档,然后返回 city_eventupdated_at 值进行评分,但在访问脚本中的嵌套字段时遇到问题:https://stackoverflow.com/questions/36781476/elasticsearch-access-fields-inside-array-of-nested-objects-in-a-groovy-script

【问题讨论】:

    标签: elasticsearch


    【解决方案1】:

    尝试基于script 的排序,您需要nested 字段才能在脚本中访问include_in_parent: true

        "city_events": {
          "type": "nested",
          "include_in_parent": true, 
          "properties": {
            "updated_at": {
              "type": "date"
            }
          }
        }
    

    还有排序部分:

      "sort": {
        "_script": {
          "type": "number",
          "script": {
            "inline": "if (doc['_type'].value=='Event') return doc['updated_at'].date.getMillis(); else if (doc['_type'].value=='City') return doc['city_events.updated_at'].date.getMillis()",
            "lang": "groovy"
          },
          "order": "desc"
        }
      }
    

    稍后编辑

    即使我在 Groovy 脚本中添加 city_events.region_id==1 条件,也不会感觉到 Elasticsearch,这将是纯 Groovy 编程,而不是 Elasticsearch 的强大功能。

    我尝试过其他方法(都在 ES 2.3.1 中):

    • copy_to 从常规的updated_at 字段到Event 内的nested 字段,以便对所有类型执行常规的nested 排序。这不起作用。
    • 即使copy_to 可以工作,Elasticsearch 也不会从Event 类型中的sort 部分中匹配"term": {"city_events.region_id": 1}(因为region_id 不存在于Event),对于那些values 会使用 -9223372036854776000 而不是实际日期(该值来自我执行的测试)。
    • Event 中也使用nested 字段,并在索引时将updated_at 放在这个嵌套字段中。由于与上述尝试#2 相同的原因,这将不起作用:Event 中也必须有一个region_id,以便sort 部分中的nested 过滤器将适用于 both 类型。

    作为一种正确的处理方式,我的建议是重新考虑一下数据结构,以便排序部分(至少)将遵循 Elasticsearch 的做法事物。您的类型称为CityEvent,在City 内部,您有一个(嵌套的)city_events 列表。你不能在City 中包含Event 并在每个城市复制事件的详细信息吗?这不一定是规范化的 RDB 数据结构。相反,ES 对非标准化数据更满意。


    为了完整起见,但我不建议这样做

      "sort": {
        "_script": {
          "type": "number",
          "script": {
            "inline": "if (doc['_type'].value=='Event') return doc['updated_at'].date.getMillis(); else if (doc['_type'].value=='City') {for(nestedObj in _source.city_events) {if(nestedObj.region_id==1) return nestedObj.updated_at.toLong();}}",
            "lang": "groovy"
          },
          "order": "desc"
        }
      }
    

    请注意,我没有在上面的 Groovy 脚本中完成所有正确的检查(例如检查文档中是否确实存在嵌套对象)。

    【讨论】:

    • 谢谢!您能否还展示如何通过此脚本过滤"city_events.region_id": "$some_id",因为它是city_events 中的一组记录,我只想使用带有$some_id 的记录进行排序。
    • 谢谢!重新考虑数据结构并在两种类型上包含一个公共字段(即使它复制了事件的数据)是一个明显的解决方案,我不敢相信我没有想到。我最终还在Events 上添加了一个嵌套的city_events 对象,然后按我最初包含在问题中的nested_filter 对事件和城市进行了排序。你和@Sebastian 都提出了这个建议,但 Sebastian 是第一个,所以我会将赏金奖励给他。但由于您的回答比较详细,我将其标记为已接受。
    • 是的,同意你的看法。
    • 我有同样的问题,但在我的情况下,我的嵌套属性之一是字符串,另一个是数字如何处理它?
    【解决方案2】:

    Elasticsearch 数据应针对读取进行优化。最好的解决方案是向存储相关排序值的两种类型添加公共字段。

    关于城市内的多个嵌套对象:我仍然会在城市级别存储最相关(最近)的值。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-10
      相关资源
      最近更新 更多