ES使用Ngram分词器实现wildcard高性能替代方案

1、wildcard 检索

wildcard 检索可定义为:支持通配符的模糊检索,类似 Mysql 中的 like 模糊匹配模式,如下使用非分词器(ik)方式实现模糊匹配。

  • 创建常规支持wildcard索引

PUT idx_recommend_words
{
  "settings": {
    "index": {
      "number_of_shards": "5",
      "number_of_replicas": "3",
      "refresh_interval": "5s"
    }
  },
  "mappings": {
    "rec_words": {
      "_all": {
        "enabled": false
      },
      "dynamic_templates": [
        {
          "attribute_values": {
            "match_mapping_type": "*",
            "mapping": {
              "type": "keyword"
            }
          }
        }
      ],
      "properties": {
        "recId": {
          "type": "long"
        },
        "recWord": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        },
        "recType": {
          "type": "keyword"
        },
        "brandName": {
          "type": "keyword"
        },
        "catName": {
          "type": "keyword"
        },
        "enabled": {
          "type": "integer"
        },
        "clicks": {
          "type": "long"
        },
        "createdBy": {
          "type": "keyword",
          "index": false
        },
        "createdAt": {
          "type": "date",
          "index": false,
          "format": "yyyy-MM-dd HH:mm:ss||epoch_millis"
        },
        "updatedBy": {
          "type": "keyword",
          "index": false
        },
        "updatedAt": {
          "type": "date",
          "index": false,
          "format": "yyyy-MM-dd HH:mm:ss||epoch_millis"
        }
      }
    }
  }
}
  • wildcard查询
GET rec_words/_search
{
  "query": {
    "bool": {
      "should": [{
        "wildcard": {
          "recWord.keyword": {
            "value": "*北京*"
          }
        }
      }]
    }
  }
}

2、wildcard性能风险

官方文档说明如下:在这里插入图片描述
翻译过来中文含义是:避免以*或?开头的模式,这会增加查找匹配项所需的迭代次数并降低搜索性能。

3、替代方案

3.1 Ngram定义

Ngram是一种基于统计语言模型的算法。

Ngram的基本思想:是将文本里面的内容按照字节进行大小为N的滑动窗口操作,形成了长度是N的字节片段序列。每一个字节片段称为gram,对所有gram的出现频度进行统计,并且按照事先设定好的阈值进行过滤,形成关键gram列表,也就是这个文本的向量特征空间,列表中的每一种gram就是一个特征向量维度。

该模型基于这样一种假设,第N个词的出现只与前面N-1个词相关,而与其它任何词都不相关,整句的概率就是各个词出现概率的乘积。

这些概率可以通过直接从语料中统计N个词同时出现的次数得到。常用的是二元的Bi-Gram(二元语法)和三元的Tri-Gram(三元语法)。

3.2 Ngram举例

例如:中文句子:“你今天上班了吗”,它的Bi-Gram(二元语法)分词结果为:

你今
今天
天上
上班
班了
了吗

3.3 Ngram 应用场景

场景1:文本压缩、检查拼写错误、加速字符串查找、文献语种识别;
场景2:自然语言处理自动化领域得到新的应用,如自动分类、自动索引、超链的自动生成、文献检索、无分隔符语言文本的切分等;
场景3:自然语言的自动分类功能,对应到Elasticsearch检索,应用场景就更加明确:无分隔符语言文本的切分分词,提高检索效率(相比:wildcard 查询和正则查询)。

  • 实践:创建N-gram分词器索引
PUT idx_recommend_words
{
  "settings": {
    "index": {
      "number_of_shards": "5",
      "number_of_replicas": "3",
      "refresh_interval": "5s"
    },
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "my_tokenizer"
        }
      },
      "tokenizer": {
        "my_tokenizer": {
          "type": "ngram",
          "min_gram": 2,
          "max_gram": 3,
          "token_chars": [
            "letter",
            "digit"
          ]
        }
      }
    }
  },
  "mappings": {
    "rec_words": {
      "_all": {
        "enabled": false
      },
      "dynamic_templates": [
        {
          "attribute_values": {
            "match_mapping_type": "*",
            "mapping": {
              "type": "keyword"
            }
          }
        }
      ],
      "properties": {
        "recId": {
          "type": "long"
        },
        "recWord": {
          "type": "text",
          "analyzer": "my_analyzer",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        },
        "recType": {
          "type": "keyword"
        },
        "brandName": {
          "type": "keyword"
        },
        "catName": {
          "type": "keyword"
        },
        "enabled": {
          "type": "integer"
        },
        "clicks": {
          "type": "long"
        },
        "createdBy": {
          "type": "keyword",
          "index": false
        },
        "createdAt": {
          "type": "date",
          "index": false,
          "format": "yyyy-MM-dd HH:mm:ss||epoch_millis"
        },
        "updatedBy": {
          "type": "keyword",
          "index": false
        },
        "updatedAt": {
          "type": "date",
          "index": false,
          "format": "yyyy-MM-dd HH:mm:ss||epoch_millis"
        }
      }
    }
  }
}
  • 替代wildcard使用match_phrase查询
POST rec_words/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match_phrase": {
            "acWord": {
              "query": "北京"
            }
          }
        }
      ]
    }
  }
}
  • 注意三个核心参数
    min_gram:最小字符长度(切分),默认为1;
    max_gram:最大字符长度(切分),默认为2;
    token_chars:生成的分词结果中包含的字符类型,默认是全部类型;
    官方参数设置说明:https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-ngram-tokenizer.html

4、结论

  • Ngram分词的本质:用空间换时间,其能匹配的前提是写入的时候已经按照:min_gram、max_gram切词;
  • 数据量非常少且不要求子串高亮,可以考虑keyword;
  • 数据量大且要求子串高亮,推荐使用:Ngram分词结合match或者match_phrase检索实现;
  • 数据量大,切记不要使用wildcard前缀匹配
    原因:带有通配符的pattern构造出来的DFA(Deterministic Finite Automaton)可能会很复杂,开销很大!甚至可能导致线上环境宕机;
    特别注意:wildcard query应杜绝使用通配符打头,实在不得已要这么做,就一定需要限制用户输入的字符串长度。
Elasticsearch实现类似 SQL 的 `LIKE '%keyword%'` 模糊查询,并且不进行分词处理,通常适用于关键字匹配场景。为了实现这一需求,可以通过字段映射为 `.keyword` 类型来确保查询时不分词,同时结合通配符或正则表达式等技术来模拟模糊匹配行为。 ### 使用通配符查询(Wildcard Query) 通配符查询允许使用 `*` 和 `?` 进行模式匹配,其中 `*` 匹配任意数量的字符,`?` 匹配单个字符。这种方式可以实现类似于 `LIKE '%keyword%'` 的效果,但需要注意的是,通配符查询性能较低,特别是在大数据集上[^1]。 示例查询: ```json GET /my_index/_doc/_search { "query": { "wildcard": { "field1.keyword": { "value": "*keyword*" } } } } ``` ### 使用 `query_string` 查询 `query_string` 查询支持通配符语法,可以在指定字段中进行模糊匹配。通过将字段设置为 `.keyword` 类型,可以确保查询时不进行分词处理[^1]。 示例查询: ```json GET /my_index/_doc/_search { "query": { "query_string": { "query": "*keyword*", "fields": ["field1.keyword", "field2.keyword"] } } } ``` ### 使用 ngram 分词器预处理数据 如果需要频繁执行类似 `LIKE '%keyword%'` 的查询,推荐使用 **ngram 分词器** 在索引阶段对字段进行预处理。这样可以在查询时使用标准的 term 查询,从而提高性能和效率。 字段映射示例: ```json "settings": { "analysis": { "analyzer": { "my_ngram_analyzer": { "type": "custom", "tokenizer": "my_ngram_tokenizer" } }, "tokenizer": { "my_ngram_tokenizer": { "type": "ngram", "min_gram": 3, "max_gram": 10 } } } }, "mappings": { "properties": { "field1": { "type": "text", "analyzer": "my_ngram_analyzer", "fields": { "keyword": { "type": "keyword" } } } } } ``` ### 使用 `term` 查询结合 `.keyword` 字段 对于不需要通配符的完全匹配场景,可以使用 `term` 查询结合 `.keyword` 字段,以实现精确匹配。这种方式性能更高,适合用于 `LIKE 'keyword%'` 类似的前缀匹配场景[^2]。 示例查询: ```json GET /my_index/_doc/_search { "query": { "term": { "field1.keyword": "keyword" } } } ``` ### 总结与建议 - 如果需要实现 `LIKE '%keyword%'` 类似的效果,推荐使用 `wildcard` 或 `query_string` 查询结合 `.keyword` 字段。 - 对于大规模数据量和高性能要求的场景,建议使用 **ngram 分词器** 预处理数据,以便使用 `term` 查询提升性能。 - 避免在大数据集上频繁使用通配符查询,因为其性能较差。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

广漂一枚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值