在 Elasticsearch 中实现自动完成功能 3:completion suggester

news2024/11/18 2:20:36

在这篇博文中,我们将讨论 complete suggester - 一种针对自动完成功能进行优化的 suggester,并且被认为比我们迄今为止讨论的方法更快。

Completion suggester 使用称为有限状态转换器的数据结构,该结构类似于 Trie 数据结构,并且针对更快的查找进行了优化。 这些数据结构存储在节点的内存中,以实现更快的搜索。 与 edge-n-gram 和 search_as_you_type 一样,这也通过使用我们提供的输入更新内存中的 FST 来完成索引时的大部分工作。

Elasticsearch 类型的一种特殊类型 —— complete,用于实现它:

PUT /movies
{
  "mappings": {
    "properties": {
      "title": {
        "type": "completion"
      }
    }
  }
}

映射还支持完成字段的 analyzer、search analyzer、max_input_length 参数。 分析器值默认为 simple analyzer,它将输入小写并对任何非字母字符,例如数字、空格、连字符等进行分词。完成类型上的分析器的行为与其他文本字段上的分析器不同。 经过分析后,分词不能单独使用 - 它们根据输入文本中的顺序放在一起并插入到 FST 中。 此外,我们无法在此方法中使用 _analyze 端点来测试我们的映射。 如果我们尝试这样做,Elasticsearch 会抛出一个错误,指出 “Can't process field [title], Analysis requests are only supported on tokenized fields”。

在索引文档时,我们指定输入和可选的权重参数 -

POST /movies/_doc/1001
{
  "title": [
    {
      "input": "Harry Potter and the Goblet of Fire",
      "weight": 5
    },
    {
      "input": "Goblet of Fire",
      "weight": 10
    }
  ]
}

POST /movies/_doc/1002
{
  "title": {
    "input": [
      "Harry Potter and the Goblet of Fire",
      "Goblet of Fire"
    ],
    "weight": 2
  }
}

我们可以使用 input 参数为单个文档指定多个匹配项。 weight 参数控制搜索结果中文档的排名。 它可以针对每个 input 进行指定,如上面的第一个文档(1001)中所示,或者可以对所有 input 保持相同,如第二个文档(1002)中所示。

使用 _search 端点的请求正文中的 suggest 子句查询建议字段。 在 ES 5.0 版本之前,有一个单独的端点 - _suggest 用于 suggester。 互联网上的许多示例都使用 _suggest。 从版本 5 开始,_search 端点本身也已更新以支持 suggester。

默认情况下,Elasticsearch 返回整个匹配文档。 如果我们只对建议文本感兴趣,我们可以使用 _source 选项并将其设置为“suggest”。 通过这种方式,我们可以最大限度地减少磁盘获取和传输开销:

GET /movies/_search?filter_path=**.harry_suggest
{
  "_source": "title.input",
  "suggest": {
    "harry_suggest": {
      "prefix": "goblet of f",
      "completion": {
        "field": "title"
      }
    }
  }
}

上面命令返回的结果为:

{
  "suggest": {
    "harry_suggest": [
      {
        "text": "goblet of f",
        "offset": 0,
        "length": 11,
        "options": [
          {
            "text": "Goblet of Fire",
            "_index": "movies",
            "_id": "1001",
            "_score": 10,
            "_source": {
              "title": [
                {
                  "input": "Harry Potter and the Goblet of Fire"
                },
                {
                  "input": "Goblet of Fire"
                }
              ]
            }
          },
          {
            "text": "Goblet of Fire",
            "_index": "movies",
            "_id": "1002",
            "_score": 2,
            "_source": {
              "title": {
                "input": [
                  "Harry Potter and the Goblet of Fire",
                  "Goblet of Fire"
                ]
              }
            }
          }
        ]
      }
    ]
  }
}

"Goblet of Fire" 在建议中返回了两次,因为我们已在两个文档中提供此文本作为输入。 这可以通过使用 skip_duplicates 选项来避免。

GET /movies/_search?filter_path=**.harry_suggest
{
  "_source": "title.input",
  "suggest": {
    "harry_suggest": {
      "prefix": "goblet of f",
      "completion": {
        "field": "title",
        "skip_duplicates": true
      }
    }
  }
}
{
  "suggest": {
    "harry_suggest": [
      {
        "text": "goblet of f",
        "offset": 0,
        "length": 11,
        "options": [
          {
            "text": "Goblet of Fire",
            "_index": "movies",
            "_id": "1001",
            "_score": 10,
            "_source": {
              "title": [
                {
                  "input": "Harry Potter and the Goblet of Fire"
                },
                {
                  "input": "Goblet of Fire"
                }
              ]
            }
          }
        ]
      }
    ]
  }
}

警告:当设置为 true 时,此选项会减慢搜索速度,因为需要访问更多建议才能找到前 N 个。

在 completion suggester 的情况下,Elasticsearch 从第一个字符开始一次匹配文档一个字符,在输入新字符时向前移动一个位置。如上所述,它保留 FST 中的输入顺序。 因此,它无法像基于 n-gram 的方法那样在输入中间进行匹配。 即,如果你有一部名为 “Harry Potter and the Goblet of Fire” 的电影,并且你输入 “goblet of fire”,它不会将文档作为匹配项返回。 但是,你可以使用输入选项来提供多个匹配项。 你可以手动对输入字符串进行分词,并将分词传递到输入选项中的 Elasticsearch,就像我们在上面的示例中通过提供 “Goblet of Fire” 作为附加输入所做的那样。

Completion suggester 支持 fuzzy queries,使我们能够在搜索文档时考虑拼写错误。 你还可以指定前缀文本作为正则表达式查询。 下面示例中的两个查询都返回 “Goblet of Fire” 作为建议 -

GET /movies/_search?filter_path=**.harry_suggest
{
  "_source": "title.input",
  "suggest": {
    "harry_suggest": {
      "prefix": "gobet of f",
      "completion": {
        "field": "title",
        "fuzzy": {
          "fuzziness": 2
        }
      }
    }
  }
}
GET /movies/_search?filter_path=**.harry_suggest
{
  "_source": "title.input",
  "suggest": {
    "harry_suggest": {
      "regex": "g[aieou]b",
      "completion": {
        "field": "title"
      }
    }
  }
}

添加上下文到搜索

与其他查询不同,completion suggesters 不支持在查询中添加过滤器。 即,你无法根据文档中其他字段的值过滤掉建议。 假设我们有一个存储电影的索引,并且我们正在开发基于标题字段的自动完成功能。 假设我们已将 title 映射为完成类型,还有其他字段,如 genres、ratings、production companies 等。有一个 title 为 “Goblet of Fire” 的文档,其 genre 为 “action”。 现在,如果我们尝试根据  genre = "romance" 过滤掉自动完成建议,我们预计它不应该返回 “Goblet of Fire”:

POST /movies/_doc/1001
{
  "genre": "action",
  "title": [
    {
      "input": "Harry Potter and the Goblet of Fire",
      "weight": 5
    },
    {
      "input": "Goblet of Fire",
      "weight": 10
    }
  ]
}

POST /movies/_doc/1002
{
  "genre": "fiction",
  "title": {
    "input": [
      "Harry Potter and the Goblet of Fire",
      "Goblet of Fire"
    ],
    "weight": 2
  }
}

GET /movies/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "genre": "romance"
          }
        }
      ]
    }
  },
  "suggest": {
    "harry_suggest": {
      "prefix": "goblet",
      "completion": {
        "field": "title"
      }
    }
  }
}

上述搜索将返回和之前一样的结果。仿佛那个过滤器根本就不存在。这并不像我们预期的那样工作 - 它返回 “Goblet of Fire” 作为建议,即使它属于 “action” 类型。 这种限制背后的主要原因是它的设计。 正如已经讨论过的,建议存储在单独的数据结构中 - 内存中 FST,而其他字段存储在磁盘上。 这种设计有助于通过内存中 FST 进行更快的搜索。 像上面这样的查询违背了这种设计。

然而,Elasticsearch 确实提供了上下文建议在一定程度上规避了这个问题。 要使用上下文建议器,我们必须在为索引创建映射时提供上下文:

DELETE movies

PUT /movies
{
  "mappings": {
    "properties": {
      "title": {
        "type": "completion",
        "contexts": [
          {
            "name": "genre",
            "type": "category"
          }
        ]
      }
    }
  }
}

对于特定的 completion 字段,我们可以定义多个具有唯一名称的上下文。 支持两种类型的上下文:

  • Category => 你正在索引的事物的类别,例如 电影/歌曲的类型(genre)
  • Geo => 你正在索引的文档的地理点,允许根据经纬度过滤建议。

上述每种上下文类型都支持一些高级参数,例如精度 (precision)、地理上下文的邻居 (neighbours)、查询时的增强 (boost),以便具有特定类别的文档获得更高的分数。 请注意,对于启用上下文的完成字段,在索引文档以及查询文档时,上下文参数是必需的。

让我们在上面创建的索引中索引一些文档:

POST /movies/_doc/2001
{
  "title": {
    "input": "Harry Potter and the Chamber of Secrets",
    "contexts": {
      "genre": "mystery"
    }
  }
}

POST /movies/_doc/2002
{
  "title": {
    "input": "Harry Potter and the Prisoner of Azkaban",
    "contexts": {
      "genre": "crime"
    }
  }
}

上面,我们将 "Harry Potter and the Prisoner of Azkaban" 索引为 “crime” 类型的电影,将 "Harry Potter and the Chamber of Secrets" 索引为 “mystery” 类型的电影。 让我们尝试获取前缀 “harry” 的建议:

GET /movies/_search?filter_path=**.harry_suggest
{
  "_source": "title.input",
  "suggest": {
    "harry_suggest": {
      "prefix": "harry",
      "completion": {
        "field": "title",
        "contexts": {
          "genre": "crime"
        }
      }
    }
  }
}

上面查询的结果为:

{
  "suggest": {
    "harry_suggest": [
      {
        "text": "harry",
        "offset": 0,
        "length": 5,
        "options": [
          {
            "text": "Harry Potter and the Prisoner of Azkaban",
            "_index": "movies",
            "_id": "2002",
            "_score": 1,
            "_source": {
              "title": {
                "input": "Harry Potter and the Prisoner of Azkaban"
              }
            },
            "contexts": {
              "genre": [
                "crime"
              ]
            }
          }
        ]
      }
    ]
  }
}

从上面的响应中可以看出,即使传递的前缀与上面索引的两个文档都匹配,也仅返回 “crime” 类型的 “Harry Potter and the Prisoner of Azkaban”。

这就是我们在 Elasticsearch 中实现自动完成的第三种方法。 那么,completion suggester 与迄今为止看到的其他方法相比如何? 它绝对是最快的,因为要搜索的数据在内存中可用,但是如果我们决定使用它实现自动完成,我们需要记住一些事情:

  • 必须注意索引的大小,因为建议存储在内存中。
  • 中缀 (infix) 匹配,例如 不支持按中间名匹配。
  • 不支持对文档中其他字段的建议进行高级过滤。

总结一下,我们可以说在选择在 Elasticsearch 中实现自动完成功能的方法时应考虑以下因素:

  • 数据是否已建立索引? 以什么格式? 我们可以重新索引它以使其更适合自动完成功能吗? 如果数据已经被索引为文本字段并且我们无法重新索引它,我们将需要采用查询时间方法 - 即前缀查询 (prefix queries) !
  • 该字段可以通过哪些方式查询? 以多种方式存储它有意义吗?
  • 是否需要支持中缀 (infix) 匹配? 文本中的单词顺序是固定的吗? 用户是否熟悉该顺序? complete suggesters 不支持中缀匹配,并且不适合具有众所周知的顺序的字段。
  • 将作为值提供给我们的字段的文本的最大大小是多少? 如果保存在内存中会产生问题吗? completion suggesters 将数据保存在内存中,基于 n-gram 的方法在基本分词化后创建附加分词以实现更快的匹配。
  • 我们需要为这个字段建立一个单独的索引吗? 如果这里提到的所有三种方法都不能满足你的要求,那么你将需要创建另一个索引。 在该索引中,只有 auto-complete 功能所需的字段才会存储为唯一文档,而不是与同一索引中的其他数据一起保存。 这将最大限度地减少节点膨胀的可能性,并且还可以提供更快的建议。 但是,是的,它毕竟是一个单独的索引,你必须保持主索引和新索引之间的数据同步。 管理另一个索引也有开销。

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

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

相关文章

PIM基础

1.PIM概述 PIM协议简介 PIM称为协议无关组播。协议无关指的是与单播路由协议无关,即PIM不需要维护专门的单播路由信息。作为组播路由解决方案,它直接利用单播路由表的路由信息,对组播报文执行RPF检查,检查通过后创建组播路由表项…

《Java极简设计模式》第08章:外观模式(Facade)

作者:冰河 星球:http://m6z.cn/6aeFbs 博客:https://binghe.gitcode.host 文章汇总:https://binghe.gitcode.host/md/all/all.html 源码地址:https://github.com/binghe001/java-simple-design-patterns/tree/master/j…

conda 创建虚拟环境

1.为什么要创建虚拟环境 我们在做开发或者跑论文实验可能会同时进行多个任务,这些任务可能会依赖于不同的python环境,比如有的用到3.6有的用到3.7,这时我们创建不同版本的python,放到虚拟环境中给不同的任务分别提供其所需要的版本…

Docker简单案例

我们安装完Docker之后就可以练习了. 镜像 案例1-拉取、查看镜像 1)首先去镜像仓库搜索nginx镜像,比如 : Docker Hub Container Image Library | App Containerization 搜索我们想要拉取的镜像 , 例如: nginx 复制命令: docker pull nginx …

【智慧燃气】中国智慧燃气现状、关键问题分析及构架分析!

关键词:智慧燃气、数据分析、数据挖掘、数字燃气、智慧燃气平台 一、智慧燃气发展现状 智慧燃气概述 对于智慧燃气的概念,燃气行业目前并未有统一的定义,基本上还处于各自阐述的阶段。小编认为,城乡智慧燃气系统(以…

400电话号码办理

办理400虚拟电话号码的需求在如今的商业环境中越来越普遍。无论是小型企业还是个人创业者,拥有自己的400虚拟电话号码都能为他们的业务带来巨大的便利和形象提升。那么,如何高效地办理400虚拟电话号码呢? 首先,选择可信赖的服务提…

干货:如何在前端统计用户访问来源?

在前端统计用户访问来源是一个常见的需求,通过获取访问来源信息,我们可以了解用户是通过直接访问、搜索引擎、外部链接等途径进入我们的网站或应用。下面是一个详细的介绍,包括方法和实现步骤。 一、获取HTTP Referer HTTP Referer是HTTP请…

使用XLua在Unity中获取lua全局变量和函数

1、Lua脚本 入口脚本 print("OK") --也会执行重定向 require("Test") 测试脚本 print("TestScript") testNum 1 testBool true testFloat 1.2 testStr "123"function testFun()print("无参无返回") endfunction te…

力扣 -- 1312. 让字符串成为回文串的最少插入次数

解题步骤&#xff1a; 参考代码&#xff1a; class Solution { public:int minInsertions(string s) {int ns.size();vector<vector<int>> dp(n,vector<int>(n));//无需初始化//填表for(int in-1;i>0;i--){for(int ji;j<n;j){//状态转移方程if(s[i]s[…

【MySQL】数据库排查慢查询、死锁进程排查、预防以及解决方法

MySQL数据库排查慢查询、死锁进程及解决方法 一、排查慢查询 1.1检查慢查询日志是否开启 1.1.1使用命令检查是否开启慢查询日志: SHOW VARIABLES LIKE slow_query_log;如果是 Value 为 off 则并未开启 1.1.2开启并且查看慢查询日志: MySQL提供了慢查询日志功能,可以记录所…

文件系统和文件读写(初识)

1.文件读写 1.QFileDialog提供了一个对话框&#xff0c;允许用户选择文件或者目录&#xff0c;也允许用户遍历文件系统&#xff0c;用以选择一个或多个文件或者目录。 2.QFileDialog继承了QDialog&#xff0c;具有父类的各种方法 3.Qt QFile文件操作 很多应用程序都需要具备…

centos7下 编译coreboot生成真机可用的bios固件, 并在真机上演示 (上篇)

看了国内的corebot相关文章以及b站的corebot视频, 大不部分都是编译 用于虚拟机(qemu)或者模拟器上运行的 很少有编译corebot项目 出可以再真机上使用的的 bios 本教程应该是全网最详细的了, 真机版的coreboot bios固件, 所以有点长, 希望对你有帮助 #coreboot编译总说明文档…

寒冬之下,互联网金融的数据化建设心得

引言&#xff1a; 在当前经济寒冬下&#xff0c;互联网金融行业面临着巨大的竞争压力和市场变化。为了保持竞争优势&#xff0c;不断创新和适应市场需求&#xff0c;互联网金融企业需要进行数据化建设。 本文将从策略规划、数据采集与整合、数据挖掘与分析、风险控制等方面&a…

数学建模——确定性时间序列分析方法

介绍 将预测对象按照时间顺序排成一组序列&#xff0c;称为时间序列。从时间序列过去的变化规律&#xff0c;推断今后变化的可能性及变化趋势、变化规律&#xff0c;这就是时间序列预测法。 时间序列模型&#xff0c;其实也是一种回归模型。其基本原理是&#xff0c;一方面承认…

windows Vscode 连接 虚拟机,超详细,含免密免ip配置 以 linux 虚拟机为例

我们这里使用 ssh 进行连接&#xff0c;不了解 ssh 的也没关系&#xff0c;感兴趣的可以自己了解一下。 我的虚拟机是 Ubuntu20.04&#xff0c;如果出现与 Centos 不一样的操作可以自行替换。 &#xff08;应该不会有&#xff1f;&#xff1f;&#xff09; 一 . 登录虚拟机~&a…

植物转录因子研究策略及方法

植物转录因子研究 转录因子&#xff08;TF&#xff09;是一类具有特殊结构的蛋白质&#xff0c;也称为反式作用因子。通过与靶基因启动子区域的顺式作用元件特异性结合&#xff0c;行使调控基因表达的功能。典型的转录因子一般具有4个功能结构域&#xff1a;DNA结合区&#xf…

pyqt 划线标注工具,可用于车道线标注

目录 效果图: pyqt代码: opencv划线: 效果图: pyqt代码: import osfrom PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QPushButton, QLabel from PyQt5.QtGui import QPainter, QPen, QColor, QImage, QPixmap from PyQt5.QtCore import Qt, QPoint i…

ubuntu编写makefile编译c++程序

常见的编译工具 gcc/gvisual cclang 编译一个简单的程序 main.cpp #include <iostream>int main() {std::cout << "hello world" << std::endl;return 0; }gcc 编译 源文件&#xff08;.cpp&#xff09;编译生成目标文件&#xff08;.o&#xf…

Python 封装一个自己的队列,切片? 深度copy ? 线程安全?

文章大纲 简介,Python 中的几种队列queue.queue 和 collections.deque 的区别collections 双端队列 deque 的封装队列是否可以支持深度 copy ?线程安全 的 queue参考文献和学习路径简介,Python 中的几种队列 https://docs.python.org/3/library/queue.html queue.queue 和 …

探究美颜算法:直播实时美颜SDK的集成和定制

下文小编将于大家一同探讨美颜算法&#xff0c;主要是直播实时美颜SDK的集成和定制方向。 一、直播实时美颜SDK的作用 直播实时美颜SDK是一种软件开发工具包&#xff0c;为开发者提供了将美颜功能集成到他们的直播应用中的手段。它可以用于各种应用&#xff0c;包括视频聊天、…