在 Elasticsearch 中实现自动完成功能 2:n-gram

news2025/4/22 9:19:08

在第一部分中,我们讨论了使用前缀查询,这是一种自动完成的查询时间方法。 在这篇文章中,我们将讨论 n-gram - 一种索引时间方法,它在基本标记化后生成额外的分词,以便我们稍后在查询时能够获得更快的前缀匹配。 但在此之前,让我们先看看什么是 n-gram。 根据维基百科 -

n-gram 是给定文本或语音序列中 n 个项目的连续序列

有关 n-gram 的更多详细的介绍,请参阅之前的文章 “Elasticsearch: Ngrams, edge ngrams, and shingles”。

是的,就是这么简单,只是一系列文本。 这里的 “n” 项在字符级 n-gram 的情况下表示 “n” 个字符,在单词级 n-gram 的情况下表示 “n” 个单词。 词级 n-gram 也称为 shingles。 此外,根据 “n” 的值,这些被分类为 uni-gram(n=1)、bi-gram(n=2)、tri-gram(n=3)等。

下面的例子会更清楚:

Character n-grams for input string = "harry":
    n = 1 : ["h", "a", "r", "r", "y"]
    n = 2 : ["ha", "ar", "rr", "ry"]
    n = 3 : ["har", "arr", "rry"]

Word n-grams for input string = "harry potter and the goblet of fire":
    n = 1 : ["harry", "potter", "and", "the", "goblet", "of", "fire"]
    n = 2 : ["harry potter", "potter and", "and the", "the goblet",
           "goblet of", "of fire"]
    n = 3 : ["harry potter and", "potter and the", "and the goblet",
            "the goblet of", "goblet of fire"]

在这篇文章中,我们将讨论两种基于 n-gram 的方法 - 首先使用 edge n-gram 分词器,然后使用内置的 search-as-you-type 类型,该类型也在内部使用 n-gram 分词器。 这些额外的分词在索引文档时被输出到倒排索引中,从而最大限度地减少搜索时间延迟。 在这里,Elasticsearch 只需将输入与这些分词进行比较,这与前缀查询方法不同,它需要检查单个分词是否以给定输入开头。

Edge-n-gram 分词器

正如我们已经看到的,文本字段被分析并存储在倒排索引中。 分词是这个三步分析过程中的第二步,在过滤字符之后但在应用分词过滤器之前运行。 Edge-n-gram 分词器是 Elasticsearch 中可用的内置分词器之一。 它首先将给定文本分解为分词,然后为每个分词生成字符级 n-grams。

让我们为电影创建一个索引,这次使用 edge-n-gram 分词器:

PUT /movies
{
  "settings": {
    "analysis": {
      "analyzer": {
        "custom_edge_ngram_analyzer": {
          "type": "custom",
          "tokenizer": "customized_edge_tokenizer",
          "filter": [
            "lowercase"
          ]
        }
      },
      "tokenizer": {
        "customized_edge_tokenizer": {
          "type": "edge_ngram",
          "min_gram": 2,
          "max_gram": 10,
          "token_chars": [
            "letter",
            "digit"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "custom_edge_ngram_analyzer"
      }
    }
  }
}

在前缀查询示例中,我们没有将分析器参数传递给映射中的任何字段,而是依赖于默认的标准分析器。 上面,我们首先创建了一个自定义分析器custom_edge_ngram_analyzer,并传递给它类型为 edge_ngram 的自定义分词器 customized_edge_tokenizer。 Edge_ngram 分词器可以使用以下参数进行定制:

  • min_gram ⇒ 放入 gram 中的最小字符数,默认为 1,类似于上面看到的 uni-gram 示例
  • max_gram ⇒ 放入 gram 中的最大字符数,默认为 2,类似于上面看到的 bi-gram 示例
  • token_chars ⇒ 要保留在 token 中的字符,如果 Elasticsearch 遇到任何不属于提供的列表的字符,它将使用该字符作为新 token 的断点。 支持的字符类包括字母、数字、标点符号、符号和空格。 在上面的映射中,我们保留了字母和数字作为 token 的一部分。 如果我们将输入字符串传递为“harry potter: Deathly Hallows”,Elasticsearch 将通过打破空格和标点符号来生成 ["harry", "potter", "deathly", "hallows"]。

让我们使用 _analyze API 来测试我们的自定义边 n-gram 分析器的行为:

GET /movies/_analyze
{
  "field": "title",
  "text": "Harry Potter and the Order of the Phoenix"
}

上面命令返回的结果为:

[ha, har, harr, harry, po, pot, pott, potte, potter, an,
 and, th, the, or, ord, orde, order, of, th, the, ph, pho,
 phoe, phoen, phoeni, phoenix]

为了保持简洁,我没有包含实际响应,其中包含一组对象,每 gram 一个对象,包含有关该 gram 的元数据。 无论如何,正如可以观察到的,我们的自定义分析器按设计工作 - 为传递的字符串发出 gram,小写且长度在最小 - 最大设置内。 让我们索引一些电影来测试自动完成功能 -

POST /movies/_doc
{
  "title": "Harry Potter and the Half-Blood Prince"
}

POST /movies/_doc
{
  "title": "Harry Potter and the Deathly Hallows – Part 1"
}

Edge-n-grammed 字段也支持中缀匹配。 即,你也可以通过传递 “har” 和 “dead” 来匹配标题为 “harry potter and the deathly hallows” 的文档。 这使得它适合自动完成实现,其中输入文本中的单词没有固定的顺序。

GET /movies/_search?filter_path=**.hits
{
  "query": {
    "match": {
      "title": {
        "query": "deathly "
      }
    }
  }
}

上面命令返回结果:

{
  "hits": {
    "hits": [
      {
        "_index": "movies",
        "_id": "fb-HHIsByaLf0EuT7s0I",
        "_score": 4.0647593,
        "_source": {
          "title": "Harry Potter and the Deathly Hallows – Part 1"
        }
      }
    ]
  }
}
GET /movies/_search?filter_path=**.hits
{
  "query": {
    "match": {
      "title": {
        "query": "harry pot"
      }
    }
  }
}

上面的命令返回:

{
  "hits": {
    "hits": [
      {
        "_index": "movies",
        "_id": "fL-HHIsByaLf0EuT5M2i",
        "_score": 1.1879652,
        "_source": {
          "title": "Harry Potter and the Half-Blood Prince"
        }
      },
      {
        "_index": "movies",
        "_id": "fb-HHIsByaLf0EuT7s0I",
        "_score": 1.1377401,
        "_source": {
          "title": "Harry Potter and the Deathly Hallows – Part 1"
        }
      }
    ]
  }
}
GET /movies/_search?filter_path=**.hits
{
  "query": {
    "match": {
      "title": {
        "query": "potter har"
      }
    }
  }
}
{
  "hits": {
    "hits": [
      {
        "_index": "movies",
        "_id": "fL-HHIsByaLf0EuT5M2i",
        "_score": 1.3746086,
        "_source": {
          "title": "Harry Potter and the Half-Blood Prince"
        }
      },
      {
        "_index": "movies",
        "_id": "fb-HHIsByaLf0EuT7s0I",
        "_score": 1.3159354,
        "_source": {
          "title": "Harry Potter and the Deathly Hallows – Part 1"
        }
      }
    ]
  }
}

默认情况下,对分析字段(上例中的 title)的搜索查询也会对搜索词运行分析器。 如果你将搜索词指定为“deathly potter”,希望它只匹配第二个文档,你会感到惊讶,因为它匹配两个文档。 这是因为搜索词 “deathly potter” 也将被分词,将 “deathly” 和 “potter” 输出为单独的分词。 尽管 “Harry Potter and the Deathly Hallows – Part 1” 与最高分相匹配,但输入查询分词是单独匹配的,从而为我们提供了两个文档作为结果。 如果你认为这可能会导致问题,你也可以为搜索查询指定分析器。

因此,edge-n-gram 通过在倒排索引中保存额外的分词来克服前缀查询的限制,从而最大限度地减少查询时间延迟。 但是,这些额外的分词确实会占用节点上的额外空间,并可能导致性能下降。 我们在选择 n-gram 字段时应该小心,因为某些字段的值可能具有无限大小,并且可能会导致索引膨胀。

Search_as_you_type

Search_as_you 类型数据类型是在 Elasticsearch 7.2 中引入的,旨在为自动完成功能提供开箱即用的支持。 与 edge-n-gram 方法一样,这也通过生成额外的分词来优化自动完成查询来完成索引时的大部分工作。 当特定字段映射为 search_as_you_type 类型时,会在内部为其创建其他子字段。 让我们将标题字段类型更改为 search_as_you_type:

DELETE movies

PUT /movies
{
  "mappings": {
    "properties": {
      "title": {
        "type": "search_as_you_type",
        "max_shingle_size": 3
      }
    }
  }
}

对于上述索引中的标题属性,将创建三个子字段。 这些子字段使用 shingle token 过滤器。 Shingles 只不过是一组连续的单词(单词 n-gram,如上所示)。

  • 根 title 字段 ⇒ 使用映射中提供的分析器进行分析,如果未提供,则使用默认值
  • title._2gram ⇒ 这会将标题分成各有两个单词的部分,即大小为 2 的 shingles。
  • title._3gram ⇒ 这会将标题分成每个包含三个单词的部分。
  • title._index_prefix ⇒ 这将对 title._3gram 下生成的分词执行进一步的 edge ngram 分词。

我们可以使用我们最喜欢的 _analyze API 来测试它的行为:

GET movies/_analyze
{
  "text": "Harry Potter and the Goblet of Fire",
  "field": "title"
}

上面返回:

harry, potter, and, the, goblet, of, fire
GET movies/_analyze
{
  "text": "Harry Potter and the Goblet of Fire",
  "field": "title._2gram"
}

上面的命令返回:

harry potter, potter and, and the, the goblet, goblet of, of fire
GET movies/_analyze
{
  "text": "Harry Potter and the Goblet of Fire",
  "field": "title._3gram"
}

上面的命令返回:

harry potter and,potter and the,and the goblet,the goblet of,goblet of fire
GET movies/_analyze
{
  "text": "Harry Potter and the Goblet of Fire",
  "field": "title._index_prefix"
}

上面的命令返回:

h, ha, har, harr, harry, harry[空隔], harry p, harry po, harry pot, harry pott, 
harry potte, harry potter, harry potter[空隔], harry potter a, harry potter an,
harry potter and,
p, po, pot, pott, potte, potter, potter[空隔], potter a, potter an, potter and,
potter and[空隔], potter and t, potter and th, potter and the,
a, an, and, and[空隔], and t, and th, and the, and the[空隔], and the g, and the go,
and the gob, and the gobl, and the goble, and the goblet,
t, th, the, the[空隔], the g, the go, the gob, the gobl, the goble, the goblet,
the goblet[空隔], the goblet o, the goblet of,
g, go, gob, gobl, goble, goblet, goblet o, goblet of, goblet of[空隔], goblet of f,
goblet of fi, goblet of fir, goblet of fire,
o, of, of[空隔], of f, of fi, of fir, of fire, of fire[空隔],
f, fi, fir, fire, fire[空隔], fire[空隔][空隔] 

要创建多少个子字段由 max_shingle_size 参数决定,默认为 3,可以设置为 2、3 或 4。Search_as_you_type 是一个类似文本的字段,因此我们为文本字段使用其他选项(例如分析器、 还支持索引、存储、search_analyzer)。

你现在一定已经猜到了,它支持前缀和中缀匹配。 在查询时,我们需要使用 multi_match 查询,因为我们也需要定位其子字段:

POST movies/_doc
{
  "title": "Harry Potter and the Goblet of Fire"
}
GET /movies/_search?filter_path=**.hits
{
  "query": {
    "multi_match": {
      "query": "the goblet",
      "type": "bool_prefix",
      "analyzer": "keyword",
      "fields": [
        "title",
        "title._2gram",
        "title._3gram"
      ]
    }
  }
}

上面的查询返回:

{
  "hits": {
    "hits": [
      {
        "_index": "movies",
        "_id": "fr-sHIsByaLf0EuThM2C",
        "_score": 3,
        "_source": {
          "title": "Harry Potter and the Goblet of Fire"
        }
      }
    ]
  }
}

我们在这里将查询类型设置为 bool_prefix。 查询将匹配具有任何顺序的 title 的文档,但具有与查询中的文本匹配的顺序的文档将排名更高。 在上面的示例中,我们将 “the goblet” 作为查询文本传递,因此标题为 “the goblet of fire” 的文档的排名将高于标题为 “fire goblet” 的文档。

另外,我们将查询分析器指定为关键字,这样我们的查询文本 “the goblet” 就不会被分析,而是按原样进行匹配。 如果没有这个,标题为 “Harry Potter and the Goblet of Fire” 的文档以及标题为 “Harry Potter and the Deathly Hallows – Part 1” 的文档也会匹配。

这不是查询 search_as_you_type 字段的唯一方法,但肯定更适合我们的自动完成用例。

与 edge-n-gram 一样,search_as_you_type 通过存储针对自动完成进行优化的数据来克服前缀查询方法的限制。 因此,在这种方法中,我们也必须小心使用该字段存储的内容。 需要额外的空间来存储这些 n-gram 分词。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1087729.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

高质量!推荐一些免费自学网站

大家好,我是 jonssonyan 说到自学网站,大家第一印象肯定是”菜鸟教程“、”w3school“、B 站大学。这些教程当然非常的好,而且适合入门学习,但是存在一些缺点,第一,知识点比较分散,没有一个整体…

【EI检索征稿】第五届机器学习、大数据与商务智能国际会议(MLBDBI 2023)

第五届机器学习、大数据与商务智能国际会议(MLBDBI 2023) 2023 5th International Conference on Machine Learning, Big Data and Business Intelligence 由浙江财经大学信息管理与人工智能学院主办,AEIC学术交流中心作支持单位的第五届机器…

Squids DBMotion新增多款同构数据库迁移

秋天的第一次数据传输! 又双叒叕,丝滑的零停机数据库在线迁移工具Squids DBMotion再发新版! Squids DBMotion继续横向扩展数据库能力,本次不仅增加了PostgreSQL、GaussDB和openGauss的同构数据库能力,也提供了这些数…

自己在家给电脑重装系统Win10教程

自己在家怎么给电脑重装系统Win10?Win10电脑系统如果操作时间特别长了,就可能出现卡顿、蓝屏等系统问题,这时候用户就想给电脑重装系统,却不知道重装具体的操作步骤,下面小编给大家详细介绍自己在家给电脑重装Win10系统…

架构必备能力——kafka的选型对比及应用场景

系列文章目录 上手第一关,手把手教你安装kafka与可视化工具kafka-eagle Kafka是什么,以及如何使用SpringBoot对接Kafka 架构必备能力——kafka的选型对比及应用场景 系列文章目录一、Kafka的模型与优势1. Kafka 模型2. Kafka 优势 二、Kafka与竞争对手的…

【@胡锡进】大模型量化分析- 南京银行 601009.SH

对于股票价格的预测,以下是几种常见的方法: SARIMA模型:SARIMA(Seasonal Autoregressive Integrated Moving Average)模型适用于具有季节性变动的时间序列数据。它结合了ARIMA模型和季节性差分的方法来预测未来的价格…

axios响应拦截器 路由导航守卫

axios响应拦截器使用场景: 1. 处理全局错误:响应拦截器可以用来统一处理API请求的错误。当后端返回错误状态码,或者响应数据不符合预期时,我们可以在拦截器中进行统一的错误处理逻辑,例如弹窗提示、记录日志等。 2. 统…

倾斜摄影三维模型的顶层构建的问题分析

倾斜摄影三维模型的顶层构建的问题分析 在构建倾斜摄影超大场景的三维模型时,常见的顶层构建问题可能包括以下几个方面: 1、数据质量问题:倾斜摄影所获取的原始数据可能存在噪点、缺失、重叠或者变形等问题,这些问题会直接影响到…

Hadoop问题:start-all.sh显示未找到命令

在sbin文件夹下是start-all.sh可以运行的,但是到了别的文件夹下就不行了,于是想到了是文件路径问题,因为hadoop环境是和java环境一起配置的导致sbin写成了bin 解决办法: 打开.bashrc配置hadoop的环境变量 sudo vim ~/.bashrc …

热电厂蒸汽流量如何无线传输至无纸记录仪上显示?

某数码影像材料制造集团生产目前主要消耗蒸汽和电能源,蒸汽用能情况较为复杂,需要用5公里的蒸汽管线将较远区域某热电厂的蒸汽接入厂内,每周专人巡查一次管线,部分蒸汽管线位置特别偏僻,不易出入。 为了监控蒸汽流量&…

Java NIO到底是个什么东西?

Java NIO到底是个什么东西? 面试官:这次咱们就来聊聊Java 的NIO呗?你对NIO有多少了解? 候选者:嗯,我对Java NIO还是有一定的了解的,NIO是JDK 1.4 开始有的,其目的是为了提高速度。NIO翻译成 n…

GIS地图学知识

一、投影坐标系 "WGS 1984"坐标系墨卡托投影分度带(UTM ZONE)的选择: 北半球区选取最后字母为N的带,带数(经度的整数/6)31,如113.25,34.12中带数(113/6)3149,选取WGS 1984 UTM ZONE 49N 二、投影分带 经度中三度分带投…

Flink测试利器之DataGen初探 | 京东云技术团队

什么是 Flinksql Flink SQL 是基于 Apache Calcite 的 SQL 解析器和优化器构建的,支持ANSI SQL 标准,允许使用标准的 SQL 语句来处理流式和批处理数据。通过 Flink SQL,可以以声明式的方式描述数据处理逻辑,而无需编写显式的代码…

【2023研电赛】商业计划书赛道上海市一等奖:基于双矢量优化谐波预测控制的MMC-PET光伏储能系统

该作品参与极术社区组织的2023研电赛作品征集活动,欢迎同学们投稿,获取作品传播推广,并有丰富礼品哦~ 团队介绍 参赛单位:上海理工大学 参赛队伍:Dream explorers 参赛队员:吕哲 李天皓 赵安杰 项目意义…

基于Springboot实现商务安全邮箱邮件收发系统项目【项目源码+论文说明】计算机毕业设计

基于Springboot实现商务安全邮箱邮件收发系统演示 摘要 随着社会的发展,社会的方方面面都在利用信息化时代的优势。计算机的优势和普及使得商务安全邮箱的开发成为必需。 本文以实际运用为开发背景,运用软件工程原理和开发方法,采用jsp技术…

Allegro芯片引脚如何散出?

在日常进行PCB设计时,遇到有BGA芯片的项目,引脚就需要散出。那么如何散出呢? 需要对引脚进行散出的BGA芯片 下面详细介绍散出方法。 (1)选择菜单Route(布线) (2)选择Create Fanout (3)在选择散出命令后,在Find选项卡,可以选择Symbol(器件)或Pins(引脚)

vscode 右侧滚动条标记不提示,问题解决纪录

问题描述 用vscode看代码时,我希望在右侧提示一个变量在文件下都在那里使用,在那里赋值,之前该功能是存在的,当我打开一个新的文件夹时这个功能消失了。 解决办法 在setting.json文件下输入 "C_Cpp.intelliSenseEngine&…

0基础学习VR全景平台篇 第108篇:全景图细节处理(下,航拍)

上课!全体起立~ 大家好,欢迎观看蛙色官方系列全景摄影课程! (调色前图库) (原图-大图) 一、导入文件 单击右下角导入按钮,选择航拍图片所在文件夹,选择图片&#xff0…

土壤水分烘干法流程

土壤水分烘干法流程 叠小盒子装土 对折 得到一个正方形,裁掉多余的。然后将正方形按如下形式折 再次对折 然后再展开,对着折痕,竖立起盒子边缘 把上面的尖角翻下来 最后将多余的长条裁出一个盒子底部大小的小方块,放入盒子…

【教学类-35-04】学号+姓名+班级(中3班)学号字帖(A4竖版2份 竖版长条)

图片展示: 背景需求: 2022年9-2023年1月我去过小3班带班,但是没有在这个班级投放过学具,本周五是我在本学期第一次带中3班,所以提供了一套学号描字帖。先让我把孩子的名字和脸混个眼熟。 之前试过一页两套名字的纸张切割方法有:…