You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Elixir/Ecto中实现地址字符串精准匹配的方案问询

地址字符串匹配:解决Jaro距离误判与PostgreSQL SIMILAR TO匹配失败问题

问题描述

  • 使用String.jaro_distance对比完全不相似的地址字符串时,返回了0.49的相似度,不符合预期:
    iex(1)> String.jaro_distance("4420 West Main Street", "EUTECTIC CORPORATION QA testing1")
    0.49107142857142855
    
  • 使用PostgreSQL的SIMILAR TO进行匹配时,大部分内容一致的地址却匹配失败:
    搜索字符串:29 SANTA CRUZ COURT PITTSBURG CA 662354553
    实际地址字符串:29 SANTA CRUZ COURT PITTSBURG CA 94565
    对应的Elixir代码实现如下:
    def find_match(seeker_company_id, string, type) do
      search = "%" <> string <> "%"
      base_query =
        from op in OpenCorporates,
          where: op.seeker_company_id == ^seeker_company_id
    
      base_query
      |> type_query(type, search)
      |> Repo.aggregate(:count)
    end
    
    defp type_query(query, :name, value) do
      from op in query,
        where: fragment("? SIMILAR TO ?", op.name, ^value)
    end
    
    defp type_query(query, :address, value) do
      from op in query,
        where: fragment("? SIMILAR TO ?", op.registered_address, ^value)
    end
    

需要找到能计算匹配百分比的解决方案,比如将上述部分匹配的地址识别为80%左右的匹配度。

解决方案

1. 替换Jaro距离为更适合的相似度算法

Jaro距离对字符分布有轻微重合的字符串容易误判,推荐使用以下两种算法:

  • Jaro-Winkler距离:在Jaro基础上增加前缀权重,对开头格式相似的地址更友好,Elixir中直接用String.jaro_winkler_distance/2:
    String.jaro_winkler_distance("4420 West Main Street", "EUTECTIC CORPORATION QA testing1")
    # 返回的相似度会远低于0.49,更符合实际差异
    
  • Levenshtein距离转匹配百分比:先计算两个字符串的编辑距离,再转换为匹配百分比,公式为匹配百分比 = 1 - (编辑距离 / 两个字符串的最大长度)。实现代码:
    def similarity_percent(s1, s2) do
      len1 = String.length(s1)
      len2 = String.length(s2)
      max_len = max(len1, len2)
      distance = String.levenshtein_distance(s1, s2)
      (1 - distance / max_len) * 100 |> Float.round(2)
    end
    
    # 测试部分匹配的地址
    similarity_percent("29 SANTA CRUZ COURT PITTSBURG CA 662354553", "29 SANTA CRUZ COURT PITTSBURG CA 94565")
    # 会返回约85%的匹配度,符合预期
    

2. 改用PostgreSQL pg_trgm扩展实现相似度匹配

SIMILAR TO是严格模式匹配,无法计算模糊相似度,推荐使用PostgreSQL的pg_trgm扩展,它通过 trigram(三元字符组)计算字符串相似度,直接返回匹配百分比:

步骤1:启用pg_trgm扩展

CREATE EXTENSION IF NOT EXISTS pg_trgm;

步骤2:修改Elixir查询代码

使用similarity函数计算相似度,设置阈值过滤结果:

def find_match(seeker_company_id, string, type) do
  base_query =
    from op in OpenCorporates,
      where: op.seeker_company_id == ^seeker_company_id

  base_query
  |> type_query(type, string)
  |> Repo.all()
end

defp type_query(query, :name, value) do
  from op in query,
    select: {op.name, fragment("similarity(?, ?)", op.name, ^value)},
    where: fragment("similarity(?, ?) > 0.7", op.name, ^value) # 0.7为阈值,可根据需求调整
end

defp type_query(query, :address, value) do
  from op in query,
    select: {op.registered_address, fragment("similarity(?, ?)", op.registered_address, ^value)},
    where: fragment("similarity(?, ?) > 0.7", op.registered_address, ^value)
end

这样就能返回所有相似度超过70%的结果,同时获取具体的匹配百分比。

3. 地址预处理优化

无论用哪种匹配方法,先对地址字符串做预处理能大幅提升准确率:

  • 统一大小写:全部转为大写或小写
  • 去除多余空格:将多个连续空格替换为单个空格
  • 标准化地址组件:比如将"ST"转为"STREET"、"CA"转为"CALIFORNIA"(可借助Elixir的address_us等库)
  • 去除无关字符:比如多余的数字、符号等

示例预处理函数:

def normalize_address(address) do
  address
  |> String.upcase()
  |> String.replace(~r/\s+/, " ")
  |> String.trim()
end

内容的提问来源于stack exchange,提问作者Junaid Farooq

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 11:30:14