【Elastic Engineering】Elasticsearch: Ngrams, edge ngrams, and shingles

简介: Elasticsearch: Ngrams, edge ngrams, and shingles

作者:刘晓国


Ngrams 和 edge ngrams 是在 Elasticsearch 中标记文本的两种更独特的方式。 Ngrams 是一种将一个标记分成一个单词的每个部分的多个子字符的方法。 ngram 和 edge ngram 过滤器都允许你指定 min_gram 以及 max_gram 设置。 这些设置控制单词被分割成的标记的大小。 这可能令人困惑,让我们看一个例子。 假设你想用 ngram 分析仪分析 “spaghetti” 这个词,让我们从最简单的情况开始,1-gams(也称为 unigrams)。


在实际的搜索例子中,比如谷歌搜索:

image.png

每当我们打入前面的几个字母时,就会出现相应的很多的候选名单。这个就是 autocomplete 功能。在 Elasticsearch 中,我们可以通过 Edge ngram 来实现这个目的。


1-grams


“spaghetti” 的 1-grams 是 s,p,a,g,h,e,t,t,i。 根据 ngram 的大小将字符串拆分为较小的 token。 在这种情况下,每个 token 都是一个字符,因为我们谈论的是 unigrams。


Bigrams


如果你要将字符串拆分为双字母组(这意味着大小为2),你将获得以下较小的 token:sp,pa,ag,gh,he,et,tt,ti。


Trigrams


再说一次,如果你要使用三个大小,你将得到 token 为 spa,pag,agh,ghe,het,ett,tti。


设置 min_gram 和 max_gram


使用此分析器时,需要设置两种不同的大小:一种指定要生成的最小 ngrams(min_gram 设置),另一种指定要生成的最大 ngrams。 使用前面的示例,如果你指定 min_gram 为2且 max_gram 为3,则你将获得前两个示例中的组合标记:


sp, spa, pa, pag, ag, agh, gh, ghe, he, het, et, ett, tt, tti, ti

如果你要将 min_gram 设置为1并将 max_gram 设置为3,那么你将得到更多的标记,从 s,sp,spa,p,pa,pag,a,....开始。


以这种方式分析文本具有一个有趣的优点。 当你查询文本时,你的查询将以相同的方式被分割成文本,所以说你正在寻找拼写错误的单词“ spaghety”。搜索这个的一种方法是做一个 fuzzy query,它允许你 指定单词的编辑距离以检查匹配。 但是你可以通过使用 ngrams 来获得类似的行为。 让我们将原始单词(“spaghetti”)生成的 bigrams 与拼写错误的单词(“spaghety”)进行比较:


“spaghetti” 的 bigrams:sp,pa,ag,gh,he,et,tt,ti

“spaghety” 的 bigrams:sp,pa,ag,gh,he,et,ty


你可以看到六个 token 重叠,因此当查询包含 “spaghety” 时,其中带有 “spaghetti” 的单词仍然匹配。请记住,这意味着你可能不打算使用的原始 “spaghetti” 单词更多的单词 ,所以请务必测试你的查询相关性!


ngrams 做的另一个有用的事情是允许你在事先不了解语言时或者当你使用与其他欧洲语言不同的方式组合单词的语言时分析文本。 这还有一个优点,即能够使用单个分析器处理多种语言,而不必指定。


Edge ngrams


常规 ngram 拆分的变体称为 edge ngrams,仅从前沿构建 ngram。 在 “spaghetti” 示例中,如果将 min_gram 设置为2并将 max_gram 设置为6,则会获得以下标记:

sp, spa, spag, spagh, spaghe

你可以看到每个标记都是从边缘构建的。 这有助于搜索共享相同前缀的单词而无需实际执行前缀查询。 如果你需要从一个单词的后面构建 ngrams,你可以使用 side 属性从后面而不是默认前面获取边缘。


Ngram 设置


当你不知道语言是什么时,Ngrams 是分析文本的好方法,因为它们可以分析单词之间没有空格的语言。 使用 min 和 max grams 配置 edge ngram analyzer 的示例如下所示:

PUT my_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "my_tokenizer"
        }
      },
      "tokenizer": {
        "my_tokenizer": {
          "type": "edge_ngram",
          "min_gram": 2,
          "max_gram": 10,
          "token_chars": [
            "letter",
            "digit"
          ]
        }
      }
    }
  }
}

我们可以用刚才创建的 my_tokenizer 来分析我们的字符串:

POST my_index/_analyze
{
  "analyzer": "my_analyzer",
  "text": "2 Quick Foxes."
}

显示的结果是:

{
  "tokens" : [
    {
      "token" : "Qu",
      "start_offset" : 2,
      "end_offset" : 4,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "Qui",
      "start_offset" : 2,
      "end_offset" : 5,
      "type" : "word",
      "position" : 1
    },
    {
      "token" : "Quic",
      "start_offset" : 2,
      "end_offset" : 6,
      "type" : "word",
      "position" : 2
    },
    {
      "token" : "Quick",
      "start_offset" : 2,
      "end_offset" : 7,
      "type" : "word",
      "position" : 3
    },
    {
      "token" : "Fo",
      "start_offset" : 8,
      "end_offset" : 10,
      "type" : "word",
      "position" : 4
    },
    {
      "token" : "Fox",
      "start_offset" : 8,
      "end_offset" : 11,
      "type" : "word",
      "position" : 5
    },
    {
      "token" : "Foxe",
      "start_offset" : 8,
      "end_offset" : 12,
      "type" : "word",
      "position" : 6
    },
    {
      "token" : "Foxes",
      "start_offset" : 8,
      "end_offset" : 13,
      "type" : "word",
      "position" : 7
    }
  ]
}

因为我们定义的 min_gram 是2,所以生成的 token 的长度是从2开始的。


通常我们建议在索引时和搜索时使用相同的分析器。 在 edge_ngram tokenizer 的情况下,建议是不同的。 仅在索引时使用 edge_ngram 标记生成器才有意义,以确保部分单词可用于索引中的匹配。 在搜索时,只需搜索用户输入的术语,例如:Quick Fo。


下面是如何为搜索类型设置字段的示例:

PUT my_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "autocomplete": {
          "tokenizer": "autocomplete",
          "filter": [
            "lowercase"
          ]
        },
        "autocomplete_search": {
          "tokenizer": "lowercase"
        }
      },
      "tokenizer": {
        "autocomplete": {
          "type": "edge_ngram",
          "min_gram": 2,
          "max_gram": 10,
          "token_chars": [
            "letter"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "autocomplete",
        "search_analyzer": "autocomplete_search"
      }
    }
  }
}

在我们的例子中,我们索引时和搜索时时用了两个不同的 analyzer:autocomplete 及 autocomplete_search。

PUT my_index/_doc/1
{
  "title": "Quick Foxes" 
}
POST my_index/_refresh

上面我们加入一个文档。下面我们来进行搜索:

GET my_index/_search
{
  "query": {
    "match": {
      "title": {
        "query": "Quick Fo", 
        "operator": "and"
      }
    }
  }
}

显示结果:

{
  "took" : 3,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.5753642,
    "hits" : [
      {
        "_index" : "my_index",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.5753642,
        "_source" : {
          "title" : "Quick Foxes"
        }
      }
    ]
  }
}

在这里 autocomplete analyzer 可以把字符串 “Quick Foxes” 分解为[qu, qui, quic, quick, fo, fox, foxe, foxes]。而自 autocomplete_search analyzer 搜索条目[quick,fo],两者都出现在索引中。


当然我们也可以做如下的搜索:

GET my_index/_search
{
  "query": {
    "match": {
      "title": {
        "query": "Fo"
      }
    }
  }
}

显示的和上面一样的结果。


Shingles


与 ngrams 和 edge ngrams一样,有一个称为 shingle 的过滤器(不,不是疾病的那个shingle!)。 Shingle token 过滤器基本上是 token 级别的 ngrams 而不是字符级别。



想想我们最喜欢的单词 “spaghetti”。使用最小和最大设置为1和3的 ngrams,Elasticsearch 将生成标记s,sp,spa,p,pa,pag,a,ag等。 一个 shingle 过滤器在 token 级别执行此操作,因此如果你有文本 “foo bar baz” 并再次使用 in_shingle_size 为2且 max_shingle_size 为3,则你将生成以下 token:

foo, foo bar, foo bar baz, bar, bar baz, baz

为什么仍然包含单 token 输出? 这是因为默认情况下,shingle 过滤器包含原始 token,因此原始标记生成令牌 foo,bar 和 baz,然后将其传递给 shingle token 过滤器,生成标记foo bar,foo bar baz 和 bar baz。 所有这些 token 组合在一起形成最终 token 流。 你可以通过将 output_unigrams 选项设置为 false 来禁用此行为,也即不需要最原始的 token:foo, bar 及 baz


下一个清单显示了 shingle token 过滤器的示例; 请注意,min_shingle_size 选项必须大于或等于2。

PUT my_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "shingle": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "shingle-filter"
          ]
        }
      },
      "filter": {
        "shingle-filter": {
          "type": "shingle",
          "min_shingle_size": 2,
          "max_shingle_size": 3,
          "output_unigrams": false
        }
      }
    }
  }
}

在这里,我们定义了一个叫做 shingle-filter 的过滤器。最小的 shangle 大小是2,最大的 shingle 大小是3。同时我们设置 output_unigrams 为 false,这样最初的那些 token 将不被包含在最终的结果之中。


下面我们来做一个例子,看看显示的结果:

GET /my_index/_analyze
{
  "text": "foo bar baz",
  "analyzer": "shingle"
}

显示的结果为:

{
  "tokens" : [
    {
      "token" : "foo bar",
      "start_offset" : 0,
      "end_offset" : 7,
      "type" : "shingle",
      "position" : 0
    },
    {
      "token" : "foo bar baz",
      "start_offset" : 0,
      "end_offset" : 11,
      "type" : "shingle",
      "position" : 0,
      "positionLength" : 2
    },
    {
      "token" : "bar baz",
      "start_offset" : 4,
      "end_offset" : 11,
      "type" : "shingle",
      "position" : 1
    }
  ]
}


参考:


【1】 https://www.elastic.co/guide/en/elasticsearch/reference/7.3/analysis-edgengram-tokenizer.html


相关实践学习
以电商场景为例搭建AI语义搜索应用
本实验旨在通过阿里云Elasticsearch结合阿里云搜索开发工作台AI模型服务,构建一个高效、精准的语义搜索系统,模拟电商场景,深入理解AI搜索技术原理并掌握其实现过程。
ElasticSearch 最新快速入门教程
本课程由千锋教育提供。全文搜索的需求非常大。而开源的解决办法Elasricsearch(Elastic)就是一个非常好的工具。目前是全文搜索引擎的首选。本系列教程由浅入深讲解了在CentOS7系统下如何搭建ElasticSearch,如何使用Kibana实现各种方式的搜索并详细分析了搜索的原理,最后讲解了在Java应用中如何集成ElasticSearch并实现搜索。  
相关文章
|
Java 数据库连接 Spring
如何在Spring Boot中使用`@Retryable`注解来实现重试机制?
如何在Spring Boot中使用`@Retryable`注解来实现重试机制?
2163 0
如何在Spring Boot中使用`@Retryable`注解来实现重试机制?
|
Dubbo Java 应用服务中间件
Dubbo——对版本号version的理解与使用
Dubbo——对版本号version的理解与使用
1951 0
Dubbo——对版本号version的理解与使用
|
存储 前端开发 Java
【开题报告】基于SpringBoot的非文化遗产宣传平台的设计与实现
【开题报告】基于SpringBoot的非文化遗产宣传平台的设计与实现
1412 0
|
JSON 网络协议 开发工具
对已有的docker容器添加新的目录映射, 端口映射,环境变量,dns等
docker容器已经建立并运行, 需要在已有容器上添加新的目录映射,端口映射,环境变量等
4104 0
|
2月前
|
Java API 开发工具
阿里云大模型服务平台百炼首次调用千问API教程:OpenAI接口、DashScope SDK等接入方式
本文介绍了阿里云百炼平台调用大模型API的完整实操流程,覆盖账号开通、API Key获取、多系统环境变量配置三大核心环节。用户完成账号注册并开通百炼服务后,即可生成专属API Key,文中针对Linux、macOS、Windows三大主流系统,分别提供了永久性与临时性环境变量的分步配置指南,通过将API Key存入环境变量,可避免代码硬编码带来的密钥泄露风险。同时提示,使用华北2、新加坡等特定地域模型时,需在请求地址中填入对应业务空间ID,帮助开发者安全、规范地完成大模型API接入,快速启动AI应用开发。
阿里云大模型服务平台百炼首次调用千问API教程:OpenAI接口、DashScope SDK等接入方式
Idea在debug时打上断点没有用 Skipped breakpoint at ... because it happened inside debugger evaluation
Idea在debug时打上断点没有用 Skipped breakpoint at ... because it happened inside debugger evaluation
3709 0
|
存储 安全 区块链
如何使用Crypto++库?
【10月更文挑战第29天】如何使用Crypto++库?
933 4
|
IDE NoSQL Java
如何搭建springboot脚手架
本文讲述了项目初始化时常见的环境搭建问题,包括IDE的下载和选择,以及版本管理的复杂性。作者分享了在使用不同版本的SpringBoot和SpringCloud时遇到的版本兼容性问题,强调了版本管理的重要性。文章还提到了Maven在解决依赖关系中的作用,以及介绍了SpringBoot的自动配置功能。此外,文章提供了全局异常处理、日志处理、跨域类和响应体的代码示例,并推荐了一些常用的开发工具,如内存版中间件和Java工具库。最后,作者提到在实际工作中,与他人协作时的环境一致性问题也可能带来困扰。
2221 1
|
缓存 NoSQL Java
自研一套高性能API网关,你都需要做什么?
本文介绍了自研API网关的基本组成和架构,涵盖网关的作用、请求生命周期、功能点详解(如插件和规则)、技术选型(Java、Netty、Nacos)及设计要点。通过异步化设计、缓存、串行化优化、提高吞吐量和合理配置工作线程,确保高性能与稳定性。最后,提供了网关的架构图,帮助读者全面理解其构建过程。
866 0
|
算法 开发工具 git
使用 fuzzywuzzy 模块计算两个字符串之间的相似度
使用 fuzzywuzzy 模块计算两个字符串之间的相似度
467 1

热门文章

最新文章

相关产品

  • 检索分析服务 Elasticsearch版