在Elasticsearch中可以按范围排序吗?

时间:2018-07-17 19:00:52

标签: sorting elasticsearch range

当我执行以下查询时:

{
  "query": {
    "bool": {
      "filter": [
        {
          "match": {
            "my_value": "hi"
          }
        },
        {
          "range": {
            "my_range": {
              "gt": 0,
              "lte": 200
            }
          }
        }
      ]
    }
  },
  "sort": {
    "my_range": {
      "order": "asc",
      "mode": "min"
    }
  }
}

我得到了错误:

"caused_by": {
  "type": "illegal_argument_exception",
  "reason": "Fielddata is not supported on field [my_range] of type [long_range]"
}

如何使range数据类型可排序?这可能吗?

Elasticsearch版本:5.4,但是我想知道是否可以使用任何版本。

更多上下文

并非别名/索引中的所有文档都具有range字段。但是,查询过滤器仅包括具有该字段的文档。

3 个答案:

答案 0 :(得分:1)

使用范围数据类型的字段进行排序不是直接的。仍然可以在某种程度上使用script based sorting以获得预期的结果。

例如为了简化脚本,我假设对于您所有的文档,针对my_range字段编制索引的数据仅包含gtlte的数据,并且您希望基于以下参数的最小值进行排序两个,然后您可以添加以下内容进行排序:

{
  "query": {
    "bool": {
      "filter": [
        {
          "match": {
            "my_value": "hi"
          }
        },
        {
          "range": {
            "my_range": {
              "gt": 0,
              "lte": 200
            }
          }
        }
      ]
    }
  },
  "sort": {
    "_script": {
      "type": "number",
      "script": {
        "lang": "painless",
        "inline": "Math.min(params['_source']['my_range']['gt'], params['_source']['my_range']['lte'])"            
      },
      "order": "asc"
    }
  }
}

您可以根据需要将所有ltgtltegte组合在一起的复杂数据来修改脚本。

更新(用于其他不同用例的脚本):

1。按差异排序
"Math.abs(params['_source']['my_range']['gt'] - params['_source']['my_range']['lte'])"
2。按gt 排序
"params['_source']['my_range']['gt']"
3。按lte 排序
"params['_source']['my_range']['lte']"
4。如果查询返回的文档很少,则没有range字段
"if(params['_source']['my_range'] != null) { <sorting logic> } else { return 0; }"

<sorting logic>替换为所需的排序逻辑(可以是以上3种之一或查询中的一种)

return 0可以替换为return -1或其他根据排序需要的数字

答案 1 :(得分:1)

我认为您正在寻找的是基于difference of the range的排序方式,我不确定仅对任何范围值进行排序是否有意义。

例如如果一个文档的范围是100, 300,而另一文档的范围是200, 600,则您可能希望根据差异进行排序,例如您可能希望较小的范围出现,即300-100 = 200出现在顶部。

如果是这样,我已经利用下面的painless script并实现了script based sorting

根据范围差异进行排序

POST <your_index_name>/_search
{  
   "query":{  
      "match_all":{  

      }
   },
   "sort":{  
      "_script":{  
         "type":"number",
         "script":{  
            "lang":"painless",
            "inline":"params._source.my_range.lte-params._source.my_range.gte"
         },
         "order":"asc"
      }
   }
} 

请注意,在这种情况下,排序将不会基于my_range的任何字段值,而只会基于它们之间的差异。如果您想基于lteltgtegt之类的字段进一步排序,则可以使用以下多个脚本来实现排序:

基于范围+范围字段(my_range.lte)中的差异进行排序

POST <your_index_name>/_search
{  
   "query":{  
      "match_all":{  

      }
   },
   "sort":[  
      {  
         "_script":{  
            "type":"number",
            "script":{  
               "lang":"painless",
               "inline":"params._source.my_range.lte - params._source.my_range.gte"
            },
            "order":"asc"
         }
      },
      {  
         "_script":{  
            "type":"number",
            "script":{  
               "lang":"painless",
               "inline":"params._source.my_range.lte"
            },
            "order":"asc"
         }
      }
   ]
}

因此,在这种情况下,即使对于两个文档,范围是相同的,my_range.lte较小的文档也会首先显示。

根据范围字段排序

但是,如果您只想基于范围值之一进行排序,则可以使用以下查询。

POST <your_index_name>/_search
{  
   "query":{  
      "match_all":{  

      }
   },
   "sort":{  
      "_script":{  
         "type":"number",
         "script":{  
            "lang":"painless",
            "inline":"params._source.my_range.lte"
         },
         "order":"asc"
      }
   }
}

更新后的答案可管理无范围的文档

这是针对方案 根据范围+ Range.lte或Range.lt中存在的差异进行排序

下面的代码是什么,

  • 检查文档是否具有my_range字段
  • 如果没有,默认情况下它将返回Long.MAX_VALUE。这意味着如果您按asc排序,则应返回此文档 持续。
  • 此外,它将检查文档是否具有ltelt,并将该值用作high。请注意,high的默认值为Long.MAX_VALUE
  • 类似地,它将检查文档是否具有gtegt并将该值用作lowlow的默认值为0
  • 立即计算将应用排序的high - low值。

更新的查询

POST <your_index_name>/_search
{  
   "size":100,
   "query":{  
      "match_all":{  

      }
   },
   "sort":[  
      {  
         "_script":{  
            "type":"number",
            "script":{  
               "lang":"painless",
               "inline":""" 
              if(params._source.my_range==null){ 
                return Long.MAX_VALUE; 
              } else { 

                long high = Long.MAX_VALUE; 
                long low = 0L; 

                if(params._source.my_range.lte!=null){ 
                  high = params._source.my_range.lte; 
                } else if(params._source.my_range.lt!=null){ 
                  high = params._source.my_range.lt; 
                } 

                if(params._source.my_range.gte!=null){ 
                  low = params._source.my_range.gte; 
                } else if (params._source.my_range.gt==null){ 
                  low = params._source.my_range.gt; 
                } 

              return high - low; 

              } 
                """
            },
            "order":"asc"
         }
      },
      {  
         "_script":{  
            "type":"number",
            "script":{  
               "lang":"painless",
               "inline":""" 
                if(params._source.my_range==null){ 
                  return Long.MAX_VALUE; 
                } 

                long high = Long.MAX_VALUE; 
                if(params._source.my_range.lte!=null){ 
                  high = params._source.my_range.lte; 
                } else if(params._source.my_range.lt!=null){ 
                  high = params._source.my_range.lt; 
                } 
                  return high;"""
            },
            "order":"asc"
         }
      }
   ]
}

这应该与ES 5.4一起使用。希望对您有帮助!

答案 2 :(得分:0)

可以使用正则表达式间隔过滤器轻松解决此问题:

  

时间间隔使用时间间隔选项可以使用数字范围,   用尖括号“ <>”括起来。对于字符串:“ foo80”:

foo<1-100>     # match
foo<01-100>    # match
foo<001-100>   # no match
Enabled with the INTERVAL or ALL flags.

Elactic docs

{
  "query": {
    "bool": {
      "filter": [
        {
          "match": {
            "my_value": "hi"
          }
        },
        {
          "regexp": {
            "my_range": {
              "value": "<0-200>"
            }
          }
        }
      ]
    }
  },
  "sort": {
    "my_range": {
      "order": "asc",
      "mode": "min"
    }
  }
}