ELasticsearch

本文探讨了Apache Lucene中的文档评分机制,包括TF/IDF评分公式详解及查询改写技术,还介绍了如何利用二次评分调整搜索结果,以及如何更改Lucene的默认评分算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

查询DSL进阶

 Apache Lucene 默认评分公式解释
      对于查询相关性,重点是去理解文档对查询的得分是如何计算出来的
      当一个文档经Lucene返回,则意味着该文档与用户提交的查询是匹配的,在这种情况下,每个返回的文档都有一个得分。得分越高,文档相关度越高,至少从Apache Lucene 及其评分公式的角度来看是这样的。
      注意:同一文档在不同查询中的得分不具备可比性,不同查询返回文档中的最高分也不具备可比性 ,这是因为文档得分依赖多个因子,除了权重和查询本身的结构,还包括匹配的词项数目,词项所在字段,以及用于规范化的匹配类型
       为了计算文档得分,需要考虑以下这些因子:
      1.文档权重 : 索引期赋予某个文档的权重值
      2.字段权重   :  查询期赋予某个字段的权重值
      3.协调因子 : 基于文档中词项命中个数的协调因子,一个文档命中了查询中的词项越多,得分越高
      4.逆文档频率  : 一个基于词项的因子,用来告诉评分公式该词项有多么罕见。 逆文档频率越低,词项越罕见
      5.长度范数  : 每个字段的基于词项个数的归一化因子  一个字段包含的词项数越多,该因子的权重越低  --这意味着Apache Lucene评分公式更喜欢包含更少词项的字段
      6.词频  : 一个基于词项的因子,用来表示一个词项在某个文档中出现的次数。词频越高,文档得分越高
      7.查询范数  : 一个基于查询的归一化因子,它等于查询中词项的权重平方和。查询范数使不同查询的得分能相互比较,尽管这种比较通常是困难且不可行的。

      TF/IDF评分公式:

Lucene的打分公式很复杂,如下:

       得分公式是一个关于查询q和文档d的函数,有两个因子coord和queryNorm并不直接依赖查询词项,而是与查询词项的一个求和公式相乘,求和公式中的每个加数由以下因子连乘所得:词频、逆文档 频率、词项权重、范数。
       从公式我们可以导出一些基本规则:
           1.越多罕见的词项被匹配上,文档得分越高
           2.文档字段越短(包含更少的词项),文档得分越高
           3.权重越高(不论是索引期还是查询期赋予的权重值),文档得分越高
      所以,Lucene 将最高得分赋予同时满足以下条件的文档:包含多个罕见词项,词项所在字段较短(该字段索引了较少的词项)。

 查询改写

      如果你之前使用过诸如前缀查询或通配符查询之类的查询类型,那么你会发现这些都是基于多词项的查询,且都涉及查询改写。Elasticsearch使用查询改写是出于对性能的考虑。从Lucene角度来看,所谓的查询改写操作,就是把费时的原始查询类型实例改写成一个性能更高的查询类型实例
     举例:    也许用户想找出索引中所有name字段以字母j开头的文档
           简单的我们可以在clients索引中执行以下查询:
           curl -XGET 'localhost:9200/clients/_search?pretty' -d '{ "query" : { "prefix" : { "name" : "j", "rewrite"  : "constant_score_boolean"  } } }'


     查询改写的属性
           之前已经说过可以对任何多词项查询使用rewrite参数来控制查询改写。我们可以将rewrite参数存放在代表查询的JSON对象中
           rewrite参数的选项配置:
           --scoring_boolean :  该选项将每个生成的词项转化为布尔查询中的一个或从句。这种处理方法比较耗CPU(因为要计算和保存每个词项的得分),而且有些查询生成的词项太多从而超出了布尔查询的限制,默认为1024个从句。改写后的查询会保存计算出来的得分。默认的布尔查询限制可以通过设置elasticsearch.yml文件的Index.query.bool/max_clause_count属性来修改。但须谨记改写后的布尔查询从句越多,查询性能越低
           --constant_score_boolean:该选项与前面提到的scoring_boolean类似,但是CPU消耗较少,这是因为该过程并不计算每个从句的得分,而是每个从句得到一个与查询权重相同的常数得分,默认情况下等于1,当然我们也可以通过设置查询权重来改变这个默认值,与scoring_boolean类似,该选项也有布尔从句数的限制
           --constant_score_filter: 正如Lucene的javadoc描述的那样,该选项按如下方式改写原始查询:通过顺序遍历每个词项来创建一个私有的过滤器,标记跟每个词项相关的所有文档。命中的文档被赋予一个跟查询权重相关的常量得分。当命中词项数或文档数较大时,该方法比前两种执行速度更快
           --top_terms_N : 该选项将每个生成的词项转化为布尔查询中的一个从句,并保存计算出来的得分。与scoring_boolean不同之处在于,该方法只保留了最佳的前N个词项,从而避免超出布尔从句数的限制。
           --top_termd_boost_N: 该选项与top_terms_N类似,不同之处在于该选项产生的从句类型为常量得分查询,得分为从句权重。

      如何决定何时采用何种查询改写方法?
           问题的答案更多取决于应用场景,如果你能接受低精度(往往伴随着高性能),那么可以采用topN查询改写方法。如果你需要更高的查询精度(往往伴随着低性能),那么应该使用布尔方法

 二次评分

      改变查询返回文档的顺序其中的一个理由就是出于对性能的考虑,例如,在整个文档集上计算文档顺序是非常耗时的,而在原始查询的返回文档子集上做这种计算则非常省事。
      Elasticsearch中的二次评分指的是重新计算查询返回文档中指定个数文档的得分。着意味着Elasticsearch会截取查询返回文档的前N个,并使用预定义的二次评分来重新计算它们的得分
      二次评分参数配置:
           在rescore对象中的查询对象中,必须配置下面这些参数:
      1.window_size : 窗口大小,该参数默认设置为from和size参数值之和。它提供了之前提到的N个文档的相关消息。该参数指定了每个分片上参与二次评分的文档个数
      2.query_weight : 查询权重值。默认等于1,原始查询的得分与二次评分的得分相加之前将乘以该值
      3.rescore_query_weight : 二次评分查询的权重值,默认等于1,二次评分查询的得分在与原始查询得分相加之前将乘以该值。
      4.rescore_mode: 二次评分的模式,默认设置为total,elasticsearch0.90.3引入该参数,它定义了二次评分中该文档得分的计算方式,可用的选项有total、max、min、avg、multiply。
            当我们设置为total时,文档得分为原始查询得分与二次评分得分之和
            当我们设置为max时,文档得分为原始查询得分与二次评分得分之和
            当我们设置为min时,文档得分为两次查询得分中的最小值
            当我们设置为avg使,文档得分为两次查询得分中的平均值
            当我们设置为multiply时,文档得分为两次查询得分的乘积  

底层索引控制

     改变Apache Lucene 的评分方式
               自2012年Apache Lucene4.0发布以后,用户便可以改变默认的基于TF/IDF的评分算法了,这是因为Lucene的API做了一些改变,Lucene4.0提供了更多的相似度模型,从而允许我们采用不同的评分方式
           可用的相似度模型:
           1.Okapi BM25模型:这是一种基于概率模型的相似度模型,可用于估算文档 与给定查询匹配的概率。为了在Elasticsearch中使用它,你需要使用该模型的名字,BM25,一般来说,Okapi BM25模型在短文本文档上的效果最好,因为这种场景中重复词项对文档的总体得分损害较大。
           2.随机偏离模型:这是一种基于同名概率模型的相似度模型,为了在elasticsearch中使用它,你需要使用该模型的名字,DFR,一般来说随机偏离模型在类似自然语言的文本上效果更好
           3.基于信息的模型: 为了在elasticsearch中使用它,你需要使用该模型的名字,IB。同样,IB模型也在类似自然语言的文本上拥有姣好的效果

Elasticsearch Java API

       Elasticsearch Java API简介
              与RESTful API相比,Java API的集成性可可移植性较差,即必须使用基于JVM的编程语言。除此之外,RESTful API和Java API还有如下差异:RESTful API可以连接不同版本的ELasticsearch集群,前提是你很清楚不同版本下REST终端之间的差异以及Elasticsearch响应的异同。 忽略上面的问题,在java世界中,Elasticsearch官方提供的Java API库是一个开箱即用的解决方案,我们直接在项目中加入额外的JAR包就可以跟Elasticsearch通信了,不需要准备模板化的JSON代码来连接HTTP层。API库提供了两种方式来连接Elasticsearch:建立一个本地节点来加入集群,或者使用传输机。。就可能性来说,Java API可以比RESTful API 做的更多,从增删文档、读取文档到查询、批量导入、获取统计信息和集群状态、使用管理工具,凡是RESTful API 能做的Java API都能做。某些功能甚至是RESTful API中不具备的,如合并多个操作为一个批量操作。事实上,Java API和RESTful API在Elasticsearch内部底层实现代码是相同的。
           使用Java API连接到Elasticsearch集群的方法有两种,这两种方法都会用到一个Client接口的恰当实例,Client接口是ELasticsearch API对外提供的各种功能的主入口
          一、 第一种方式的思路就是把应用程序当成Elasticsearch集群中的一个节点,并和其他节点一样对待,例如下面的代码片段创建了一个客户端实例;
                Node node = nodeBuilder().clusterName("escluster2").client(true).node();
                Client client = node.client();
                我们使用NodeBuilder类创建了一个节点,其中NodeBuilder可根据需要配置并建立节点。集群名称很重要,我们可以通过clusterName()方法指定它。如果没有提供集群名称,则NodeBuilder会使用默认名称(elasticsearch),这时你可能会连接到一个你不希望连接到的集群(如果网络上存在一个名为elasticsearch的集群且你的客户端能够发现它)。另一个要点是client()方法,该方法可以把最终创建的节点指定为客户端节点,而客户端节点不会持有数据,这一点请务必谨慎对待,如果忽略了可能会引发各种奇怪的问题,也可能会导致数据的丢失,所以,请务必使用client(true)方法。
           二、使用传输机连接方式,本方法允许只连接到集群而不会加入集群。客户端可以同时连接到多个节点,并通过轮询调度的方式使用它们。这意味着每个请求将被发送到不同的节点上,并由收到请求的节点负责把请求转发到合适的节点。代码片段、
                Settings settings = ImmutableSettings.settingsBuilder().put("cluster.name","escluster2").build();
                TransportCLient client = new TransportClient(settings);
                client.addTransportAddress(new InetSocketTransportAddress("127.0.0.1",9300));
                这个方法很简单,首先我们使用ImmutableSettings类的静态方法settingsBuilder()来建立合适的配置对象,如本例中我们只需设置好集群名称,接着我们使用这个配置来建立一个TransportClient的客户端对象,然后把待连接节点的网络地址添加给它。这可以通过调用TransportClient类的addTransportAddress方法并传递InetSocketTransportAddress对象来实现。为创建INetSocketTransportAddress对象我们需要提供Elasticsearch节点所在及其的ip和节点传输层监听的口号

      CRUD操作

           ①读取文档:
                     GetResponse response = client.prepareGet("library","book","1").setFields("title","_source").execute().actionGet()
                在准备过程中,设置了索引名称,类型名称和文档ID后,我们会得到一个建造器对象。该构造器对象是org.elasticsearch.action.get.GetRequestBuilder的一个实例
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值