弹性搜索排序预处理

时间:2015-08-17 19:59:15

标签: elasticsearch lucene

我在ES中有一个索引,除了其他字段外,还有revenue_amount和revenue_currency字段。收入以不同货币存储。在运行时,所有货币都转换为美元并进行渲染。

现在,我想支持对revenue_amount字段进行排序。问题是ES在转换为美元之前按收入排序结果,因此返回顶部的收入可能不是转换为美元后的最高收入。

我想知道,如果可能在排序之前,ES调用一个用户定义的函数来更改字段值,然后再应用sort?像这样:

  

revenue_converted = convertToUSD(收入)

因此,排序将应用于revenue_converted而不是收入。

我知道我可以在索引时转换货币,但每次更新费率时都需要刷新索引,所以我希望尽可能避免使用。

1 个答案:

答案 0 :(得分:1)

您有两种方法可以实现这一目标:一种方法是使用script-based sorting作为保密提及:

{
    "query" : {
        ....                                    <--- your query goes here
    },
    "sort" : {
        "_script" : {
            "script" : "doc.revenue_amount.value * usd_conversion_rate",
            "type" : "number",
            "params" : {
                "usd_conversion_rate" : 0.4273  <--- the conversion rate to USD
            },
            "order" : "desc"
        }
    }
}

usd_conversion_rate因素是美元的转换率。因此,例如,如果1美元的价值为另一种货币的2.34单位,则usd_conversion_rate因子将为1 / 2.34(或0.4273)。当与revenue_amount相乘时,它会以美元参考货币为您提供金额。

但基于脚本的排序效果不是很好,建议使用function_score,因此可以按分数对结果进行排序。这导致我们实现了你需要的第二种方式,就像这样。一种方法是使用script_score函数,但这又涉及脚本。

{
  "query": {
    "function_score": {
      "query": {},
      "functions": [
        {
          "script_score": {
            "script": "doc.revenue_amount.value * usd_conversion_rate",
            "boost_mode": "replace",
            "params": {
              "usd_conversion_rate": 0.4273
            }
          }
        }
      ]
    }
  }
}

由于我们的上述脚本非常简单(即将某个字段乘以某个因子),最简单的方法是使用field_value_factor,它是这样的:

{
  "query": {
    "function_score": {
      "query": {
        ...                              <--- your query goes here
      },
      "functions": [
        {
          "field_value_factor": {
            "field": "revenue_amount",
            "boost_mode": "replace",
            "factor": 0.4273             <--- insert the conversion rate here
          }
        }
      ]
    }
  }
}

<强>更新

根据您的最新评论,似乎适合您的选项毕竟是使用script_score。这里的想法是输入查询表中可用的所有货币汇率作为script_score脚本的参数,然后根据revenue_currency字段的值使用正确的汇率。

{
  "query": {
    "function_score": {
      "query": {},
      "functions": [
        {
          "script_score": {
            "script": "doc.revenue_amount.value * (doc.revenue_currency.value == 'EUR' ? EUR : (doc.revenue_currency.value == 'AUD' ? AUD : 1))",
            "boost_mode": "replace",
            "params": {
              "EUR": 0.4945,
              "AUD": 0.5623
            }
          }
        }
      ]
    }
  }
}