【问题标题】:Elastic Search sort preprocessing弹性搜索排序预处理
【发布时间】:2026-02-22 02:15:01
【问题描述】:

我在 ES 中有一个索引,除了其他字段外,它还具有收入金额和收入货币字段。收入以不同的货币存储。在运行时,所有货币都转换为美元并呈现。

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

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

revenue_converted = convertToUSD(revenue)

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

我知道我可以在索引时转换货币,但这需要在每次更新汇率时刷新索引,所以如果可能的话,我想避免它。

【问题讨论】:

标签: elasticsearch lucene


【解决方案1】:

你有两种方法来实现这一点:一种是使用script-based sorting 就像keety提到的那样:

{
    "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
            }
          }
        }
      ]
    }
  }
}

【讨论】:

  • 感谢您的回答。在我的例子中,factor 不是静态的,而是取决于存储在 ES 中的基础 income_currency 字段。如何调用我的函数并将其传递给相应的收入货币字段?即“因素”:conversionRate(doc.revenue_currency.value)
  • 您能否为您的revenue_currency 字段显示一个示例值,好吗?我的印象是您不想将货币汇率存储在您的文档中,以便在汇率变化时不必重新索引它们(这是有道理的)
  • 可以说,我的收入以三种不同的货币(美元、欧元和澳元)存储。在运行时,我将它们全部转换为 usd。我有一个查找表,它告诉我从美元到欧元和澳元的转换率。所以在我可以转换任何收入字段之前,我需要检查相应的收入_货币字段以找出转换率。是的,货币汇率没有被索引,但货币代码被索引。