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

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
简介: 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


相关实践学习
使用阿里云Elasticsearch体验信息检索加速
通过创建登录阿里云Elasticsearch集群,使用DataWorks将MySQL数据同步至Elasticsearch,体验多条件检索效果,简单展示数据同步和信息检索加速的过程和操作。
ElasticSearch 入门精讲
ElasticSearch是一个开源的、基于Lucene的、分布式、高扩展、高实时的搜索与数据分析引擎。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基于Lucene)。 ElasticSearch的实现原理主要分为以下几个步骤: 用户将数据提交到Elastic Search 数据库中 通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据 当用户搜索数据时候,再根据权重将结果排名、打分 将返回结果呈现给用户 Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。
相关文章
|
6月前
|
运维 架构师 搜索推荐
7 年+积累、 Elastic 创始人Shay Banon 等 15 位专家推荐的 Elasticsearch 8.X新书已上线...
7 年+积累、 Elastic 创始人Shay Banon 等 15 位专家推荐的 Elasticsearch 8.X新书已上线...
84 4
|
6月前
|
存储 安全 数据处理
Elastic 中国开发者大会2023最新干货——Elasticsearch 7、8 新功能一网打尽
Elastic 中国开发者大会2023最新干货——Elasticsearch 7、8 新功能一网打尽
69 0
|
11月前
|
搜索推荐 索引
Elasticsearch elastic io 100%,但磁盘的iops和吞吐量没爆没啥原因吗?
Elasticsearch elastic io 100%,但磁盘的iops和吞吐量没爆没啥原因吗?
182 3
|
11月前
|
存储 缓存 监控
Elasticsearch elastic io 100%,但磁盘的iops和吞吐量没爆没啥原因吗?
Elasticsearch elastic io 100%,但磁盘的iops和吞吐量没爆没啥原因吗?
279 2
|
存储 自然语言处理 监控
ElasticSearch第三讲:ES详解 - Elastic Stack生态和场景方案
ElasticSearch第三讲:ES详解 - Elastic Stack生态和场景方案
146 0
|
缓存 安全 Java
带你读《Elastic Stack 实战手册》之8:—— 3.4.1.1.安装Elasticsearch(本地及docker)(2)
带你读《Elastic Stack 实战手册》之8:—— 3.4.1.1.安装Elasticsearch(本地及docker)(2)
163 1
|
数据采集 数据可视化 搜索推荐
带你读《Elastic Stack 实战手册》之3:——3.1.1.从 Elasticsearch 到 Elastic Stack(上)
带你读《Elastic Stack 实战手册》之3:——3.1.1.从 Elasticsearch 到 Elastic Stack(上)
247 0
|
存储 安全 数据可视化
带你读《Elastic Stack 实战手册》之3:——3.1.1.从 Elasticsearch 到 Elastic Stack(中)
带你读《Elastic Stack 实战手册》之3:——3.1.1.从 Elasticsearch 到 Elastic Stack(中)
180 0
|
测试技术 Apache
带你读《Elastic Stack 实战手册》之3:——3.1.1.从 Elasticsearch 到 Elastic Stack(下)
带你读《Elastic Stack 实战手册》之3:——3.1.1.从 Elasticsearch 到 Elastic Stack(下)
176 0
|
存储 缓存 Ubuntu
带你读《Elastic Stack 实战手册》之8:—— 3.4.1.1.安装Elasticsearch(本地及docker)(1)
带你读《Elastic Stack 实战手册》之8:—— 3.4.1.1.安装Elasticsearch(本地及docker)(1)
200 0

相关产品

  • 检索分析服务 Elasticsearch版