草庐IT

Elasticsearch倒排索引详解

一只懒得睁眼的猫 2023-12-18 原文
 这篇博客的主要目的是介绍一下ES当中的倒排索引,参考:

https://blog.csdn.net/qq_21312297/article/details/102496833

https://zhuanlan.zhihu.com/p/68092794

https://baijiahao.baidu.com/s?id=1661292466150156938&wfr=spider&for=pc

https://blog.csdn.net/jiaojiao521765146514/article/details/83750548

  1. 倒排索引的概念
    在介绍倒排索引之前,我们先回忆几个基本的概念.

文档:通常情况下,我们会把一个具体的目标进行一定的整理,然后以JSON的形式交给ES。比如商品,通常我们会把商品名称,描述,价格,库存等一系列内容整理好,交给ES。那么,

对于ES来说,这个具体的目标就是文档,JSON是ES喜欢的文档表现形式。

文档ID:为了便于对文档进行标识,ES会将接收到的文档进行编号,这个编号可以由ES自动生成,也可以由客户端指定。

那么根据文档ID和文档,我们就可以得到正排索引,即通过key找value:

单词:文档交给ES后,ES会根据设定的规则,对文档进行分析和拆分,文档将会被拆分成一个个单词。换句话说,对于ES来说,分析后的文档就是由单词组成的。

单词ID:和文档一样,ES也会对单词进行标识,标识的方式同样也是对单词进行编号。

有了以上几个概念,我们再来解释倒排索引。倒排索引,描述了从单词到文档的关联关系。利用倒排索引,可以快速的通过单词找到和该单词关联的文档列表。倒排索引由两部分组成,一个

是单词词典,一个是倒排列表。一个文档由许多的单词组成,我们交给ES的文档的越多,分析拆分后得到的单词就越多,这些单词被整理组织成一个集合,集合中的每个元素记录了单词本身

的一些相关信息以及一个指定倒排列表的指针。通过单词,我们会在倒排列表中找到一个倒排项,该倒排项中,记录了出现过该单词的所有文档ID,以及单词在文档中的位置信息。也就是说,

倒排列表,是倒排项的集合。倒排列表会以文件的形式,具体的存储在磁盘中的某个位置,这个文件,称之为倒排文件。

简单来说:通俗来讲正向索引是通过key找value,反向索引则是通过value找key。

那么在Elasticsearch当中,对于上面的正排索引,建立的倒排索引索引如下:

如上图所示:

Elasticsearch分别为每个field都建立了一个倒排索引,24,Kate, John Female这些叫term,而[1,2]就是Posting List倒排列表。Posting list就是一个int的数组,倒排列表记录了出现过

某个单词的所有文档的文档列表及单词在该文档中出现的位置信息,每条记录称为一个倒排项(Posting)。根据倒排列表,即可获知哪些文档包含某个单词。

  1. 倒排索引的直观了解
    正排索引:

倒排索引:

单词ID:记录每个单词的单词编号;单词:对应的单词;文档频率:代表文档集合中有多少个文档包含某个单词;

倒排列表:包含单词ID及其他必要信息,具体信息如下:

DocId:单词出现的文档id;TF:单词在某个文档中出现的次数;POS:单词在文档中出现的位置;

以单词“加盟”为例,其单词编号为6,文档频率为3,代表整个文档集合中有三个文档包含这个单词,对应的倒排列表为{(2;1;<4>),(3;1;<7>),(5;1;<5>)},含义是在文档2,3,5出现过

这个单词,在每个文档的出现过1次,单词“加盟”在第一个文档的POS是4,即文档的第四个单词是“加盟”,其他的类似。

这个倒排索引已经是一个非常完备的索引系统,实际搜索系统的索引结构基本如此。

  1. 基于倒排索引的ES数据查询过程
    如上面介绍:

ElasticSearch引擎把文档数据写入到倒排索引(Inverted Index)的数据结构中,倒排索引建立的是分词(Term)和文档(Document)之间的映射关系,在倒排索引中,数据是面向词

(Term)而不是面向文档的。一个倒排索引由文档中所有不重复词的列表构成,对于其中每个词,有一个包含它的文档列表,示例,对以下三个文档去除停用词后构造倒排索引:

倒排索引的查询过程,假设我们要查询包含“搜索引擎”的文档,具体步骤如下:

  1. 通过倒排索引获得“搜索引擎”对应的文档id列表,有1,3;

  2. 通过正排索引查询1和3的完整内容;

  3. 返回最终结果.

  4. 单词词典的数据结构
    当我们通过ES来查询某个关键字时,ES会先从单词词典中找到这个关键字对应的元素,然后,从元素中的指针定位到倒排列表中的某个倒排项,就能从倒排项中,快速知道,所有包含该关键字的

文档列表。所以,单词词典采用什么样的数据结构来存储,是关系到查询速度的重要因素之一;思考:如果这里有上千万的记录呢?如何通过term来查找呢?

一般来说,单词词典通常的数据结构有两种,一种是哈希+链表,另一种是树。

方式1:哈希+链表
哈希+链表这种形式,和JAVA中老的HashMap的数组结构形式是一样的。在创建索引时,会根据单词的哈希值,计算出单词所在位置,然后存放到该位置的链表中。

方式2:树
树是另外一种ES经常用在单词词典中的数据结构,通常是B或B+树。这种数据结构,和MySQL数据库的索引是同样的,也是一种查询高效的数据结构。

B+树内部结点存索引,叶子结点存数据,这里的 单词词典就是B+树索引,倒排列表就是数据,整合在一起后如下所示:

ES存储的是一个JSON格式的文档,其中包含多个字段,每个字段会有自己的倒排索引!!!!

  1. 倒排索引的创建—分词器
    我们交给ES的是文档,ES回馈给我们的是倒排索引,那么,ES是如何把文档变成倒排索引的呢?一般而言,会经过三个步骤:

  2. 预处理。首先,要对文档内容进行一定程序的整理,如过滤掉其中的html标签等,这个过程,称之为预处理;

  3. 分词。根据特定规则,将预处理后的内容拆分成一个个单词,这个过程,称之为分词;

  4. 标准化。分词后,文档变成了一个单词的集合,但是,在这个集合中,可能存在一些无意义的,错误的,或重复的单词,那么,在标准化阶段,就是要把这些内容处理掉,以避免这些单词进入词典,

影响词典的质量。在分词阶段,起到关键作用的就是分词器,并且,分词阶段只能有一个分词器。你可以使用ES内置的分词器,也可以第三方提供的分词器或者自定义分词器。

  1. 分词器介绍
    通常情况下,ES内置的分词器对中文是不友好的,所以,第三方分词器或者自定义分词器,对于中文搜索站点来说,是必须要做的。我们来认识一下ES内置的几个分词器,以及一些第三方分词器。

ES自带的分词器

  1. Standard Analyzer:是默认的分词器,会按词进行拆分,并做英文大写到小写的转换,如果是中文,会按照单字拆分。

。。。。。。。。

。。。。。。。。

以上就是ES自带的分词器,我们发现,这些分词器基本上都无法满足业务需要,尤其是包含中文搜索的业务。那么,我们就需要引入一些第三方的分词器来帮助ES完成分词。

第三方分词器

  1. IK:实现中英文单词的切分,可自定义词库,支持热更新分词词典。

。。。。。。。

。。。。。。。

有关Elasticsearch倒排索引详解的更多相关文章

  1. ruby-on-rails - 协会的 Rails 索引 - 2

    我发现自己需要这个。假设cart是一个包含用户列表的模型。defindex_of_itemcart.users.each_with_indexdo|u,i|ifu==current_userreturniendend获取此类关联索引的更简单方法是什么? 最佳答案 indexArray上的方法与您的index_of_item方法相同,例如cart.users.index(current_user)返回数组中第一个对象的索引==给obj。如果未找到匹配项,则返回nil。 关于ruby-on-

  2. ruby - Rails -- :id attribute? 所需的数据库索引 - 2

    因此,当我遵循MichaelHartl的RubyonRails教程时,我注意到在用户表中,我们为:email属性添加了一个唯一索引,以提高find的效率方法,因此它不会逐行搜索。到目前为止,我们一直在根据情况使用find_by_email和find_by_id进行搜索。然而,我们从未为:id属性设置索引。:id是否自动索引,因为它在默认情况下是唯一的并且本质上是顺序的?或者情况并非如此,我应该为:id搜索添加索引吗? 最佳答案 大多数数据库(包括sqlite,这是RoR中的默认数据库)会自动索引主键,对于RailsMigration

  3. ruby - 引用具有指定索引的枚举器值 - 2

    假设我有一个可枚举对象enum,现在我想获取第三个项目。我知道一种通用方法是转换成数组,然后使用索引访问,如:enum.to_a[2]但这种方式会创建一个临时数组,效率可能很低。现在我使用:enum.each_with_index{|v,i|breakvifi==2}但这非常丑陋和多余。执行此操作最有效的方法是什么? 最佳答案 你可以使用take剥离前三个元素,然后剥离last从take给你的数组中获取第三个元素:third=enum.take(3).last如果您根本不想生成任何数组,那么也许:#Ifenumisn'tanEnum

  4. ruby - 将 Logstash 中的时间戳时区转换为输出索引名称 - 2

    在我的场景中,Logstash收到的系统日志行的“时间戳”是UTC,我们在Elasticsearch输出中使用事件“时间戳”:output{elasticsearch{embedded=>falsehost=>localhostport=>9200protocol=>httpcluster=>'elasticsearch'index=>"syslog-%{+YYYY.MM.dd}"}}我的问题是,在UTC午夜,Logstash在外时区(GMT-4=>America/Montreal)结束前将日志发送到不同的索引,并且索引在20小时(晚上8点)之后没有日志,因为“时间戳”是UTC。我们已

  5. ruby - Rails Elasticsearch 聚合 - 2

    不知何故,我似乎无法获得包含我的聚合的响应...使用curl它按预期工作:HBZUMB01$curl-XPOST"http://localhost:9200/contents/_search"-d'{"size":0,"aggs":{"sport_count":{"value_count":{"field":"dwid"}}}}'我收到回复:{"took":4,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":90,"max_score":0.0,"hits":[]},"a

  6. ruby - 从特定索引开始迭代数组 - 2

    我想从特定索引开始遍历数组。我该怎么做?myj.eachdo|temp|...end 最佳答案 执行以下操作:your_array[your_index..-1].eachdo|temp|###end 关于ruby-从特定索引开始迭代数组,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/44151758/

  7. ruby - Array of Arrays,根据索引处的数组内容删除一个索引? - 2

    我一直在努力学习如何处理由数组组成的数组。假设我有这个数组:my_array=[['ORANGE',1],['APPLE',2],['PEACH',3]我将如何找到包含'apple'的my_array索引并删除该索引(删除子数组['APPLE',2]因为'apple'包含在该索引的数组中)?谢谢-我非常感谢这里的帮助。 最佳答案 您可以使用Array.select过滤掉项目:>>a=[['ORANGE',1],['APPLE',2],['PEACH',3]]=>[["ORANGE",1],["APPLE",2],["PEACH",3

  8. ruby - 如何使用部分字符串搜索数组并返回索引? - 2

    我想使用部分字符串搜索数组,然后获取找到该字符串的索引。例如:a=["Thisisline1","Wehaveline2here","andfinallyline3","potato"]a.index("potato")#thisreturns3a.index("Wehave")#thisreturnsnil使用a.grep将返回完整的字符串,使用a.any?将返回正确的true/false语句,但都不会返回匹配的索引找到了,或者至少我不知道该怎么做。我正在编写一段代码,该代码读取文件、查找特定header,然后返回该header的索引,以便它可以将其用作future搜索的偏移量。如果

  9. ruby-on-rails - Rails 4 从迁移索引中删除迁移 ID - 2

    如何在rakedb:migrate:status中删除带有“**NOFILE**”的迁移ID列表?例如:StatusMigrationIDMigrationName--------------------------------------------------up20131017204224Createusersup20131218005823**********NOFILE**********up20131218011334**********NOFILE**********我不明白为什么当我自己手动删除它时它仍然保留旧的迁移文件,因为我正在研究迁移的工作原理。这是为了记录吗?但

  10. elasticsearch源码关于TransportSearchAction【阶段三】 - 2

    1.回顾.TransportServicepublicclassTransportServiceextendsAbstractLifecycleComponentTransportService:方法:1publicfinalTextendsTransportResponse>voidsendRequest(finalTransport.Connectionconnection,finalStringaction,finalTransportRequestrequest,finalTransportRequestOptionsoptions,TransportResponseHandlerT>

随机推荐