首页 > 解决方案 > 使用 Python 批量更新 elasticsearch 文档

问题描述

我有如下弹性搜索文档,我需要根据creationtime currentdate纠正年龄值

年龄 = 创建时间 - 当前日期

hits = [
   {
      "_id":"CrRvuvcC_uqfwo-WSwLi",
      "creationtime":"2018-05-20T20:57:02",
      "currentdate":"2021-02-05 00:00:00",
      "age":"60 months"
   },
   {
      "_id":"CrRvuvcC_uqfwo-WSwLi",
      "creationtime":"2013-07-20T20:57:02",
      "currentdate":"2021-02-05 00:00:00",
      "age":"60 months"
   },
   {
      "_id":"CrRvuvcC_uqfwo-WSwLi",
      "creationtime":"2014-08-20T20:57:02",
      "currentdate":"2021-02-05 00:00:00",
      "age":"60 months"
   },
   {
      "_id":"CrRvuvcC_uqfwo-WSwLi",
      "creationtime":"2015-09-20T20:57:02",
      "currentdate":"2021-02-05 00:00:00",
      "age":"60 months"
   }
]

我想根据每个文档 ID 进行批量更新,但问题是我需要更正 6 个月的数据和每个数据大小(索引的文档计数)几乎是535329,我想有效地根据_id对年龄进行批量更新每天使用 python 处理所有文档。

有没有办法做到这一点,而无需循环,我遇到的所有使用 Pandas 数据帧进行更新的示例都是基于已知值。但是在这里_id我会在代码运行时得到。

我编写的逻辑是获取所有文档并存储它们的_id,然后为每个_id更新年龄。但如果我想在 6 个月的每一天批量更新所有文档,这不是一种有效的方法。

谁能给我一些想法或指出正确的方向。

标签: pythonelasticsearch

解决方案


如评论中所述,无需获取 ID。您甚至不需要自己获取文件!

一个_update_by_query电话就足够了。解析日期后,您可以使用它ChronoUnit来获取差异:

POST your-index-name/_update_by_query
{
  "query": {
    "match_all": {}
  },
  "script": {
    "source": """
      def created =  LocalDateTime.parse(ctx._source.creationtime, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"));

      def currentdate = LocalDateTime.parse(ctx._source.currentdate, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    
      def months = ChronoUnit.MONTHS.between(created, currentdate);
      ctx._source._age = months + ' month' + (months > 1 ? 's' : '');
    """,
    "lang": "painless"
  }
}

官方python客户端也有这个方法。这是一个工作示例

尝试在文档的一小部分上运行此更新脚本,然后通过添加除match_all我放在那里的查询之外的查询来释放整个索引。


值得一提的是,除非你在这个age字段上进行搜索,否则它不需要存储在你的索引中,因为它可以在查询时计算出来。

你看,如果你的索引映射的日期是这样正确定义的:

{
  "mappings": {
    "properties": {
      "creationtime": {
        "type": "date",
        "format": "yyyy-MM-dd'T'HH:mm:ss"
      },
      "currentdate": {
        "type": "date",
        "format": "yyyy-MM-dd HH:mm:ss"
      },
      ...
    }
  }
}

age可以计算为脚本字段

POST ttimes/_search
{
  "query": {
    "match_all": {}
  },
  "script_fields": {
    "age_calculated": {
      "script": {
        "source": """
          def months = ChronoUnit.MONTHS.between(
                          doc['creationtime'].value,
                          doc['currentdate'].value );
          return months + ' month' + (months > 1 ? 's' : '');
        """
      }
    }
  }
}

唯一需要注意的是,该值不会在内部,_source而是在其自己的调用组内部fields(这意味着一次可能有更多脚本字段!)。

"hits" : [
  {
    ...
    "_id" : "FFfPuncBly0XYOUcdIs5",
    "fields" : {
      "age_calculated" : [ "32 months" ]   <--
    }
  },
  ...

推荐阅读