这篇博客的主要目的是介绍一下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
文档:通常情况下,我们会把一个具体的目标进行一定的整理,然后以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)。根据倒排列表,即可获知哪些文档包含某个单词。
倒排索引:
单词ID:记录每个单词的单词编号;单词:对应的单词;文档频率:代表文档集合中有多少个文档包含某个单词;
倒排列表:包含单词ID及其他必要信息,具体信息如下:
DocId:单词出现的文档id;TF:单词在某个文档中出现的次数;POS:单词在文档中出现的位置;
以单词“加盟”为例,其单词编号为6,文档频率为3,代表整个文档集合中有三个文档包含这个单词,对应的倒排列表为{(2;1;<4>),(3;1;<7>),(5;1;<5>)},含义是在文档2,3,5出现过
这个单词,在每个文档的出现过1次,单词“加盟”在第一个文档的POS是4,即文档的第四个单词是“加盟”,其他的类似。
这个倒排索引已经是一个非常完备的索引系统,实际搜索系统的索引结构基本如此。
ElasticSearch引擎把文档数据写入到倒排索引(Inverted Index)的数据结构中,倒排索引建立的是分词(Term)和文档(Document)之间的映射关系,在倒排索引中,数据是面向词
(Term)而不是面向文档的。一个倒排索引由文档中所有不重复词的列表构成,对于其中每个词,有一个包含它的文档列表,示例,对以下三个文档去除停用词后构造倒排索引:
倒排索引的查询过程,假设我们要查询包含“搜索引擎”的文档,具体步骤如下:
通过倒排索引获得“搜索引擎”对应的文档id列表,有1,3;
通过正排索引查询1和3的完整内容;
返回最终结果.
单词词典的数据结构
当我们通过ES来查询某个关键字时,ES会先从单词词典中找到这个关键字对应的元素,然后,从元素中的指针定位到倒排列表中的某个倒排项,就能从倒排项中,快速知道,所有包含该关键字的
文档列表。所以,单词词典采用什么样的数据结构来存储,是关系到查询速度的重要因素之一;思考:如果这里有上千万的记录呢?如何通过term来查找呢?
一般来说,单词词典通常的数据结构有两种,一种是哈希+链表,另一种是树。
方式1:哈希+链表
哈希+链表这种形式,和JAVA中老的HashMap的数组结构形式是一样的。在创建索引时,会根据单词的哈希值,计算出单词所在位置,然后存放到该位置的链表中。
方式2:树
树是另外一种ES经常用在单词词典中的数据结构,通常是B或B+树。这种数据结构,和MySQL数据库的索引是同样的,也是一种查询高效的数据结构。
B+树内部结点存索引,叶子结点存数据,这里的 单词词典就是B+树索引,倒排列表就是数据,整合在一起后如下所示:
ES存储的是一个JSON格式的文档,其中包含多个字段,每个字段会有自己的倒排索引!!!!
倒排索引的创建—分词器
我们交给ES的是文档,ES回馈给我们的是倒排索引,那么,ES是如何把文档变成倒排索引的呢?一般而言,会经过三个步骤:
预处理。首先,要对文档内容进行一定程序的整理,如过滤掉其中的html标签等,这个过程,称之为预处理;
分词。根据特定规则,将预处理后的内容拆分成一个个单词,这个过程,称之为分词;
标准化。分词后,文档变成了一个单词的集合,但是,在这个集合中,可能存在一些无意义的,错误的,或重复的单词,那么,在标准化阶段,就是要把这些内容处理掉,以避免这些单词进入词典,
影响词典的质量。在分词阶段,起到关键作用的就是分词器,并且,分词阶段只能有一个分词器。你可以使用ES内置的分词器,也可以第三方提供的分词器或者自定义分词器。
ES自带的分词器
。。。。。。。。
。。。。。。。。
以上就是ES自带的分词器,我们发现,这些分词器基本上都无法满足业务需要,尤其是包含中文搜索的业务。那么,我们就需要引入一些第三方的分词器来帮助ES完成分词。
第三方分词器
。。。。。。。
。。。。。。。
我发现自己需要这个。假设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-
因此,当我遵循MichaelHartl的RubyonRails教程时,我注意到在用户表中,我们为:email属性添加了一个唯一索引,以提高find的效率方法,因此它不会逐行搜索。到目前为止,我们一直在根据情况使用find_by_email和find_by_id进行搜索。然而,我们从未为:id属性设置索引。:id是否自动索引,因为它在默认情况下是唯一的并且本质上是顺序的?或者情况并非如此,我应该为:id搜索添加索引吗? 最佳答案 大多数数据库(包括sqlite,这是RoR中的默认数据库)会自动索引主键,对于RailsMigration
假设我有一个可枚举对象enum,现在我想获取第三个项目。我知道一种通用方法是转换成数组,然后使用索引访问,如:enum.to_a[2]但这种方式会创建一个临时数组,效率可能很低。现在我使用:enum.each_with_index{|v,i|breakvifi==2}但这非常丑陋和多余。执行此操作最有效的方法是什么? 最佳答案 你可以使用take剥离前三个元素,然后剥离last从take给你的数组中获取第三个元素:third=enum.take(3).last如果您根本不想生成任何数组,那么也许:#Ifenumisn'tanEnum
在我的场景中,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。我们已
不知何故,我似乎无法获得包含我的聚合的响应...使用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
我想从特定索引开始遍历数组。我该怎么做?myj.eachdo|temp|...end 最佳答案 执行以下操作:your_array[your_index..-1].eachdo|temp|###end 关于ruby-从特定索引开始迭代数组,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/44151758/
我一直在努力学习如何处理由数组组成的数组。假设我有这个数组: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
我想使用部分字符串搜索数组,然后获取找到该字符串的索引。例如:a=["Thisisline1","Wehaveline2here","andfinallyline3","potato"]a.index("potato")#thisreturns3a.index("Wehave")#thisreturnsnil使用a.grep将返回完整的字符串,使用a.any?将返回正确的true/false语句,但都不会返回匹配的索引找到了,或者至少我不知道该怎么做。我正在编写一段代码,该代码读取文件、查找特定header,然后返回该header的索引,以便它可以将其用作future搜索的偏移量。如果
如何在rakedb:migrate:status中删除带有“**NOFILE**”的迁移ID列表?例如:StatusMigrationIDMigrationName--------------------------------------------------up20131017204224Createusersup20131218005823**********NOFILE**********up20131218011334**********NOFILE**********我不明白为什么当我自己手动删除它时它仍然保留旧的迁移文件,因为我正在研究迁移的工作原理。这是为了记录吗?但
1.回顾.TransportServicepublicclassTransportServiceextendsAbstractLifecycleComponentTransportService:方法:1publicfinalTextendsTransportResponse>voidsendRequest(finalTransport.Connectionconnection,finalStringaction,finalTransportRequestrequest,finalTransportRequestOptionsoptions,TransportResponseHandlerT>