Elasticsearch:在分析过程中对数字进行标准化

作者:来自 Elastic spinscale

分析链中的数字标准化

在全文搜索中,一个常见问题是如何处理数字。最基本的方法是将它们完全提取出来,并在范围内作为真实数字进行查询,但在很多情况下,这需要大量分析,而且数字往往只是全文搜索的一部分,比如 iphone 17 或 bed 1.4 m。

问题是,当用户输入搜索时,他们对数字的理解可能与你不同。

1.4 m 和 1,4 m 是相同的吗?事实是,美国和欧洲在大数和分数的分隔符上使用不同的字符。除此之外,用户在搜索引擎中输入数字时,点和逗号常常可以互换使用,尤其是数字较小时。

  • 007 和 7 是相同的吗?取决于你的使用场景。
  • 1.4 m 和 1.40 m 是相同的吗?这取决......你明白我的意思。

那么我们能做些什么来稍微标准化数字呢?

为了这个示例,让我们使用 keep_types token filter,仅保留数字,丢弃分析链中的其他内容:

复制代码
POST _analyze
{
  "text": "makita führungsschiene 1.4 m, 1,4 m 1,40 1.40",
  "tokenizer": "standard",
  "filter": [
    {
      "type": "keep_types",
      "types": [
        "<NUM>"
      ]
    }
  ]
}

这只会返回看起来像数字的 token,无论它们是否包含点或逗号,但会排除其他内容,例如普通单词,比如 makita。

接下来,我们先统一所有带点或逗号的数字。

复制代码
POST _analyze
{
  "text": "makita führungsschiene 1.4 m, 1,4 m 1,40 1.40",
  "tokenizer": "standard",
  "filter": [
    {
      "type": "keep_types",
      "types": [
        "<NUM>"
      ]
    },
    {
      "type": "pattern_replace",
      "pattern": "(\\d+)\\,(\\d+)",
      "replacement" : "$1.$2"
    }
  ]
}

这只返回 1.4 或 1.40 ------ 很好!所以无论索引了什么,或者用户搜索什么,现在我们总是假设数字使用点,借助 pattern_replace token filter 实现。

如果不关心位置,可以在最后使用 unique token filter ------ 当然也可以省略 norms 以减少索引大小。

你也可以去掉点,只保留数字本身,但这可能导致搜索 1.7 时返回 iphone 17 ------ 同样,这取决于是否希望这样。

接下来,我们去掉前导零:

复制代码
POST _analyze
{
  "text": "test 007 7 700 000 0",
  "tokenizer": "standard",
  "filter": [
    {
      "type": "keep_types",
      "types": [
        "<NUM>"
      ]
    },
    {
      "type": "pattern_replace",
      "pattern": "^0+(\\d+)",
      "replacement" : "$1"
    }
  ]
}

现在,007 或 000 会被简化为单个数字字符。虽然这可能有用,但请注意,当用户搜索 007 作为零件编号时,可能会返回包含 7 的所有结果,从而增加歧义。

接下来,真正有趣的部分来了:去掉尾随零,但不要弄得太复杂。像往常一样,如果你手头只有正则表达式,你可能会想出一个复杂的正则,但也许预处理可能已经是个好主意。

复制代码
POST _analyze
{
  "text": "0.100 0.1000 0.101 100 100.0 100.00 100.001",
  "tokenizer": "standard",
  "filter": [
    {
      "type": "keep_types",
      "types": [
        "<NUM>"
      ]
    },
    {
      "type": "pattern_replace",
      "pattern": "^(\\d+)\\.([0-9])(0+)$",
      "replacement" : "$1.$2"
    }
  ]
}

这会返回(至少如果你在请求中添加 filter_path=**.token):

复制代码
{
  "tokens": [
    {
      "token": "0.1"
    },
    {
      "token": "0.1"
    },
    {
      "token": "0.101"
    },
    {
      "token": "100"
    },
    {
      "token": "100.0"
    },
    {
      "token": "100.0"
    },
    {
      "token": "100.001"
    }
  ]
}

你已经可以看到这里还有一些可以改进的地方。100 和 100.0 之间真的有区别吗?也许在这种情况下可以完全去掉尾随的 .0。我相信你会为此想出一个很棒的正则表达式。

让我们把所有步骤整合起来:

复制代码
POST _analyze
{
  "text": "makita führungsschiene 1.4 m, 1,4 m 1,40 1.40 1.0 1.00 0.100 0.1000 0.101 0.1010 100 100.0 100.00 100.001 0.100 007 700",
  "tokenizer": "standard",
  "filter": [
    {
      "type": "keep_types",
      "types": [
        "<NUM>"
      ]
    },
    {
      "type": "pattern_replace",
      "pattern": "(\\d+)\\,(\\d+)",
      "replacement" : "$1.$2"
    },
    {
      "type": "pattern_replace",
      "pattern": "^0+(\\d+)",
      "replacement" : "$1"
    },
    {
      "type": "pattern_replace",
      "pattern": "^(\\d+)\\.([0-9])(0+)$",
      "replacement" : "$1.$2"
    }
  ]
}

在真实的分析链中,你可能会去掉 keep_types filter,并且在适用时尝试将正则表达式组合以提高速度,但这大概是一个不错的起点。

仔细看上面的输出,你会注意到还有一个小问题:0.1010 没有被简化为 0.101。因此,你可能需要对正则表达式做进一步修正 ------ 记住,如果增加一个 token filter 有助于可读性,也是可以的 😊

还有一个实现提示。如果你想确保 pattern replace filter 只针对数字运行,可以使用 condition token filter

原文:https://discuss.elastic.co/t/dec-2nd-2025-en-normalizing-numbers-during-analysis/383512

相关推荐
一只小白0009 分钟前
数据库对象实例化流程模板 + 常见错误
数据库
一江寒逸21 分钟前
零基础从入门到精通MySQL(下篇):精通篇——吃透索引底层、锁机制与性能优化,成为MySQL实战高手
数据库·mysql·性能优化
紧固视界22 分钟前
2026上海紧固件专业展,紧固件设备集中展示平台
大数据·人工智能·上海紧固件展·紧固件展·上海紧固件专业展
DevOpenClub26 分钟前
全国三甲医院主体信息 API 接口
java·大数据·数据库
一勺菠萝丶35 分钟前
管理后台使用手册在线预览与首次登录引导弹窗实现
java·前端·数据库
无忧智库39 分钟前
某大型银行“十五五”金融大模型风控与智能投顾平台建设方案深度解读(WORD)
数据库·金融
爱码小白40 分钟前
数据库多表命名的通用规范
数据库·python·mysql
大喵桑丶1 小时前
ZABBIX7二次开发AI监控数据调取杂记
大数据·人工智能·python
huohuopro1 小时前
Hbase伪分布式远程访问配置
数据库·分布式·hbase
DX_水位流量监测1 小时前
德希科技在线多参数七参传感器使用说明
大数据·水质监测·水质传感器·水质厂家·供水水质监测·在线多参数水质分析仪·水质七参