You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Elasticsearch 8.10按_id降序排序问题求助

问题描述

现有Elasticsearch 8.10版本的索引my_index,文档_id格式为a:1、a:2、a:3、a:100,期望按_id降序排序得到结果:a:100、a:3、a:2、a:1。

执行以下查询语句时触发错误:

GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {"_doc": "desc"}
  ]
}

错误提示:

#! Loading the fielddata on the _id field is deprecated and will be removed in future versions. If you require sorting or aggregating on this field you should also include the id in the body of your documents, and map this field as a keyword field that has [doc_values] enabled

原索引创建语句:

PUT /my_index
{
  "mappings": {
    "properties": {
      "name": {"type": "text"},
      "age": {"type": "integer"},
      "type": {"type": "keyword"}
    }
  }
}

尝试修改索引映射(但无效,仍报错):

PUT /my_index
{
  "mappings": {
    "properties": {
      "_id": {"type": "keyword", "copy_to":"id"},
      "id": {"type": "keyword", "store":true},
      "name": {"type": "text"},
      "age": {"type": "integer"},
      "type": {"type": "keyword"}
    }
  }
}
解决方案

Elasticsearch 8.x禁止直接对_id字段排序(会触发fielddata加载的废弃警告),且_id作为字符串直接排序会得到字典序结果(a:1、a:100、a:2、a:3),不符合需求。需要针对_id中冒号后的数字做数值排序,提供两种可行方案:

方案1:重新索引,拆分_id为结构化字段

这种方案性能最优,适合数据量较大的场景:

  1. 创建新索引,添加专门用于排序的拆分字段:
PUT /my_index_new
{
  "mappings": {
    "properties": {
      "name": {"type": "text"},
      "age": {"type": "integer"},
      "type": {"type": "keyword"},
      "id_prefix": {"type": "keyword"},  // 存储_id中冒号前的字符串部分
      "id_num": {"type": "integer"}      // 存储_id中冒号后的数字部分
    }
  }
}
  1. 使用_reindex API迁移数据,同时通过脚本拆分_id:
POST _reindex
{
  "source": {
    "index": "my_index"
  },
  "dest": {
    "index": "my_index_new"
  },
  "script": {
    "source": """
      def parts = ctx._id.splitOnToken(':');
      ctx._source.id_prefix = parts[0];
      ctx._source.id_num = Integer.parseInt(parts[1]);
    """
  }
}
  1. 查询时按拆分字段排序:
GET /my_index_new/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {"id_prefix": "desc"},  // 可选,保证同前缀的文档分组
    {"id_num": "desc"}
  ]
}

方案2:使用运行时字段(无需重新索引)

适合数据量较小、不想重新索引的场景,通过动态计算字段实现排序:

GET /my_index/_search
{
  "runtime_mappings": {
    "id_num": {
      "type": "integer",
      "script": """
        def parts = params._id.splitOnToken(':');
        emit(Integer.parseInt(parts[1]));
      """
    }
  },
  "query": {
    "match_all": {}
  },
  "sort": [
    {"id_num": "desc"}
  ]
}

如果需要先按前缀排序再按数字排序,可以扩展运行时字段:

GET /my_index/_search
{
  "runtime_mappings": {
    "id_prefix": {
      "type": "keyword",
      "script": """
        def parts = params._id.splitOnToken(':');
        emit(parts[0]);
      """
    },
    "id_num": {
      "type": "integer",
      "script": """
        def parts = params._id.splitOnToken(':');
        emit(Integer.parseInt(parts[1]));
      """
    }
  },
  "query": {
    "match_all": {}
  },
  "sort": [
    {"id_prefix": "desc"},
    {"id_num": "desc"}
  ]
}

关键说明

之前尝试在映射中定义_id字段的方式无效,因为_id是Elasticsearch的元字段,不能作为普通字段添加到properties中,因此copy_to配置不会生效。

内容的提问来源于stack exchange,提问作者no13bus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 10:57:42