検索結果のソート
特定のフィールドに対して1つ以上のソートを追加することができます。各ソートは逆にすることも可能です。ソートはフィールドごとに定義され、_score
を使用してスコアでソートし、_doc
を使用してインデックス順でソートします。
次のインデックスマッピングを仮定します:
Python
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)
Ruby
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
Go
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)
Js
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);
Console
PUT /my-index-000001
{
"mappings": {
"properties": {
"post_date": { "type": "date" },
"user": {
"type": "keyword"
},
"name": {
"type": "keyword"
},
"age": { "type": "integer" }
}
}
}
Python
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)
Ruby
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
Js
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);
Console
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" }
}
}
## 値のソート
検索応答には、各ドキュメントの`````sort`````値が含まれています。`````format`````パラメータを使用して、[`````date`````](/read/elasticsearch-8-15/9dfa1da42eb162ff.md)および[`````date_nanos`````](/read/elasticsearch-8-15/b43d322aa0194e4b.md)フィールドの`````sort`````値の[日付形式](03b5827d4785ee50.md#built-in-date-formats)を指定します。次の検索は、`````strict_date_optional_time_nanos`````形式の`````post_date`````フィールドの`````sort`````値を返します。
#### Python
``````python
resp = client.search(
index="my-index-000001",
sort=[
{
"post_date": {
"format": "strict_date_optional_time_nanos"
}
}
],
query={
"term": {
"user": "kimchy"
}
},
)
print(resp)
`
Ruby
response = client.search(
index: 'my-index-000001',
body: {
sort: [
{
post_date: {
format: 'strict_date_optional_time_nanos'
}
}
],
query: {
term: {
user: 'kimchy'
}
}
}
)
puts response
Js
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);
Console
GET /my-index-000001/_search
{
"sort" : [
{ "post_date" : {"format": "strict_date_optional_time_nanos"}}
],
"query" : {
"term" : { "user" : "kimchy" }
}
}
ソート順
| | |
| --- | --- |
| `````asc````` | 昇順でソート |
| `````desc````` | 降順でソート |
`````_score`````でソートする場合、順序は`````desc`````にデフォルト設定され、他の何かでソートする場合は`````asc`````にデフォルト設定されます。
## ソートモードオプション
Elasticsearchは、配列または複数値フィールドによるソートをサポートしています。`````mode`````オプションは、ドキュメントが属する配列値の選択を制御します。`````mode`````オプションには、次の値を持つことができます:
| | |
| --- | --- |
| `````min````` | 最小値を選択します。 |
| `````max````` | 最大値を選択します。 |
| `````sum````` | すべての値の合計をソート値として使用します。数値ベースの配列フィールドにのみ適用されます。 |
| `````avg````` | すべての値の平均をソート値として使用します。数値ベースの配列フィールドにのみ適用されます。 |
| `````median````` | すべての値の中央値をソート値として使用します。数値ベースの配列フィールドにのみ適用されます。 |
昇順ソートのデフォルトソートモードは`````min`````です—最小値が選択されます。降順のデフォルトソートモードは`````max`````です—最大値が選択されます。
### ソートモードの使用例
以下の例では、フィールド価格がドキュメントごとに複数の価格を持っています。この場合、結果のヒットは、ドキュメントごとの平均価格に基づいて昇順でソートされます。
#### Python
``````python
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)
`
Ruby
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
Go
{
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)
}
Js
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);
Console
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
オプションを使用して値を1つの型から別の型にキャストすることも可能です。このオプションは、["double", "long", "date", "date_nanos"
]を受け入れ、ソートフィールドが異なるマッピングを持つ複数のデータストリームやインデックスを横断する検索に役立ちます。
たとえば、これらの2つのインデックスを考えてみてください:
Python
resp = client.indices.create(
index="index_double",
mappings={
"properties": {
"field": {
"type": "double"
}
}
},
)
print(resp)
Ruby
response = client.indices.create(
index: 'index_double',
body: {
mappings: {
properties: {
field: {
type: 'double'
}
}
}
}
)
puts response
Go
res, err := es.Indices.Create(
"index_double",
es.Indices.Create.WithBody(strings.NewReader(`{
"mappings": {
"properties": {
"field": {
"type": "double"
}
}
}
}`)),
)
fmt.Println(res, err)
Js
const response = await client.indices.create({
index: "index_double",
mappings: {
properties: {
field: {
type: "double",
},
},
},
});
console.log(response);
Console
PUT /index_double
{
"mappings": {
"properties": {
"field": { "type": "double" }
}
}
}
Python
resp = client.indices.create(
index="index_long",
mappings={
"properties": {
"field": {
"type": "long"
}
}
},
)
print(resp)
Ruby
response = client.indices.create(
index: 'index_long',
body: {
mappings: {
properties: {
field: {
type: 'long'
}
}
}
}
)
puts response
Go
res, err := es.Indices.Create(
"index_long",
es.Indices.Create.WithBody(strings.NewReader(`{
"mappings": {
"properties": {
"field": {
"type": "long"
}
}
}
}`)),
)
fmt.Println(res, err)
Js
const response = await client.indices.create({
index: "index_long",
mappings: {
properties: {
field: {
type: "long",
},
},
},
});
console.log(response);
Console
PUT /index_long
{
"mappings": {
"properties": {
"field": { "type": "long" }
}
}
}
#### Php
``````php
$params = [
'index' => 'index_long,index_double',
'body' => [
'sort' => [
[
'field' => [
'numeric_type' => 'double',
],
],
],
],
];
$response = $client->search($params);
`
Python
resp = client.search(
index="index_long,index_double",
sort=[
{
"field": {
"numeric_type": "double"
}
}
],
)
print(resp)
Ruby
response = client.search(
index: 'index_long,index_double',
body: {
sort: [
{
field: {
numeric_type: 'double'
}
}
]
}
)
puts response
Go
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)
Js
const response = await client.search({
index: "index_long,index_double",
sort: [
{
field: {
numeric_type: "double",
},
},
],
});
console.log(response);
Console
POST /index_long,index_double/_search
{
"sort" : [
{
"field" : {
"numeric_type" : "double"
}
}
]
}
上記の例では、index_long
インデックスの値はダブルにキャストされ、index_double
インデックスによって生成された値と互換性を持たせます。また、浮動小数点フィールドをlong
に変換することも可能ですが、この場合、浮動小数点は引数以下の最大値(負の値の場合は引数以上の最小値)に置き換えられ、数学的整数に等しくなります。
このオプションは、ミリ秒解像度を使用するdate
フィールドをナノ秒解像度のdate_nanos
フィールドに変換するためにも使用できます。たとえば、これらの2つのインデックスを考えてみてください:
Python
resp = client.indices.create(
index="index_double",
mappings={
"properties": {
"field": {
"type": "date"
}
}
},
)
print(resp)
Ruby
response = client.indices.create(
index: 'index_double',
body: {
mappings: {
properties: {
field: {
type: 'date'
}
}
}
}
)
puts response
Go
res, err := es.Indices.Create(
"index_double",
es.Indices.Create.WithBody(strings.NewReader(`{
"mappings": {
"properties": {
"field": {
"type": "date"
}
}
}
}`)),
)
fmt.Println(res, err)
Js
const response = await client.indices.create({
index: "index_double",
mappings: {
properties: {
field: {
type: "date",
},
},
},
});
console.log(response);
Console
PUT /index_double
{
"mappings": {
"properties": {
"field": { "type": "date" }
}
}
}
Python
resp = client.indices.create(
index="index_long",
mappings={
"properties": {
"field": {
"type": "date_nanos"
}
}
},
)
print(resp)
Ruby
response = client.indices.create(
index: 'index_long',
body: {
mappings: {
properties: {
field: {
type: 'date_nanos'
}
}
}
}
)
puts response
Go
res, err := es.Indices.Create(
"index_long",
es.Indices.Create.WithBody(strings.NewReader(`{
"mappings": {
"properties": {
"field": {
"type": "date_nanos"
}
}
}
}`)),
)
fmt.Println(res, err)
Js
const response = await client.indices.create({
index: "index_long",
mappings: {
properties: {
field: {
type: "date_nanos",
},
},
},
});
console.log(response);
Console
PUT /index_long
{
"mappings": {
"properties": {
"field": { "type": "date_nanos" }
}
}
}
これらのインデックスの値は異なる解像度で保存されているため、これらのフィールドでのソートは常にdate
をdate_nanos
(昇順)でソートします。numeric_type
型オプションを使用すると、ソートの単一解像度を設定できます。date
に設定すると、date_nanos
はミリ秒解像度に変換され、date_nanos
はdate
フィールドの値をナノ秒解像度に変換します。
Php
$params = [
'index' => 'index_long,index_double',
'body' => [
'sort' => [
[
'field' => [
'numeric_type' => 'date_nanos',
],
],
],
],
];
$response = $client->search($params);
Python
resp = client.search(
index="index_long,index_double",
sort=[
{
"field": {
"numeric_type": "date_nanos"
}
}
],
)
print(resp)
Go
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)
Js
const response = await client.search({
index: "index_long,index_double",
sort: [
{
field: {
numeric_type: "date_nanos",
},
},
],
});
console.log(response);
Console
POST /index_long,index_double/_search
{
"sort" : [
{
"field" : {
"numeric_type" : "date_nanos"
}
}
]
}
オーバーフローを避けるため、date_nanos
への変換は1970年以前および2262年以降の日付には適用できません。ナノ秒はロングとして表現されます。
ネストされたオブジェクト内のソート
Elasticsearchは、1つ以上のネストされたオブジェクト内のフィールドによるソートもサポートしています。ネストされたフィールドによるソートサポートには、次のプロパティを持つnested
ソートオプションがあります:
path
- どのネストされたオブジェクトでソートするかを定義します。実際のソートフィールドは、このネストされたオブジェクト内の直接フィールドでなければなりません。ネストされたフィールドでソートする場合、このフィールドは必須です。
filter
- ネストされたパス内の内部オブジェクトが一致する必要があるフィルター。一般的なケースは、ネストされたフィルターまたはクエリ内でクエリ/フィルターを繰り返すことです。デフォルトでは
filter
はアクティブではありません。 max_children
- ソート値を選択する際に、ルートドキュメントごとに考慮する最大子数。デフォルトは無制限です。
nested
- トップレベルの
nested
と同じですが、現在のネストされたオブジェクト内の別のネストされたパスに適用されます。
ネストされたフィールドがnested
コンテキストなしでソートに定義されている場合、Elasticsearchはエラーをスローします。
ネストされたソートの例
以下の例では、offer
はnested
型のフィールドです。ネストされたpath
を指定する必要があります。そうしないと、Elasticsearchはソート値をキャプチャするネストされたレベルを知りません。
Php
$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);
Python
resp = client.search(
query={
"term": {
"product": "chocolate"
}
},
sort=[
{
"offer.price": {
"mode": "avg",
"order": "asc",
"nested": {
"path": "offer",
"filter": {
"term": {
"offer.color": "blue"
}
}
}
}
}
],
)
print(resp)
Ruby
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
Go
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)
Js
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);
Console
POST /_search
{
"query" : {
"term" : { "product" : "chocolate" }
},
"sort" : [
{
"offer.price" : {
"mode" : "avg",
"order" : "asc",
"nested": {
"path": "offer",
"filter": {
"term" : { "offer.color" : "blue" }
}
}
}
}
]
}
以下の例では、parent
およびchild
フィールドはnested
型です。nested.path
は各レベルで指定する必要があります。そうしないと、Elasticsearchはソート値をキャプチャするネストされたレベルを知りません。
Php
$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);
Python
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)
Go
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)
Js
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);
Console
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
です。
たとえば:
Python
resp = client.search(
sort=[
{
"price": {
"missing": "_last"
}
}
],
query={
"term": {
"product": "chocolate"
}
},
)
print(resp)
Ruby
response = client.search(
body: {
sort: [
{
price: {
missing: '_last'
}
}
],
query: {
term: {
product: 'chocolate'
}
}
}
)
puts response
Go
res, err := es.Search(
es.Search.WithBody(strings.NewReader(`{
"sort": [
{
"price": {
"missing": "_last"
}
}
],
"query": {
"term": {
"product": "chocolate"
}
}
}`)),
es.Search.WithPretty(),
)
fmt.Println(res, err)
Js
const response = await client.search({
sort: [
{
price: {
missing: "_last",
},
},
],
query: {
term: {
product: "chocolate",
},
},
});
console.log(response);
Console
GET /_search
{
"sort" : [
{ "price" : {"missing" : "_last"} }
],
"query" : {
"term" : { "product" : "chocolate" }
}
}
ネストされた内部オブジェクトがnested.filter
と一致しない場合、欠損値が使用されます。
マッピングされていないフィールドの無視
デフォルトでは、フィールドに関連付けられたマッピングがない場合、検索リクエストは失敗します。unmapped_type
オプションを使用すると、マッピングがないフィールドを無視し、それらでソートしないことができます。このパラメータの値は、どのソート値を出力するかを決定するために使用されます。以下は、その使用例です:
Python
resp = client.search(
sort=[
{
"price": {
"unmapped_type": "long"
}
}
],
query={
"term": {
"product": "chocolate"
}
},
)
print(resp)
Ruby
response = client.search(
body: {
sort: [
{
price: {
unmapped_type: 'long'
}
}
],
query: {
term: {
product: 'chocolate'
}
}
}
)
puts response
Go
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)
Js
const response = await client.search({
sort: [
{
price: {
unmapped_type: "long",
},
},
],
query: {
term: {
product: "chocolate",
},
},
});
console.log(response);
Console
GET /_search
{
"sort" : [
{ "price" : {"unmapped_type" : "long"} }
],
"query" : {
"term" : { "product" : "chocolate" }
}
}
クエリされるインデックスのいずれかにprice
のマッピングがない場合、Elasticsearchはそれをlong
型のマッピングがあるかのように処理し、このインデックス内のすべてのドキュメントがこのフィールドに値を持たないと見なします。
地理的距離によるソート
#### Python
``````python
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)
`
Ruby
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
Go
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)
Js
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);
Console
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
- フィールドに複数の地理ポイントがある場合の処理方法。デフォルトでは、昇順でソートする際には最短距離が考慮され、降順でソートする際には最長距離が考慮されます。サポートされている値は
min
、max
、median
、avg
です。 unit
- ソート値を計算する際に使用する単位。デフォルトは
m
(メートル)です。 ignore_unmapped
- マッピングされていないフィールドを欠損値として扱うかどうかを示します。
true
に設定すると、フィールドソートでunmapped_type
を指定するのと同じです。デフォルトはfalse
(マッピングされていないフィールドが検索を失敗させる)です。
地理的距離によるソートは、設定可能な欠損値をサポートしていません: ドキュメントが距離計算に使用されるフィールドの値を持たない場合、距離は常にInfinity
と見なされます。
座標を提供する際にサポートされている形式は次のとおりです:
プロパティとしての緯度経度
Python
resp = client.search(
sort=[
{
"_geo_distance": {
"pin.location": {
"lat": 40,
"lon": -70
},
"order": "asc",
"unit": "km"
}
}
],
query={
"term": {
"user": "kimchy"
}
},
)
print(resp)
Ruby
response = client.search(
body: {
sort: [
{
_geo_distance: {
'pin.location' => {
lat: 40,
lon: -70
},
order: 'asc',
unit: 'km'
}
}
],
query: {
term: {
user: 'kimchy'
}
}
}
)
puts response
Go
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)
Js
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);
Console
GET /_search
{
"sort" : [
{
"_geo_distance" : {
"pin.location" : {
"lat" : 40,
"lon" : -70
},
"order" : "asc",
"unit" : "km"
}
}
],
"query" : {
"term" : { "user" : "kimchy" }
}
}
WKT文字列としての緯度経度
Python
resp = client.search(
sort=[
{
"_geo_distance": {
"pin.location": "POINT (-70 40)",
"order": "asc",
"unit": "km"
}
}
],
query={
"term": {
"user": "kimchy"
}
},
)
print(resp)
Ruby
response = client.search(
body: {
sort: [
{
_geo_distance: {
'pin.location' => 'POINT (-70 40)',
order: 'asc',
unit: 'km'
}
}
],
query: {
term: {
user: 'kimchy'
}
}
}
)
puts response
Js
const response = await client.search({
sort: [
{
_geo_distance: {
"pin.location": "POINT (-70 40)",
order: "asc",
unit: "km",
},
},
],
query: {
term: {
user: "kimchy",
},
},
});
console.log(response);
Console
GET /_search
{
"sort": [
{
"_geo_distance": {
"pin.location": "POINT (-70 40)",
"order": "asc",
"unit": "km"
}
}
],
"query": {
"term": { "user": "kimchy" }
}
}
Geohash
Python
resp = client.search(
sort=[
{
"_geo_distance": {
"pin.location": "drm3btev3e86",
"order": "asc",
"unit": "km"
}
}
],
query={
"term": {
"user": "kimchy"
}
},
)
print(resp)
Ruby
response = client.search(
body: {
sort: [
{
_geo_distance: {
'pin.location' => 'drm3btev3e86',
order: 'asc',
unit: 'km'
}
}
],
query: {
term: {
user: 'kimchy'
}
}
}
)
puts response
Go
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)
Js
const response = await client.search({
sort: [
{
_geo_distance: {
"pin.location": "drm3btev3e86",
order: "asc",
unit: "km",
},
},
],
query: {
term: {
user: "kimchy",
},
},
});
console.log(response);
Console
GET /_search
{
"sort": [
{
"_geo_distance": {
"pin.location": "drm3btev3e86",
"order": "asc",
"unit": "km"
}
}
],
"query": {
"term": { "user": "kimchy" }
}
}
配列としての緯度経度
[lon, lat]
形式。ここでのlon/latの順序は、GeoJSONに準拠しています。
Python
resp = client.search(
sort=[
{
"_geo_distance": {
"pin.location": [
-70,
40
],
"order": "asc",
"unit": "km"
}
}
],
query={
"term": {
"user": "kimchy"
}
},
)
print(resp)
Ruby
response = client.search(
body: {
sort: [
{
_geo_distance: {
'pin.location' => [
-70,
40
],
order: 'asc',
unit: 'km'
}
}
],
query: {
term: {
user: 'kimchy'
}
}
}
)
puts response
Go
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)
Js
const response = await client.search({
sort: [
{
_geo_distance: {
"pin.location": [-70, 40],
order: "asc",
unit: "km",
},
},
],
query: {
term: {
user: "kimchy",
},
},
});
console.log(response);
Console
GET /_search
{
"sort": [
{
"_geo_distance": {
"pin.location": [ -70, 40 ],
"order": "asc",
"unit": "km"
}
}
],
"query": {
"term": { "user": "kimchy" }
}
}
複数の参照ポイント
複数の地理ポイントを、任意のgeo_point
形式を含む配列として渡すことができます。たとえば
Python
resp = client.search(
sort=[
{
"_geo_distance": {
"pin.location": [
[
-70,
40
],
[
-71,
42
]
],
"order": "asc",
"unit": "km"
}
}
],
query={
"term": {
"user": "kimchy"
}
},
)
print(resp)
Ruby
response = client.search(
body: {
sort: [
{
_geo_distance: {
'pin.location' => [
[
-70,
40
],
[
-71,
42
]
],
order: 'asc',
unit: 'km'
}
}
],
query: {
term: {
user: 'kimchy'
}
}
}
)
puts response
Go
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)
Js
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);
Console
GET /_search
{
"sort": [
{
"_geo_distance": {
"pin.location": [ [ -70, 40 ], [ -71, 42 ] ],
"order": "asc",
"unit": "km"
}
}
],
"query": {
"term": { "user": "kimchy" }
}
}
など。
ドキュメントの最終的な距離は、min
/max
/avg
(mode
を介して定義された)すべてのポイントの距離になります。
スクリプトベースのソート
カスタムスクリプトに基づいてソートを許可します。以下はその例です:
Python
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)
Ruby
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
Go
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)
Js
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);
Console
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に設定すると、スコアは計算され、追跡されます。
Python
resp = client.search(
track_scores=True,
sort=[
{
"post_date": {
"order": "desc"
}
},
{
"name": "desc"
},
{
"age": "desc"
}
],
query={
"term": {
"user": "kimchy"
}
},
)
print(resp)
Ruby
response = client.search(
body: {
track_scores: true,
sort: [
{
post_date: {
order: 'desc'
}
},
{
name: 'desc'
},
{
age: 'desc'
}
],
query: {
term: {
user: 'kimchy'
}
}
}
)
puts response
Go
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)
Js
const response = await client.search({
track_scores: true,
sort: [
{
post_date: {
order: "desc",
},
},
{
name: "desc",
},
{
age: "desc",
},
],
query: {
term: {
user: "kimchy",
},
},
});
console.log(response);
Console
GET /_search
{
"track_scores": true,
"sort" : [
{ "post_date" : {"order" : "desc"} },
{ "name" : "desc" },
{ "age" : "desc" }
],
"query" : {
"term" : { "user" : "kimchy" }
}
}
メモリの考慮事項
ソート時、関連するソートされたフィールド値がメモリにロードされます。これは、シャードごとにそれらを含むのに十分なメモリが必要であることを意味します。文字列ベースの型の場合、ソートされるフィールドは分析/トークン化されていない必要があります。数値型の場合、可能であれば、型を狭い型(short
、integer
、float
など)に明示的に設定することをお勧めします。