【问题标题】:Remove a field from a Elasticsearch document从 Elasticsearch 文档中删除字段
【发布时间】:2022-01-12 07:10:27
【问题描述】:

我需要删除索引到 Elasticsearch 的所有文档中的一个字段。我该怎么做?

【问题讨论】:

  • 默认情况下这是不可能的,因为目前 Lucene 不支持。基本上,您只能从 Lucene 索引中放置或删除整个 Lucene 文档。 1 获取您的文档的第一个版本 2 删除该字段 3 推送您的文档的这个新版本。
  • 感谢分享stackoverflow.com/a/53771354/8392866Thiagofalcao。这太棒了!
  • 完成此操作的唯一方法是使用已删除的字段值重新映射索引。查看stackoverflow.com/a/38874129/13543225

标签: elasticsearch


【解决方案1】:

@backtrack 说的是真的,但是在 Elasticsearch 中有一种非常方便的方法可以做到这一点。 Elasticsearch 会抽象出删除的内部复杂性。 您需要使用更新 API 来实现这一点 -

curl -XPOST 'localhost:9200/test/type1/1/_update' -d '{
    "script" : "ctx._source.remove(\"name_of_field\")"
}'

您可以找到更多文档here

注意:从 Elastic Search 6 开始,您需要包含内容类型标头:

-H 'Content-Type: application/json'

【讨论】:

  • 如果你有十亿个包含这个字段的文档,这个性能如何?
  • 实际文档将被删除,每次更改都会添加一个新文档。
  • ElasticSearch 5.0 的注意事项:您应该使用命名参数而不是硬编码的名称。参数更快,它们不会破坏脚本的编译限制。见documentation
  • @VineethMohan 它也会删除映射吗?所以如果我查询 test/type1/_mapping,它会显示 'name_of_field' 吗?所以基本上,我想删除并添加具有差异类型的相同文件名。这有可能吗?
  • 另外,如果您使用连字符引用嵌套字段,您可以使用ctx._source[\"my-field\"].remove(\"subfield\")
【解决方案2】:

Elasticsearch 在 2.3 中添加了 update_by_query。这个实验性界面允许您对匹配查询的所有文档进行更新。

在内部 elasticsearch 进行扫描/滚动以收集批量文档,然后像批量更新界面一样更新它们。由于没有网络和序列化的开销,这比使用您自己的扫描/滚动界面手动执行要快。每条记录都必须加载到内存中,修改然后写入。

昨天我从我的 ES 集群中删除了一个大字段。在 update_by_query 期间,我看到每秒 10,000 条记录的持续吞吐量受到 CPU 而不是 IO 的限制。

如果集群有其他更新流量,请查看设置conflicts=proceed,或者当其中一个记录在其中一个批次下更新时,整个作业将在达到ConflictError 时停止。

类似地设置wait_for_completion=false 将导致update_by_query 通过tasks 接口运行。否则,如果连接关闭,作业将终止。

网址:

http://localhost:9200/INDEX/TYPE/_update_by_query?wait_for_completion=false&conflicts=proceed

POST 正文:

{
  "script": "ctx._source.remove('name_of_field')",
  "query": {
    "bool": {
      "must": [
        {
          "exists": {
            "field": "name_of_field"
          }
        }
      ]
    }
  }
}

从 Elasticsearch 1.43 开始,内联 groovy scripting is disabled by default。您需要通过将script.inline: true 添加到您的配置文件中来启用它,这样的内联脚本才能工作。

或者将 groovy 作为脚本上传并使用"script": { "file": "scriptname", "lang": "groovy"} 格式。

【讨论】:

  • 我还不知道,如何回收该字段使用的 field_data 空间。希望滚动重启会导致序号被重新加载。
  • 主体需要稍作修改,否则效果很好。我不得不将脚本包装在 JSON 对象中,可能是因为 API 发生了一些变化。
  • 如果我的字段是数组的一部分(例如 [{"name":"test"},{"name":"test1"}] )并且我不知道该字段的数组那么我如何使用上面的查询删除该字段。请帮我解决这个问题?
  • @sayed-abolfazl-fatemi 感谢您对代码的清理编辑。
  • 这太棒了。谢谢!
【解决方案3】:

您可以使用_update_by_query

示例 1

索引:my_index

字段:user.email

POST my_index/_update_by_query?conflicts=proceed
{
    "script" : "ctx._source.user.remove('email')",
    "query" : {
        "exists": { "field": "user.email" }
    }
}

示例 2

索引:my_index

字段:total_items

POST my_index/_update_by_query?conflicts=proceed
{
    "script" : "ctx._source.remove('total_items')",
    "query" : {
        "exists": { "field": "total_items" }
    }
}

【讨论】:

  • 嗨,我尝试了与您在此处提到的完全相同的方法,但它似乎对我不起作用。我是这个领域的新手。我的索引中有一个不需要的字段,说索引名称“test_xyz”。这包含近 155154 个文档。我想从我的索引中删除不需要的字段。这就是我的索引模式在 json 格式 {A : {B : {C: } } } 中的样子。我基本上想删除 B - 意味着 C 将自动从我的索引中删除。为了做到这一点,我使用了你的第一个想法。将 A 作为用户,B 作为电子邮件。你能帮我解决这个问题吗?
  • 嗨@MaunilVyas,它应该可以工作:POST my_index/_update_by_query?conflicts=proceed { "script" : "ctx._source.A.remove('B')", "query" : { " exists": { "field": "A.B" } } } 如果不起作用,则需要使用正确映射的目标索引重新索引数据。
  • 感谢您的回复。是的,它现在正在工作。我犯了一些小错误!
  • 你好,这个操作也是从映射中删除字段吗?不是吧?
  • 这个答案帮助我理解了 nested 字段的删除。谢谢!
【解决方案4】:

以前的答案对我不起作用。

我必须添加关键字“内联”:

POST /my_index/_update_by_query
{
  "script": {
    "inline": "ctx._source.remove(\"myfield\")"
  },
  "query" : {
      "exists": { "field": "myfield" }
  }
}

【讨论】:

    【解决方案5】:

    默认情况下这是不可能的,因为现在 Lucene 不支持它。基本上,您只能从 Lucene 索引中放置或删除整个 Lucene 文档。

    1. 获取您的文档的第一个版本
    2. 删除字段
    3. 推送这个新版本的文档

    此答案对版本

    【讨论】:

    • @ThomasDecaux,非常感谢。我在 2015 年回答过,我知道 ES 现在有能力。再次感谢您的指点。
    • @ThomasDecaux - 确实如此。感谢您的宝贵意见。我刚刚添加了版本。
    【解决方案6】:

    对于那些坚持使用 bulk API 的人,实现删除文档字段的替代方法是在批量 API 调用的 update action payload 中提供额外的脚本。

    命令部分与官方文档中描述的相同:

    curl -s -H "Content-Type: application/x-ndjson"  -H "Accept: application/json; indent=4;" \
         --data-binary   '@es_bulk_edit_data.json'  --request POST \
         "http://YOUR_ELASTICSEARCH_HOST:PORT_NUM/OPTIONAL_INDEX/OPTIONAL_TYPE/_bulk?pretty"
    

    在请求正文文件中,可能需要为同一个文档使用2个payload,其中一个用于创建、更新字段,另一个用于通过脚本删除字段,可能是这样的:

    // assume you attempt to add one field `artist`, update one field `num_views`,
    // and delete one field `useless` in the document with type t1 and ID 123
    {"update": {"_type": "t1", "_id": "123"}}
    {"doc": {"artist": "new_artist", "num_views": 67}}
    {"update": {"_type": "t1", "_id": "123"}}
    {"script": {"source": "ctx._source.remove(params.del_field_name)", "lang":"painless", "params":{"del_field_name": "useless"}}}
    

    注意:

    • 在批量 API 中,doc 部分不能与 script 部分放在同一个有效负载中,ElasticSearch 似乎拒绝处理这种有效负载结构并返回错误响应400 bad request,原因消息将为Validation Failed: 1: can't provide both script and doc;。这就是为什么我将删除和所有其他操作在 2 个有效负载中分开。
    • 这些在 5.6 和 6.6 版本上进行了测试,在最新版本 (v7.10) 中也应该得到相同的结果

    【讨论】:

      【解决方案7】:
      PUT /products/_update/1
      {
        "docs" :{
          "price": 12,
          "quantity": 3,
          "in_stock": 6
        }
      }
      
      Now if I need to remove "quantity" then:
      
      POST products/_update/1
      {
        "script": {
          "source": "ctx._source.remove(\"quantity\")"
        }
      }
      

      【讨论】:

        【解决方案8】:

        我想在前面的答案中补充一点,删除字段后,索引的大小不会改变。将不得不创建一个新索引或使用 _reindex api。

        curl -X POST "localhost:9200/_reindex?pretty" -H 'Content-Type: application/json' -d'
        {
         "source": {
           "index": "old-index"
         },
         "dest": {
           "index": "new-index"
        }}
        
        '
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-05-03
          • 2015-08-31
          • 1970-01-01
          • 2019-11-01
          • 2014-02-12
          • 1970-01-01
          • 2020-11-21
          • 1970-01-01
          相关资源
          最近更新 更多