对搜索结果进行排序

编辑

允许您在特定字段上添加一个或多个排序。每个排序也可以反转。排序是在每个字段级别定义的,使用特殊的字段名 _score 按分数排序,使用 _doc 按索引顺序排序。

假设有以下索引映射

resp = client.indices.create(
    index="my-index-000001",
    mappings={
        "properties": {
            "post_date": {
                "type": "date"
            },
            "user": {
                "type": "keyword"
            },
            "name": {
                "type": "keyword"
            },
            "age": {
                "type": "integer"
            }
        }
    },
)
print(resp)
response = client.indices.create(
  index: 'my-index-000001',
  body: {
    mappings: {
      properties: {
        post_date: {
          type: 'date'
        },
        user: {
          type: 'keyword'
        },
        name: {
          type: 'keyword'
        },
        age: {
          type: 'integer'
        }
      }
    }
  }
)
puts response
res, err := es.Indices.Create(
	"my-index-000001",
	es.Indices.Create.WithBody(strings.NewReader(`{
	  "mappings": {
	    "properties": {
	      "post_date": {
	        "type": "date"
	      },
	      "user": {
	        "type": "keyword"
	      },
	      "name": {
	        "type": "keyword"
	      },
	      "age": {
	        "type": "integer"
	      }
	    }
	  }
	}`)),
)
fmt.Println(res, err)
const response = await client.indices.create({
  index: "my-index-000001",
  mappings: {
    properties: {
      post_date: {
        type: "date",
      },
      user: {
        type: "keyword",
      },
      name: {
        type: "keyword",
      },
      age: {
        type: "integer",
      },
    },
  },
});
console.log(response);
PUT /my-index-000001
{
  "mappings": {
    "properties": {
      "post_date": { "type": "date" },
      "user": {
        "type": "keyword"
      },
      "name": {
        "type": "keyword"
      },
      "age": { "type": "integer" }
    }
  }
}
resp = client.search(
    index="my-index-000001",
    sort=[
        {
            "post_date": {
                "order": "asc",
                "format": "strict_date_optional_time_nanos"
            }
        },
        "user",
        {
            "name": "desc"
        },
        {
            "age": "desc"
        },
        "_score"
    ],
    query={
        "term": {
            "user": "kimchy"
        }
    },
)
print(resp)
response = client.search(
  index: 'my-index-000001',
  body: {
    sort: [
      {
        post_date: {
          order: 'asc',
          format: 'strict_date_optional_time_nanos'
        }
      },
      'user',
      {
        name: 'desc'
      },
      {
        age: 'desc'
      },
      '_score'
    ],
    query: {
      term: {
        user: 'kimchy'
      }
    }
  }
)
puts response
const response = await client.search({
  index: "my-index-000001",
  sort: [
    {
      post_date: {
        order: "asc",
        format: "strict_date_optional_time_nanos",
      },
    },
    "user",
    {
      name: "desc",
    },
    {
      age: "desc",
    },
    "_score",
  ],
  query: {
    term: {
      user: "kimchy",
    },
  },
});
console.log(response);
GET /my-index-000001/_search
{
  "sort" : [
    { "post_date" : {"order" : "asc", "format": "strict_date_optional_time_nanos"}},
    "user",
    { "name" : "desc" },
    { "age" : "desc" },
    "_score"
  ],
  "query" : {
    "term" : { "user" : "kimchy" }
  }
}

_doc 除了是最有效的排序顺序之外,没有实际的用例。因此,如果您不关心文档返回的顺序,则应按 _doc 排序。这在滚动时尤其有用。

排序值

编辑

搜索响应包括每个文档的 sort 值。使用 format 参数为 datedate_nanos 字段的 sort 值指定一个 日期格式。以下搜索以 strict_date_optional_time_nanos 格式返回 post_date 字段的 sort 值。

resp = client.search(
    index="my-index-000001",
    sort=[
        {
            "post_date": {
                "format": "strict_date_optional_time_nanos"
            }
        }
    ],
    query={
        "term": {
            "user": "kimchy"
        }
    },
)
print(resp)
response = client.search(
  index: 'my-index-000001',
  body: {
    sort: [
      {
        post_date: {
          format: 'strict_date_optional_time_nanos'
        }
      }
    ],
    query: {
      term: {
        user: 'kimchy'
      }
    }
  }
)
puts response
const response = await client.search({
  index: "my-index-000001",
  sort: [
    {
      post_date: {
        format: "strict_date_optional_time_nanos",
      },
    },
  ],
  query: {
    term: {
      user: "kimchy",
    },
  },
});
console.log(response);
GET /my-index-000001/_search
{
  "sort" : [
    { "post_date" : {"format": "strict_date_optional_time_nanos"}}
  ],
  "query" : {
    "term" : { "user" : "kimchy" }
  }
}

排序顺序

编辑

order 选项可以具有以下值:

asc

按升序排序

desc

按降序排序

当按 _score 排序时,顺序默认为 desc,当按其他任何内容排序时,默认为 asc

排序模式选项

编辑

Elasticsearch 支持按数组或多值字段进行排序。 mode 选项控制选择哪个数组值来对其所属的文档进行排序。 mode 选项可以具有以下值:

min

选择最小值。

max

选择最大值。

sum

使用所有值的总和作为排序值。仅适用于基于数字的数组字段。

avg

使用所有值的平均值作为排序值。仅适用于基于数字的数组字段。

median

使用所有值的中位数作为排序值。仅适用于基于数字的数组字段。

升序排序中的默认排序模式是 min — 选择最小值。降序排序中的默认排序模式是 max — 选择最大值。

排序模式示例用法
编辑

在以下示例中,字段 price 每个文档有多个价格。在这种情况下,结果命中将按每个文档的平均价格以升序排序。

resp = client.index(
    index="my-index-000001",
    id="1",
    refresh=True,
    document={
        "product": "chocolate",
        "price": [
            20,
            4
        ]
    },
)
print(resp)

resp1 = client.search(
    query={
        "term": {
            "product": "chocolate"
        }
    },
    sort=[
        {
            "price": {
                "order": "asc",
                "mode": "avg"
            }
        }
    ],
)
print(resp1)
response = client.index(
  index: 'my-index-000001',
  id: 1,
  refresh: true,
  body: {
    product: 'chocolate',
    price: [
      20,
      4
    ]
  }
)
puts response

response = client.search(
  body: {
    query: {
      term: {
        product: 'chocolate'
      }
    },
    sort: [
      {
        price: {
          order: 'asc',
          mode: 'avg'
        }
      }
    ]
  }
)
puts response
{
	res, err := es.Index(
		"my-index-000001",
		strings.NewReader(`{
	  "product": "chocolate",
	  "price": [
	    20,
	    4
	  ]
	}`),
		es.Index.WithDocumentID("1"),
		es.Index.WithRefresh("true"),
		es.Index.WithPretty(),
	)
	fmt.Println(res, err)
}

{
	res, err := es.Search(
		es.Search.WithBody(strings.NewReader(`{
	  "query": {
	    "term": {
	      "product": "chocolate"
	    }
	  },
	  "sort": [
	    {
	      "price": {
	        "order": "asc",
	        "mode": "avg"
	      }
	    }
	  ]
	}`)),
		es.Search.WithPretty(),
	)
	fmt.Println(res, err)
}
const response = await client.index({
  index: "my-index-000001",
  id: 1,
  refresh: "true",
  document: {
    product: "chocolate",
    price: [20, 4],
  },
});
console.log(response);

const response1 = await client.search({
  query: {
    term: {
      product: "chocolate",
    },
  },
  sort: [
    {
      price: {
        order: "asc",
        mode: "avg",
      },
    },
  ],
});
console.log(response1);
PUT /my-index-000001/_doc/1?refresh
{
   "product": "chocolate",
   "price": [20, 4]
}

POST /_search
{
   "query" : {
      "term" : { "product" : "chocolate" }
   },
   "sort" : [
      {"price" : {"order" : "asc", "mode" : "avg"}}
   ]
}

排序数值字段

编辑

对于数值字段,也可以使用 numeric_type 选项将值从一种类型转换为另一种类型。此选项接受以下值:["double", "long", "date", "date_nanos"],对于在映射不同的排序字段的多个数据流或索引之间进行搜索非常有用。

例如考虑以下两个索引

resp = client.indices.create(
    index="index_double",
    mappings={
        "properties": {
            "field": {
                "type": "double"
            }
        }
    },
)
print(resp)
response = client.indices.create(
  index: 'index_double',
  body: {
    mappings: {
      properties: {
        field: {
          type: 'double'
        }
      }
    }
  }
)
puts response
res, err := es.Indices.Create(
	"index_double",
	es.Indices.Create.WithBody(strings.NewReader(`{
	  "mappings": {
	    "properties": {
	      "field": {
	        "type": "double"
	      }
	    }
	  }
	}`)),
)
fmt.Println(res, err)
const response = await client.indices.create({
  index: "index_double",
  mappings: {
    properties: {
      field: {
        type: "double",
      },
    },
  },
});
console.log(response);
PUT /index_double
{
  "mappings": {
    "properties": {
      "field": { "type": "double" }
    }
  }
}
resp = client.indices.create(
    index="index_long",
    mappings={
        "properties": {
            "field": {
                "type": "long"
            }
        }
    },
)
print(resp)
response = client.indices.create(
  index: 'index_long',
  body: {
    mappings: {
      properties: {
        field: {
          type: 'long'
        }
      }
    }
  }
)
puts response
res, err := es.Indices.Create(
	"index_long",
	es.Indices.Create.WithBody(strings.NewReader(`{
	  "mappings": {
	    "properties": {
	      "field": {
	        "type": "long"
	      }
	    }
	  }
	}`)),
)
fmt.Println(res, err)
const response = await client.indices.create({
  index: "index_long",
  mappings: {
    properties: {
      field: {
        type: "long",
      },
    },
  },
});
console.log(response);
PUT /index_long
{
  "mappings": {
    "properties": {
      "field": { "type": "long" }
    }
  }
}

由于 field 在第一个索引中映射为 double,在第二个索引中映射为 long,因此默认情况下无法使用此字段对查询这两个索引的请求进行排序。但是,您可以使用 numeric_type 选项将类型强制为一种或另一种类型,以便为所有索引强制指定特定类型

$params = [
    'index' => 'index_long,index_double',
    'body' => [
        'sort' => [
            [
                'field' => [
                    'numeric_type' => 'double',
                ],
            ],
        ],
    ],
];
$response = $client->search($params);
resp = client.search(
    index="index_long,index_double",
    sort=[
        {
            "field": {
                "numeric_type": "double"
            }
        }
    ],
)
print(resp)
response = client.search(
  index: 'index_long,index_double',
  body: {
    sort: [
      {
        field: {
          numeric_type: 'double'
        }
      }
    ]
  }
)
puts response
res, err := es.Search(
	es.Search.WithIndex("index_long,index_double"),
	es.Search.WithBody(strings.NewReader(`{
	  "sort": [
	    {
	      "field": {
	        "numeric_type": "double"
	      }
	    }
	  ]
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  index: "index_long,index_double",
  sort: [
    {
      field: {
        numeric_type: "double",
      },
    },
  ],
});
console.log(response);
POST /index_long,index_double/_search
{
   "sort" : [
      {
        "field" : {
            "numeric_type" : "double"
        }
      }
   ]
}

在上面的示例中,为了与 index_double 索引生成的值兼容,index_long 索引的值将转换为 double。也可以将浮点字段转换为 long,但请注意,在这种情况下,浮点数将替换为小于或等于(如果该值是负数,则大于或等于)参数且等于数学整数的最大值。

此选项还可用于将使用毫秒分辨率的 date 字段转换为具有纳秒分辨率的 date_nanos 字段。例如考虑以下两个索引

resp = client.indices.create(
    index="index_double",
    mappings={
        "properties": {
            "field": {
                "type": "date"
            }
        }
    },
)
print(resp)
response = client.indices.create(
  index: 'index_double',
  body: {
    mappings: {
      properties: {
        field: {
          type: 'date'
        }
      }
    }
  }
)
puts response
res, err := es.Indices.Create(
	"index_double",
	es.Indices.Create.WithBody(strings.NewReader(`{
	  "mappings": {
	    "properties": {
	      "field": {
	        "type": "date"
	      }
	    }
	  }
	}`)),
)
fmt.Println(res, err)
const response = await client.indices.create({
  index: "index_double",
  mappings: {
    properties: {
      field: {
        type: "date",
      },
    },
  },
});
console.log(response);
PUT /index_double
{
  "mappings": {
    "properties": {
      "field": { "type": "date" }
    }
  }
}
resp = client.indices.create(
    index="index_long",
    mappings={
        "properties": {
            "field": {
                "type": "date_nanos"
            }
        }
    },
)
print(resp)
response = client.indices.create(
  index: 'index_long',
  body: {
    mappings: {
      properties: {
        field: {
          type: 'date_nanos'
        }
      }
    }
  }
)
puts response
res, err := es.Indices.Create(
	"index_long",
	es.Indices.Create.WithBody(strings.NewReader(`{
	  "mappings": {
	    "properties": {
	      "field": {
	        "type": "date_nanos"
	      }
	    }
	  }
	}`)),
)
fmt.Println(res, err)
const response = await client.indices.create({
  index: "index_long",
  mappings: {
    properties: {
      field: {
        type: "date_nanos",
      },
    },
  },
});
console.log(response);
PUT /index_long
{
  "mappings": {
    "properties": {
      "field": { "type": "date_nanos" }
    }
  }
}

这些索引中的值以不同的分辨率存储,因此对这些字段进行排序将始终按 datedate_nanos 之前排序(升序)。使用 numeric_type 类型选项,可以为排序设置单个分辨率,设置为 date 会将 date_nanos 转换为毫秒分辨率,而 date_nanos 会将 date 字段中的值转换为纳秒分辨率

$params = [
    'index' => 'index_long,index_double',
    'body' => [
        'sort' => [
            [
                'field' => [
                    'numeric_type' => 'date_nanos',
                ],
            ],
        ],
    ],
];
$response = $client->search($params);
resp = client.search(
    index="index_long,index_double",
    sort=[
        {
            "field": {
                "numeric_type": "date_nanos"
            }
        }
    ],
)
print(resp)
res, err := es.Search(
	es.Search.WithIndex("index_long,index_double"),
	es.Search.WithBody(strings.NewReader(`{
	  "sort": [
	    {
	      "field": {
	        "numeric_type": "date_nanos"
	      }
	    }
	  ]
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  index: "index_long,index_double",
  sort: [
    {
      field: {
        numeric_type: "date_nanos",
      },
    },
  ],
});
console.log(response);
POST /index_long,index_double/_search
{
   "sort" : [
      {
        "field" : {
            "numeric_type" : "date_nanos"
        }
      }
   ]
}

为避免溢出,对 1970 年之前和 2262 年之后的日期不能应用转换为 date_nanos,因为纳秒表示为 longs。

在嵌套对象中排序。

编辑

Elasticsearch 还支持按一个或多个嵌套对象内的字段进行排序。按嵌套字段排序支持具有 nested 排序选项,该选项具有以下属性

path
定义要对哪个嵌套对象进行排序。实际的排序字段必须是此嵌套对象内的直接字段。按嵌套字段排序时,此字段是必需的。
filter
嵌套路径内的内部对象应该匹配的过滤器,以便排序时考虑其字段值。常见情况是在嵌套过滤器或查询中重复查询/过滤器。默认情况下,没有激活任何 filter
max_children
在选择排序值时,每个根文档要考虑的最大子项数。默认为无限制。
nested
与顶层 nested 相同,但适用于当前嵌套对象内的另一个嵌套路径。

如果在没有 nested 上下文的情况下在排序中定义了嵌套字段,则 Elasticsearch 将引发错误。

嵌套排序示例
编辑

在下面的示例中,offernested 类型的字段。需要指定嵌套的 path;否则,Elasticsearch 不知道需要在哪个嵌套级别捕获排序值。

$params = [
    'body' => [
        'query' => [
            'term' => [
                'product' => 'chocolate',
            ],
        ],
        'sort' => [
            [
                'offer.price' => [
                    'mode' => 'avg',
                    'order' => 'asc',
                    'nested' => [
                        'path' => 'offer',
                        'filter' => [
                            'term' => [
                                'offer.color' => 'blue',
                            ],
                        ],
                    ],
                ],
            ],
        ],
    ],
];
$response = $client->search($params);
resp = client.search(
    query={
        "term": {
            "product": "chocolate"
        }
    },
    sort=[
        {
            "offer.price": {
                "mode": "avg",
                "order": "asc",
                "nested": {
                    "path": "offer",
                    "filter": {
                        "term": {
                            "offer.color": "blue"
                        }
                    }
                }
            }
        }
    ],
)
print(resp)
response = client.search(
  body: {
    query: {
      term: {
        product: 'chocolate'
      }
    },
    sort: [
      {
        'offer.price' => {
          mode: 'avg',
          order: 'asc',
          nested: {
            path: 'offer',
            filter: {
              term: {
                'offer.color' => 'blue'
              }
            }
          }
        }
      }
    ]
  }
)
puts response
res, err := es.Search(
	es.Search.WithBody(strings.NewReader(`{
	  "query": {
	    "term": {
	      "product": "chocolate"
	    }
	  },
	  "sort": [
	    {
	      "offer.price": {
	        "mode": "avg",
	        "order": "asc",
	        "nested": {
	          "path": "offer",
	          "filter": {
	            "term": {
	              "offer.color": "blue"
	            }
	          }
	        }
	      }
	    }
	  ]
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  query: {
    term: {
      product: "chocolate",
    },
  },
  sort: [
    {
      "offer.price": {
        mode: "avg",
        order: "asc",
        nested: {
          path: "offer",
          filter: {
            term: {
              "offer.color": "blue",
            },
          },
        },
      },
    },
  ],
});
console.log(response);
POST /_search
{
   "query" : {
      "term" : { "product" : "chocolate" }
   },
   "sort" : [
       {
          "offer.price" : {
             "mode" :  "avg",
             "order" : "asc",
             "nested": {
                "path": "offer",
                "filter": {
                   "term" : { "offer.color" : "blue" }
                }
             }
          }
       }
    ]
}

在下面的示例中,parentchild 字段是 nested 类型。需要在每个级别指定 nested.path;否则,Elasticsearch 不知道需要在哪个嵌套级别捕获排序值。

$params = [
    'body' => [
        'query' => [
            'nested' => [
                'path' => 'parent',
                'query' => [
                    'bool' => [
                        'must' => [
                            'range' => [
                                'parent.age' => [
                                    'gte' => 21,
                                ],
                            ],
                        ],
                        'filter' => [
                            'nested' => [
                                'path' => 'parent.child',
                                'query' => [
                                    'match' => [
                                        'parent.child.name' => 'matt',
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
            ],
        ],
        'sort' => [
            [
                'parent.child.age' => [
                    'mode' => 'min',
                    'order' => 'asc',
                    'nested' => [
                        'path' => 'parent',
                        'filter' => [
                            'range' => [
                                'parent.age' => [
                                    'gte' => 21,
                                ],
                            ],
                        ],
                        'nested' => [
                            'path' => 'parent.child',
                            'filter' => [
                                'match' => [
                                    'parent.child.name' => 'matt',
                                ],
                            ],
                        ],
                    ],
                ],
            ],
        ],
    ],
];
$response = $client->search($params);
resp = client.search(
    query={
        "nested": {
            "path": "parent",
            "query": {
                "bool": {
                    "must": {
                        "range": {
                            "parent.age": {
                                "gte": 21
                            }
                        }
                    },
                    "filter": {
                        "nested": {
                            "path": "parent.child",
                            "query": {
                                "match": {
                                    "parent.child.name": "matt"
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    sort=[
        {
            "parent.child.age": {
                "mode": "min",
                "order": "asc",
                "nested": {
                    "path": "parent",
                    "filter": {
                        "range": {
                            "parent.age": {
                                "gte": 21
                            }
                        }
                    },
                    "nested": {
                        "path": "parent.child",
                        "filter": {
                            "match": {
                                "parent.child.name": "matt"
                            }
                        }
                    }
                }
            }
        }
    ],
)
print(resp)
res, err := es.Search(
	es.Search.WithBody(strings.NewReader(`{
	  "query": {
	    "nested": {
	      "path": "parent",
	      "query": {
	        "bool": {
	          "must": {
	            "range": {
	              "parent.age": {
	                "gte": 21
	              }
	            }
	          },
	          "filter": {
	            "nested": {
	              "path": "parent.child",
	              "query": {
	                "match": {
	                  "parent.child.name": "matt"
	                }
	              }
	            }
	          }
	        }
	      }
	    }
	  },
	  "sort": [
	    {
	      "parent.child.age": {
	        "mode": "min",
	        "order": "asc",
	        "nested": {
	          "path": "parent",
	          "filter": {
	            "range": {
	              "parent.age": {
	                "gte": 21
	              }
	            }
	          },
	          "nested": {
	            "path": "parent.child",
	            "filter": {
	              "match": {
	                "parent.child.name": "matt"
	              }
	            }
	          }
	        }
	      }
	    }
	  ]
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  query: {
    nested: {
      path: "parent",
      query: {
        bool: {
          must: {
            range: {
              "parent.age": {
                gte: 21,
              },
            },
          },
          filter: {
            nested: {
              path: "parent.child",
              query: {
                match: {
                  "parent.child.name": "matt",
                },
              },
            },
          },
        },
      },
    },
  },
  sort: [
    {
      "parent.child.age": {
        mode: "min",
        order: "asc",
        nested: {
          path: "parent",
          filter: {
            range: {
              "parent.age": {
                gte: 21,
              },
            },
          },
          nested: {
            path: "parent.child",
            filter: {
              match: {
                "parent.child.name": "matt",
              },
            },
          },
        },
      },
    },
  ],
});
console.log(response);
POST /_search
{
   "query": {
      "nested": {
         "path": "parent",
         "query": {
            "bool": {
                "must": {"range": {"parent.age": {"gte": 21}}},
                "filter": {
                    "nested": {
                        "path": "parent.child",
                        "query": {"match": {"parent.child.name": "matt"}}
                    }
                }
            }
         }
      }
   },
   "sort" : [
      {
         "parent.child.age" : {
            "mode" :  "min",
            "order" : "asc",
            "nested": {
               "path": "parent",
               "filter": {
                  "range": {"parent.age": {"gte": 21}}
               },
               "nested": {
                  "path": "parent.child",
                  "filter": {
                     "match": {"parent.child.name": "matt"}
                  }
               }
            }
         }
      }
   ]
}

按脚本和按地理距离排序时也支持嵌套排序。

缺失值

编辑

missing 参数指定应如何处理缺少排序字段的文档:可以将 missing 值设置为 _last_first 或自定义值(将用作缺少文档的排序值)。默认值为 _last

例如

resp = client.search(
    sort=[
        {
            "price": {
                "missing": "_last"
            }
        }
    ],
    query={
        "term": {
            "product": "chocolate"
        }
    },
)
print(resp)
response = client.search(
  body: {
    sort: [
      {
        price: {
          missing: '_last'
        }
      }
    ],
    query: {
      term: {
        product: 'chocolate'
      }
    }
  }
)
puts response
res, err := es.Search(
	es.Search.WithBody(strings.NewReader(`{
	  "sort": [
	    {
	      "price": {
	        "missing": "_last"
	      }
	    }
	  ],
	  "query": {
	    "term": {
	      "product": "chocolate"
	    }
	  }
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  sort: [
    {
      price: {
        missing: "_last",
      },
    },
  ],
  query: {
    term: {
      product: "chocolate",
    },
  },
});
console.log(response);
GET /_search
{
  "sort" : [
    { "price" : {"missing" : "_last"} }
  ],
  "query" : {
    "term" : { "product" : "chocolate" }
  }
}

如果嵌套的内部对象与 nested.filter 不匹配,则使用缺失值。

忽略未映射的字段

编辑

默认情况下,如果某个字段没有关联的映射,则搜索请求将失败。 unmapped_type 选项允许您忽略没有映射的字段,而不是按这些字段进行排序。此参数的值用于确定要发出哪些排序值。以下是如何使用它的示例

resp = client.search(
    sort=[
        {
            "price": {
                "unmapped_type": "long"
            }
        }
    ],
    query={
        "term": {
            "product": "chocolate"
        }
    },
)
print(resp)
response = client.search(
  body: {
    sort: [
      {
        price: {
          unmapped_type: 'long'
        }
      }
    ],
    query: {
      term: {
        product: 'chocolate'
      }
    }
  }
)
puts response
res, err := es.Search(
	es.Search.WithBody(strings.NewReader(`{
	  "sort": [
	    {
	      "price": {
	        "unmapped_type": "long"
	      }
	    }
	  ],
	  "query": {
	    "term": {
	      "product": "chocolate"
	    }
	  }
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  sort: [
    {
      price: {
        unmapped_type: "long",
      },
    },
  ],
  query: {
    term: {
      product: "chocolate",
    },
  },
});
console.log(response);
GET /_search
{
  "sort" : [
    { "price" : {"unmapped_type" : "long"} }
  ],
  "query" : {
    "term" : { "product" : "chocolate" }
  }
}

如果查询的任何索引都没有 price 的映射,则 Elasticsearch 将像处理类型为 long 的映射一样处理它,并且此索引中的所有文档都没有此字段的值。

地理距离排序

编辑

允许按 _geo_distance 排序。以下是一个示例,假设 pin.locationgeo_point 类型的字段

resp = client.search(
    sort=[
        {
            "_geo_distance": {
                "pin.location": [
                    -70,
                    40
                ],
                "order": "asc",
                "unit": "km",
                "mode": "min",
                "distance_type": "arc",
                "ignore_unmapped": True
            }
        }
    ],
    query={
        "term": {
            "user": "kimchy"
        }
    },
)
print(resp)
response = client.search(
  body: {
    sort: [
      {
        _geo_distance: {
          'pin.location' => [
            -70,
            40
          ],
          order: 'asc',
          unit: 'km',
          mode: 'min',
          distance_type: 'arc',
          ignore_unmapped: true
        }
      }
    ],
    query: {
      term: {
        user: 'kimchy'
      }
    }
  }
)
puts response
res, err := es.Search(
	es.Search.WithBody(strings.NewReader(`{
	  "sort": [
	    {
	      "_geo_distance": {
	        "pin.location": [
	          -70,
	          40
	        ],
	        "order": "asc",
	        "unit": "km",
	        "mode": "min",
	        "distance_type": "arc",
	        "ignore_unmapped": true
	      }
	    }
	  ],
	  "query": {
	    "term": {
	      "user": "kimchy"
	    }
	  }
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  sort: [
    {
      _geo_distance: {
        "pin.location": [-70, 40],
        order: "asc",
        unit: "km",
        mode: "min",
        distance_type: "arc",
        ignore_unmapped: true,
      },
    },
  ],
  query: {
    term: {
      user: "kimchy",
    },
  },
});
console.log(response);
GET /_search
{
  "sort" : [
    {
      "_geo_distance" : {
          "pin.location" : [-70, 40],
          "order" : "asc",
          "unit" : "km",
          "mode" : "min",
          "distance_type" : "arc",
          "ignore_unmapped": true
      }
    }
  ],
  "query" : {
    "term" : { "user" : "kimchy" }
  }
}
distance_type
如何计算距离。可以是 arc (默认)或 plane(更快,但在长距离和靠近两极时不太准确)。
mode
如果字段有多个地理点,该怎么办。默认情况下,升序排序时会考虑最短距离,而降序排序时会考虑最长距离。支持的值为 minmaxmedianavg
unit
计算排序值时要使用的单位。默认值为 m(米)。
ignore_unmapped
指示是否应将未映射的字段视为缺失值。将其设置为 true 等效于在字段排序中指定 unmapped_type。默认值为 false(未映射的字段会导致搜索失败)。

地理距离排序不支持可配置的缺失值:当文档没有用于距离计算的字段值时,距离将始终被认为等于 Infinity

提供坐标时支持以下格式

作为属性的纬度和经度
编辑
resp = client.search(
    sort=[
        {
            "_geo_distance": {
                "pin.location": {
                    "lat": 40,
                    "lon": -70
                },
                "order": "asc",
                "unit": "km"
            }
        }
    ],
    query={
        "term": {
            "user": "kimchy"
        }
    },
)
print(resp)
response = client.search(
  body: {
    sort: [
      {
        _geo_distance: {
          'pin.location' => {
            lat: 40,
            lon: -70
          },
          order: 'asc',
          unit: 'km'
        }
      }
    ],
    query: {
      term: {
        user: 'kimchy'
      }
    }
  }
)
puts response
res, err := es.Search(
	es.Search.WithBody(strings.NewReader(`{
	  "sort": [
	    {
	      "_geo_distance": {
	        "pin.location": {
	          "lat": 40,
	          "lon": -70
	        },
	        "order": "asc",
	        "unit": "km"
	      }
	    }
	  ],
	  "query": {
	    "term": {
	      "user": "kimchy"
	    }
	  }
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  sort: [
    {
      _geo_distance: {
        "pin.location": {
          lat: 40,
          lon: -70,
        },
        order: "asc",
        unit: "km",
      },
    },
  ],
  query: {
    term: {
      user: "kimchy",
    },
  },
});
console.log(response);
GET /_search
{
  "sort" : [
    {
      "_geo_distance" : {
        "pin.location" : {
          "lat" : 40,
          "lon" : -70
        },
        "order" : "asc",
        "unit" : "km"
      }
    }
  ],
  "query" : {
    "term" : { "user" : "kimchy" }
  }
}
纬度/经度(以 WKT 字符串表示)
编辑

格式为 Well-Known Text

resp = client.search(
    sort=[
        {
            "_geo_distance": {
                "pin.location": "POINT (-70 40)",
                "order": "asc",
                "unit": "km"
            }
        }
    ],
    query={
        "term": {
            "user": "kimchy"
        }
    },
)
print(resp)
response = client.search(
  body: {
    sort: [
      {
        _geo_distance: {
          'pin.location' => 'POINT (-70 40)',
          order: 'asc',
          unit: 'km'
        }
      }
    ],
    query: {
      term: {
        user: 'kimchy'
      }
    }
  }
)
puts response
const response = await client.search({
  sort: [
    {
      _geo_distance: {
        "pin.location": "POINT (-70 40)",
        order: "asc",
        unit: "km",
      },
    },
  ],
  query: {
    term: {
      user: "kimchy",
    },
  },
});
console.log(response);
GET /_search
{
  "sort": [
    {
      "_geo_distance": {
        "pin.location": "POINT (-70 40)",
        "order": "asc",
        "unit": "km"
      }
    }
  ],
  "query": {
    "term": { "user": "kimchy" }
  }
}
Geohash
编辑
resp = client.search(
    sort=[
        {
            "_geo_distance": {
                "pin.location": "drm3btev3e86",
                "order": "asc",
                "unit": "km"
            }
        }
    ],
    query={
        "term": {
            "user": "kimchy"
        }
    },
)
print(resp)
response = client.search(
  body: {
    sort: [
      {
        _geo_distance: {
          'pin.location' => 'drm3btev3e86',
          order: 'asc',
          unit: 'km'
        }
      }
    ],
    query: {
      term: {
        user: 'kimchy'
      }
    }
  }
)
puts response
res, err := es.Search(
	es.Search.WithBody(strings.NewReader(`{
	  "sort": [
	    {
	      "_geo_distance": {
	        "pin.location": "drm3btev3e86",
	        "order": "asc",
	        "unit": "km"
	      }
	    }
	  ],
	  "query": {
	    "term": {
	      "user": "kimchy"
	    }
	  }
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  sort: [
    {
      _geo_distance: {
        "pin.location": "drm3btev3e86",
        order: "asc",
        unit: "km",
      },
    },
  ],
  query: {
    term: {
      user: "kimchy",
    },
  },
});
console.log(response);
GET /_search
{
  "sort": [
    {
      "_geo_distance": {
        "pin.location": "drm3btev3e86",
        "order": "asc",
        "unit": "km"
      }
    }
  ],
  "query": {
    "term": { "user": "kimchy" }
  }
}
纬度/经度(以数组表示)
编辑

格式为 [经度, 纬度],注意,此处经度/纬度的顺序是为了符合 GeoJSON 标准。

resp = client.search(
    sort=[
        {
            "_geo_distance": {
                "pin.location": [
                    -70,
                    40
                ],
                "order": "asc",
                "unit": "km"
            }
        }
    ],
    query={
        "term": {
            "user": "kimchy"
        }
    },
)
print(resp)
response = client.search(
  body: {
    sort: [
      {
        _geo_distance: {
          'pin.location' => [
            -70,
            40
          ],
          order: 'asc',
          unit: 'km'
        }
      }
    ],
    query: {
      term: {
        user: 'kimchy'
      }
    }
  }
)
puts response
res, err := es.Search(
	es.Search.WithBody(strings.NewReader(`{
	  "sort": [
	    {
	      "_geo_distance": {
	        "pin.location": [
	          -70,
	          40
	        ],
	        "order": "asc",
	        "unit": "km"
	      }
	    }
	  ],
	  "query": {
	    "term": {
	      "user": "kimchy"
	    }
	  }
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  sort: [
    {
      _geo_distance: {
        "pin.location": [-70, 40],
        order: "asc",
        unit: "km",
      },
    },
  ],
  query: {
    term: {
      user: "kimchy",
    },
  },
});
console.log(response);
GET /_search
{
  "sort": [
    {
      "_geo_distance": {
        "pin.location": [ -70, 40 ],
        "order": "asc",
        "unit": "km"
      }
    }
  ],
  "query": {
    "term": { "user": "kimchy" }
  }
}

多个参考点

编辑

可以传递多个地理位置点,作为包含任何 geo_point 格式的数组,例如:

resp = client.search(
    sort=[
        {
            "_geo_distance": {
                "pin.location": [
                    [
                        -70,
                        40
                    ],
                    [
                        -71,
                        42
                    ]
                ],
                "order": "asc",
                "unit": "km"
            }
        }
    ],
    query={
        "term": {
            "user": "kimchy"
        }
    },
)
print(resp)
response = client.search(
  body: {
    sort: [
      {
        _geo_distance: {
          'pin.location' => [
            [
              -70,
              40
            ],
            [
              -71,
              42
            ]
          ],
          order: 'asc',
          unit: 'km'
        }
      }
    ],
    query: {
      term: {
        user: 'kimchy'
      }
    }
  }
)
puts response
res, err := es.Search(
	es.Search.WithBody(strings.NewReader(`{
	  "sort": [
	    {
	      "_geo_distance": {
	        "pin.location": [
	          [
	            -70,
	            40
	          ],
	          [
	            -71,
	            42
	          ]
	        ],
	        "order": "asc",
	        "unit": "km"
	      }
	    }
	  ],
	  "query": {
	    "term": {
	      "user": "kimchy"
	    }
	  }
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  sort: [
    {
      _geo_distance: {
        "pin.location": [
          [-70, 40],
          [-71, 42],
        ],
        order: "asc",
        unit: "km",
      },
    },
  ],
  query: {
    term: {
      user: "kimchy",
    },
  },
});
console.log(response);
GET /_search
{
  "sort": [
    {
      "_geo_distance": {
        "pin.location": [ [ -70, 40 ], [ -71, 42 ] ],
        "order": "asc",
        "unit": "km"
      }
    }
  ],
  "query": {
    "term": { "user": "kimchy" }
  }
}

等等。

文档的最终距离将是文档中所有点到排序请求中给定所有点的 min/max/avg(通过 mode 定义)距离。

基于脚本的排序

编辑

允许基于自定义脚本进行排序,这是一个示例:

resp = client.search(
    query={
        "term": {
            "user": "kimchy"
        }
    },
    sort={
        "_script": {
            "type": "number",
            "script": {
                "lang": "painless",
                "source": "doc['field_name'].value * params.factor",
                "params": {
                    "factor": 1.1
                }
            },
            "order": "asc"
        }
    },
)
print(resp)
response = client.search(
  body: {
    query: {
      term: {
        user: 'kimchy'
      }
    },
    sort: {
      _script: {
        type: 'number',
        script: {
          lang: 'painless',
          source: "doc['field_name'].value * params.factor",
          params: {
            factor: 1.1
          }
        },
        order: 'asc'
      }
    }
  }
)
puts response
res, err := es.Search(
	es.Search.WithBody(strings.NewReader(`{
	  "query": {
	    "term": {
	      "user": "kimchy"
	    }
	  },
	  "sort": {
	    "_script": {
	      "type": "number",
	      "script": {
	        "lang": "painless",
	        "source": "doc['field_name'].value * params.factor",
	        "params": {
	          "factor": 1.1
	        }
	      },
	      "order": "asc"
	    }
	  }
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  query: {
    term: {
      user: "kimchy",
    },
  },
  sort: {
    _script: {
      type: "number",
      script: {
        lang: "painless",
        source: "doc['field_name'].value * params.factor",
        params: {
          factor: 1.1,
        },
      },
      order: "asc",
    },
  },
});
console.log(response);
GET /_search
{
  "query": {
    "term": { "user": "kimchy" }
  },
  "sort": {
    "_script": {
      "type": "number",
      "script": {
        "lang": "painless",
        "source": "doc['field_name'].value * params.factor",
        "params": {
          "factor": 1.1
        }
      },
      "order": "asc"
    }
  }
}

跟踪得分

编辑

当对字段进行排序时,不会计算得分。通过将 track_scores 设置为 true,仍然会计算并跟踪得分。

resp = client.search(
    track_scores=True,
    sort=[
        {
            "post_date": {
                "order": "desc"
            }
        },
        {
            "name": "desc"
        },
        {
            "age": "desc"
        }
    ],
    query={
        "term": {
            "user": "kimchy"
        }
    },
)
print(resp)
response = client.search(
  body: {
    track_scores: true,
    sort: [
      {
        post_date: {
          order: 'desc'
        }
      },
      {
        name: 'desc'
      },
      {
        age: 'desc'
      }
    ],
    query: {
      term: {
        user: 'kimchy'
      }
    }
  }
)
puts response
res, err := es.Search(
	es.Search.WithBody(strings.NewReader(`{
	  "track_scores": true,
	  "sort": [
	    {
	      "post_date": {
	        "order": "desc"
	      }
	    },
	    {
	      "name": "desc"
	    },
	    {
	      "age": "desc"
	    }
	  ],
	  "query": {
	    "term": {
	      "user": "kimchy"
	    }
	  }
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  track_scores: true,
  sort: [
    {
      post_date: {
        order: "desc",
      },
    },
    {
      name: "desc",
    },
    {
      age: "desc",
    },
  ],
  query: {
    term: {
      user: "kimchy",
    },
  },
});
console.log(response);
GET /_search
{
  "track_scores": true,
  "sort" : [
    { "post_date" : {"order" : "desc"} },
    { "name" : "desc" },
    { "age" : "desc" }
  ],
  "query" : {
    "term" : { "user" : "kimchy" }
  }
}

内存注意事项

编辑

排序时,相关的排序字段值会加载到内存中。这意味着每个分片应该有足够的内存来容纳它们。对于基于字符串的类型,排序的字段不应该被分析/分词。对于数字类型,如果可能,建议将类型显式设置为更窄的类型(如 shortintegerfloat)。