索引排序编辑

在 Elasticsearch 中创建新索引时,可以配置每个分片内的段如何排序。默认情况下,Lucene 不会应用任何排序。 index.sort.* 设置定义了应使用哪些字段来对每个段内的文档进行排序。

嵌套字段与索引排序不兼容,因为它们依赖于嵌套文档存储在连续的文档 ID 中的假设,而索引排序可能会破坏这种假设。如果在包含嵌套字段的索引上激活索引排序,则会抛出错误。

例如,以下示例显示了如何在单个字段上定义排序

resp = client.indices.create(
    index="my-index-000001",
    body={
        "settings": {
            "index": {"sort.field": "date", "sort.order": "desc"}
        },
        "mappings": {"properties": {"date": {"type": "date"}}},
    },
)
print(resp)
response = client.indices.create(
  index: 'my-index-000001',
  body: {
    settings: {
      index: {
        'sort.field' => 'date',
        'sort.order' => 'desc'
      }
    },
    mappings: {
      properties: {
        date: {
          type: 'date'
        }
      }
    }
  }
)
puts response
PUT my-index-000001
{
  "settings": {
    "index": {
      "sort.field": "date", 
      "sort.order": "desc"  
    }
  },
  "mappings": {
    "properties": {
      "date": {
        "type": "date"
      }
    }
  }
}

此索引按 date 字段排序

…​ 按降序排列。

也可以按多个字段对索引进行排序

resp = client.indices.create(
    index="my-index-000001",
    body={
        "settings": {
            "index": {
                "sort.field": ["username", "date"],
                "sort.order": ["asc", "desc"],
            }
        },
        "mappings": {
            "properties": {
                "username": {"type": "keyword", "doc_values": True},
                "date": {"type": "date"},
            }
        },
    },
)
print(resp)
response = client.indices.create(
  index: 'my-index-000001',
  body: {
    settings: {
      index: {
        'sort.field' => [
          'username',
          'date'
        ],
        'sort.order' => [
          'asc',
          'desc'
        ]
      }
    },
    mappings: {
      properties: {
        username: {
          type: 'keyword',
          doc_values: true
        },
        date: {
          type: 'date'
        }
      }
    }
  }
)
puts response
PUT my-index-000001
{
  "settings": {
    "index": {
      "sort.field": [ "username", "date" ], 
      "sort.order": [ "asc", "desc" ]       
    }
  },
  "mappings": {
    "properties": {
      "username": {
        "type": "keyword",
        "doc_values": true
      },
      "date": {
        "type": "date"
      }
    }
  }
}

此索引首先按 username 排序,然后按 date 排序

…​ username 字段按升序排列,date 字段按降序排列。

索引排序支持以下设置

index.sort.field
用于对索引进行排序的字段列表。这里只允许 booleannumericdate 和具有 doc_valueskeyword 字段。
index.sort.order

每个字段使用的排序顺序。order 选项可以具有以下值

  • asc:用于升序排列
  • desc:用于降序排列。
index.sort.mode

Elasticsearch 支持按多值字段排序。mode 选项控制选择哪个值来对文档进行排序。mode 选项可以具有以下值

  • min:选择最低值。
  • max:选择最高值。
index.sort.missing

missing 参数指定如何处理缺少该字段的文档。missing 值可以具有以下值

  • _last:没有该字段值的文档排在最后。
  • _first:没有该字段值的文档排在最前面。

索引排序只能在索引创建时定义一次。不允许在现有索引上添加或更新排序。索引排序在索引吞吐量方面也会产生成本,因为文档必须在刷新和合并时进行排序。在激活此功能之前,您应该测试对应用程序的影响。

搜索请求的提前终止编辑

默认情况下,在 Elasticsearch 中,搜索请求必须访问与查询匹配的每个文档以检索按指定排序排序的顶级文档。但是,当索引排序和搜索排序相同时,可以限制每个段应访问的文档数量以全局检索排名最高的 N 个文档。例如,假设我们有一个按时间戳字段排序的索引

resp = client.indices.create(
    index="events",
    body={
        "settings": {
            "index": {"sort.field": "timestamp", "sort.order": "desc"}
        },
        "mappings": {"properties": {"timestamp": {"type": "date"}}},
    },
)
print(resp)
response = client.indices.create(
  index: 'events',
  body: {
    settings: {
      index: {
        'sort.field' => 'timestamp',
        'sort.order' => 'desc'
      }
    },
    mappings: {
      properties: {
        timestamp: {
          type: 'date'
        }
      }
    }
  }
)
puts response
PUT events
{
  "settings": {
    "index": {
      "sort.field": "timestamp",
      "sort.order": "desc" 
    }
  },
  "mappings": {
    "properties": {
      "timestamp": {
        "type": "date"
      }
    }
  }
}

此索引按时间戳降序排序(最近的排在最前面)

您可以搜索最后 10 个事件

resp = client.search(
    index="events",
    body={"size": 10, "sort": [{"timestamp": "desc"}]},
)
print(resp)
response = client.search(
  index: 'events',
  body: {
    size: 10,
    sort: [
      {
        timestamp: 'desc'
      }
    ]
  }
)
puts response
GET /events/_search
{
  "size": 10,
  "sort": [
    { "timestamp": "desc" }
  ]
}

Elasticsearch 将检测到每个段的顶级文档已在索引中排序,并且只会比较每个段的前 N 个文档。收集与查询匹配的其余文档以计算结果总数并构建聚合。

如果您只查找最后 10 个事件,并且对与查询匹配的文档总数不感兴趣,则可以将 track_total_hits 设置为 false

resp = client.search(
    index="events",
    body={
        "size": 10,
        "sort": [{"timestamp": "desc"}],
        "track_total_hits": False,
    },
)
print(resp)
response = client.search(
  index: 'events',
  body: {
    size: 10,
    sort: [
      {
        timestamp: 'desc'
      }
    ],
    track_total_hits: false
  }
)
puts response
GET /events/_search
{
  "size": 10,
  "sort": [ 
      { "timestamp": "desc" }
  ],
  "track_total_hits": false
}

索引排序将用于对顶级文档进行排名,并且每个段将在前 10 个匹配项后提前终止收集。

这次,Elasticsearch 不会尝试计算文档数量,并且一旦每个段收集了 N 个文档,就可以立即终止查询。

{
  "_shards": ...
   "hits" : {  
      "max_score" : null,
      "hits" : []
  },
  "took": 20,
  "timed_out": false
}

由于提前终止,与查询匹配的命中总数未知。

聚合将收集与查询匹配的所有文档,无论 track_total_hits 的值如何