Lucene5学习之评分Scoring

简介:

  评分机制是Lucene的核心部分之一。Lucene默认是按照评分机制对每个Document进行打分,然后在返回结果中按照得分进行降序排序。内部的打分机制是通过Query,Weight,Scorer,Similarity这几个协作完成的。想要根据自己的业务对默认的评分机制进行干预来影响最终的索引文档的评分,那你必须首先对Lucene的评分公式要了解:

     coord(q,d):这里q即query,d即document,表示指定查询项在document中出现的频率,频率越大说明该document匹配度越大,评分就越高,默认实现是:

 

Java代码   收藏代码
  1. /** Implemented as <code>overlap / maxOverlap</code>. */  
  2.   @Override  
  3.   public float coord(int overlap, int maxOverlap) {  
  4.     return overlap / (float)maxOverlap;  
  5.   }  

    queryNorm(q):用来计算每个查询的权重的,从它的参数只有一个q就知道,它只是用来衡量每个查询的权重的,使每个Query之间也可以比较,注意:它的计算结果会影响最终document的得分值,但它不会影响每个文档的得分排序,因为每个document都会应用这个query权重值。默认它的实现数学公式是这样的:

    queryNorm实现代码在DefaultSimilarity类中:

Java代码   收藏代码
  1. /** Implemented as <code>1/sqrt(sumOfSquaredWeights)</code>. */  
  2.   @Override  
  3.   public float queryNorm(float sumOfSquaredWeights) {  
  4.     return (float)(1.0 / Math.sqrt(sumOfSquaredWeights));  
  5.   }  

    tf(t,d):用来统计指定Term t在document  d中的出现频率,出现次数越多说明匹配度越高,得分自然就越高,默认实现是

Java代码   收藏代码
  1. /** Implemented as <code>sqrt(freq)</code>. */  
  2.   @Override  
  3.   public float tf(float freq) {  
  4.     return (float)Math.sqrt(freq);  
  5.   }  

   idf(t):统计出现Term t的document的频率docFreq,docFreq越小,idf越大,则得分越高(一个Term若只在几个document中出现,说明这几个document稀有,物以稀为贵,所以你懂的)。

 

   t.getBoot():就是给Term设置权重值,比如使用QueryParser语法表达式时可以这样:Java^1.2

 

   norm(t,d):主要分两部分:一部分是Document的权重,不过在Lucene5中Document的权重已经被取消了

   一部分是Field的boot,刚才说过了,一部分是field中分词器分出来的Token个数因素,个数越多,匹配度越低,就好比你在1000000个字符中匹配到一个关键字和在10个字符中匹配到一个关键字,lucene认为后者权重更大应该排在前面。

   上面各个子函数计算出来的分值再相乘求积得到最终得分。

    

   

    演示域权重对评分的影响:

Java代码   收藏代码
  1. package com.yida.framework.lucene5.score;  
  2.   
  3. import java.io.IOException;  
  4.   
  5. import org.apache.lucene.analysis.Analyzer;  
  6. import org.apache.lucene.document.Document;  
  7. import org.apache.lucene.document.Field;  
  8. import org.apache.lucene.document.Field.Store;  
  9. import org.apache.lucene.document.TextField;  
  10. import org.apache.lucene.index.DirectoryReader;  
  11. import org.apache.lucene.index.IndexReader;  
  12. import org.apache.lucene.index.IndexWriter;  
  13. import org.apache.lucene.index.IndexWriterConfig;  
  14. import org.apache.lucene.index.IndexWriterConfig.OpenMode;  
  15. import org.apache.lucene.index.Term;  
  16. import org.apache.lucene.search.IndexSearcher;  
  17. import org.apache.lucene.search.Query;  
  18. import org.apache.lucene.search.ScoreDoc;  
  19. import org.apache.lucene.search.TermQuery;  
  20. import org.apache.lucene.search.TopDocs;  
  21. import org.apache.lucene.store.RAMDirectory;  
  22. import org.wltea.analyzer.lucene.IKAnalyzer;  
  23. /** 
  24.  * 为域设置权重从而影响索引文档的最终评分[为Document设置权重的API已经被废弃了] 
  25.  * @author Lanxiaowei 
  26.  * 
  27.  */  
  28. public class FieldBootTest {  
  29.     public static void main(String[] args) throws IOException {  
  30.         RAMDirectory directory = new RAMDirectory();  
  31.         Analyzer analyzer = new IKAnalyzer();  
  32.         IndexWriterConfig config = new IndexWriterConfig(analyzer);  
  33.         config.setOpenMode(OpenMode.CREATE_OR_APPEND);  
  34.         IndexWriter writer = new IndexWriter(directory, config);  
  35.         Document doc1 = new Document();  
  36.         Field f1 = new TextField("title""Java, hello world!",Store.YES);  
  37.         doc1.add(f1);  
  38.         writer.addDocument(doc1);  
  39.   
  40.         Document doc2 = new Document();  
  41.         Field f2 = new TextField("title""Java ,I like it.",Store.YES);  
  42.         //第二个文档的title域权重  
  43.         f2.setBoost(100);  
  44.         doc2.add(f2);  
  45.         writer.addDocument(doc2);  
  46.         writer.close();  
  47.           
  48.           
  49.         IndexReader reader = DirectoryReader.open(directory);  
  50.         IndexSearcher searcher = new IndexSearcher(reader);  
  51.         Query query = new TermQuery(new Term("title","java"));  
  52.         TopDocs topDocs = searcher.search(query, Integer.MAX_VALUE);  
  53.         ScoreDoc[] docs = topDocs.scoreDocs;  
  54.         if(null == docs || docs.length == 0) {  
  55.             System.out.println("No results for this query.");  
  56.             return;  
  57.         }  
  58.         for (ScoreDoc scoreDoc : docs) {  
  59.             int docID = scoreDoc.doc;  
  60.             float score = scoreDoc.score;  
  61.             Document document = searcher.doc(docID);  
  62.             String title = document.get("title");  
  63.             System.out.println("docId:" + docID);  
  64.             System.out.println("title:" + title);  
  65.             System.out.println("score:" + score);  
  66.             System.out.println("\n");  
  67.         }  
  68.         reader.close();  
  69.         directory.close();  
  70.     }  
  71. }  

   

    测试域值长度对评分的影响:

    

Java代码   收藏代码
  1. package com.yida.framework.lucene5.score;  
  2.   
  3. import java.io.IOException;  
  4.   
  5. import org.apache.lucene.analysis.Analyzer;  
  6. import org.apache.lucene.document.Document;  
  7. import org.apache.lucene.document.Field;  
  8. import org.apache.lucene.index.DirectoryReader;  
  9. import org.apache.lucene.index.IndexReader;  
  10. import org.apache.lucene.index.IndexWriter;  
  11. import org.apache.lucene.index.IndexWriterConfig;  
  12. import org.apache.lucene.index.IndexWriterConfig.OpenMode;  
  13. import org.apache.lucene.index.Term;  
  14. import org.apache.lucene.search.IndexSearcher;  
  15. import org.apache.lucene.search.Query;  
  16. import org.apache.lucene.search.ScoreDoc;  
  17. import org.apache.lucene.search.TermQuery;  
  18. import org.apache.lucene.search.TopDocs;  
  19. import org.apache.lucene.store.RAMDirectory;  
  20. import org.wltea.analyzer.lucene.IKAnalyzer;  
  21.   
  22. /** 
  23.  * 测试域值长度对评分的影响 
  24.  * @author Lanxioawei 
  25.  * 
  26.  */  
  27. public class FileValueLengthBootTest {  
  28.     public static void main(String[] args) throws IOException {  
  29.         RAMDirectory directory = new RAMDirectory();  
  30.         Analyzer analyzer = new IKAnalyzer();  
  31.         IndexWriterConfig config = new IndexWriterConfig(analyzer);  
  32.         config.setOpenMode(OpenMode.CREATE_OR_APPEND);  
  33.         IndexWriter writer = new IndexWriter(directory, config);  
  34.         Document doc1 = new Document();  
  35.         //Field f1 = new Field("title", "Java, hello world!", Field.Store.YES, Field.Index.ANALYZED_NO_NORMS);  
  36.         Field f1 = new Field("title""Java, hello world!", Field.Store.YES, Field.Index.ANALYZED);  
  37.         doc1.add(f1);  
  38.         writer.addDocument(doc1);  
  39.   
  40.         Document doc2 = new Document();  
  41.         //Field.Index.ANALYZED_NO_NORMS表示禁用Norms  
  42.         //Field f2 = new Field("title", "Hello hello hello hello hello Java Java.", Field.Store.YES, Field.Index.ANALYZED_NO_NORMS);  
  43.         Field f2 = new Field("title""Hello hello hello hello hello Java Java.", Field.Store.YES, Field.Index.ANALYZED);  
  44.         doc2.add(f2);  
  45.         writer.addDocument(doc2);  
  46.         writer.close();  
  47.           
  48.         //因为第二个索引文档的title域值比第一个的Term个数要多,所以第二个索引文档评分比第一个低  
  49.         //但如果禁用Norms,不考虑索引域值的长度因素,因为第二个文档匹配到了两个Term,所以评分较高  
  50.           
  51.         IndexReader reader = DirectoryReader.open(directory);  
  52.         IndexSearcher searcher = new IndexSearcher(reader);  
  53.         Query query = new TermQuery(new Term("title","java"));  
  54.         TopDocs topDocs = searcher.search(query, Integer.MAX_VALUE);  
  55.         ScoreDoc[] docs = topDocs.scoreDocs;  
  56.         if(null == docs || docs.length == 0) {  
  57.             System.out.println("No results for this query.");  
  58.             return;  
  59.         }  
  60.         for (ScoreDoc scoreDoc : docs) {  
  61.             int docID = scoreDoc.doc;  
  62.             float score = scoreDoc.score;  
  63.             Document document = searcher.doc(docID);  
  64.             String title = document.get("title");  
  65.             System.out.println("docId:" + docID);  
  66.             System.out.println("title:" + title);  
  67.             System.out.println("score:" + score);  
  68.             System.out.println("\n");  
  69.         }  
  70.         reader.close();  
  71.         directory.close();  
  72.     }  
  73. }  

 

 

    设置Term权重对评分的影响:

Java代码   收藏代码
  1. package com.yida.framework.lucene5.score;  
  2.   
  3. import java.io.IOException;  
  4.   
  5. import org.apache.lucene.analysis.Analyzer;  
  6. import org.apache.lucene.document.Document;  
  7. import org.apache.lucene.document.Field;  
  8. import org.apache.lucene.document.Field.Store;  
  9. import org.apache.lucene.document.TextField;  
  10. import org.apache.lucene.index.DirectoryReader;  
  11. import org.apache.lucene.index.IndexReader;  
  12. import org.apache.lucene.index.IndexWriter;  
  13. import org.apache.lucene.index.IndexWriterConfig;  
  14. import org.apache.lucene.index.IndexWriterConfig.OpenMode;  
  15. import org.apache.lucene.queryparser.classic.ParseException;  
  16. import org.apache.lucene.queryparser.classic.QueryParser;  
  17. import org.apache.lucene.search.IndexSearcher;  
  18. import org.apache.lucene.search.Query;  
  19. import org.apache.lucene.search.ScoreDoc;  
  20. import org.apache.lucene.search.TopDocs;  
  21. import org.apache.lucene.store.RAMDirectory;  
  22. import org.wltea.analyzer.lucene.IKAnalyzer;  
  23.   
  24. /** 
  25.  * Term权重对评分的影响测试 
  26.  * @author Lanxiaowei 
  27.  * 
  28.  */  
  29. public class QueryBootTest {  
  30.     public static void main(String[] args) throws IOException, ParseException {  
  31.         RAMDirectory directory = new RAMDirectory();  
  32.         Analyzer analyzer = new IKAnalyzer();  
  33.         IndexWriterConfig config = new IndexWriterConfig(analyzer);  
  34.         config.setOpenMode(OpenMode.CREATE_OR_APPEND);  
  35.         IndexWriter writer = new IndexWriter(directory, config);  
  36.         Document doc1 = new Document();  
  37.         Field f1 = new TextField("title""Java, hello hello!",Store.YES);  
  38.         doc1.add(f1);  
  39.         writer.addDocument(doc1);  
  40.   
  41.         Document doc2 = new Document();  
  42.         Field f2 = new TextField("title""Python Python Python hello.",Store.YES);  
  43.         doc2.add(f2);  
  44.         writer.addDocument(doc2);  
  45.         writer.close();  
  46.           
  47.           
  48.         IndexReader reader = DirectoryReader.open(directory);  
  49.         IndexSearcher searcher = new IndexSearcher(reader);  
  50.         QueryParser parser = new QueryParser("title",analyzer);  
  51.         //Query query = parser.parse("java hello");  
  52.           
  53.         //不设置权重之前,Python出现了3次,所以文档2的评分较高  
  54.         //增加java关键字的权重,使文档1的评分大于文档2  
  55.         Query query = parser.parse("java^100 Python");  
  56.         TopDocs topDocs = searcher.search(query, Integer.MAX_VALUE);  
  57.         ScoreDoc[] docs = topDocs.scoreDocs;  
  58.         if(null == docs || docs.length == 0) {  
  59.             System.out.println("No results for this query.");  
  60.             return;  
  61.         }  
  62.         for (ScoreDoc scoreDoc : docs) {  
  63.             int docID = scoreDoc.doc;  
  64.             float score = scoreDoc.score;  
  65.             Document document = searcher.doc(docID);  
  66.             String title = document.get("title");  
  67.             System.out.println("docId:" + docID);  
  68.             System.out.println("title:" + title);  
  69.             System.out.println("score:" + score);  
  70.             System.out.println("\n");  
  71.         }  
  72.         reader.close();  
  73.         directory.close();  
  74.     }  
  75. }  

 

    自定义Similarity来重写上述几个子函数的实现,从而更细粒度的干预评分:

Java代码   收藏代码
  1. package com.yida.framework.lucene5.score;  
  2.   
  3. import org.apache.lucene.search.similarities.DefaultSimilarity;  
  4.   
  5. public class CustomSimilarity extends DefaultSimilarity {  
  6.     @Override  
  7.     public float idf(long docFreq, long numDocs) {  
  8.         //docFreq表示某个Term在哪几个文档中出现过,numDocs表示总的文档数  
  9.         System.out.println("docFreq:" + docFreq);  
  10.         System.out.println("numDocs:" + numDocs);  
  11.         return super.idf(docFreq, numDocs);  
  12.     }  
  13. }  

    继承DefaultSimilarity类,重写里面的相关函数即可,来看看DefaultSimilarity的源码:

Java代码   收藏代码
  1. package org.apache.lucene.search.similarities;  
  2.   
  3. /* 
  4.  * Licensed to the Apache Software Foundation (ASF) under one or more 
  5.  * contributor license agreements.  See the NOTICE file distributed with 
  6.  * this work for additional information regarding copyright ownership. 
  7.  * The ASF licenses this file to You under the Apache License, Version 2.0 
  8.  * (the "License"); you may not use this file except in compliance with 
  9.  * the License.  You may obtain a copy of the License at 
  10.  * 
  11.  *     http://www.apache.org/licenses/LICENSE-2.0 
  12.  * 
  13.  * Unless required by applicable law or agreed to in writing, software 
  14.  * distributed under the License is distributed on an "AS IS" BASIS, 
  15.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  16.  * See the License for the specific language governing permissions and 
  17.  * limitations under the License. 
  18.  */  
  19.   
  20. import org.apache.lucene.index.FieldInvertState;  
  21. import org.apache.lucene.util.BytesRef;  
  22. import org.apache.lucene.util.SmallFloat;  
  23.   
  24. /** 
  25.  * Expert: Default scoring implementation which {@link #encodeNormValue(float) 
  26.  * encodes} norm values as a single byte before being stored. At search time, 
  27.  * the norm byte value is read from the index 
  28.  * {@link org.apache.lucene.store.Directory directory} and 
  29.  * {@link #decodeNormValue(long) decoded} back to a float <i>norm</i> value. 
  30.  * This encoding/decoding, while reducing index size, comes with the price of 
  31.  * precision loss - it is not guaranteed that <i>decode(encode(x)) = x</i>. For 
  32.  * instance, <i>decode(encode(0.89)) = 0.75</i>. 
  33.  * <p> 
  34.  * Compression of norm values to a single byte saves memory at search time, 
  35.  * because once a field is referenced at search time, its norms - for all 
  36.  * documents - are maintained in memory. 
  37.  * <p> 
  38.  * The rationale supporting such lossy compression of norm values is that given 
  39.  * the difficulty (and inaccuracy) of users to express their true information 
  40.  * need by a query, only big differences matter. <br> 
  41.  * &nbsp;<br> 
  42.  * Last, note that search time is too late to modify this <i>norm</i> part of 
  43.  * scoring, e.g. by using a different {@link Similarity} for search. 
  44.  */  
  45. public class DefaultSimilarity extends TFIDFSimilarity {  
  46.     
  47.   /** Cache of decoded bytes. */  
  48.   private static final float[] NORM_TABLE = new float[256];  
  49.   
  50.   static {  
  51.     for (int i = 0; i < 256; i++) {  
  52.       NORM_TABLE[i] = SmallFloat.byte315ToFloat((byte)i);  
  53.     }  
  54.   }  
  55.   
  56.   /** Sole constructor: parameter-free */  
  57.   public DefaultSimilarity() {}  
  58.     
  59.   /** Implemented as <code>overlap / maxOverlap</code>. */  
  60.   @Override  
  61.   public float coord(int overlap, int maxOverlap) {  
  62.     return overlap / (float)maxOverlap;  
  63.   }  
  64.   
  65.   /** Implemented as <code>1/sqrt(sumOfSquaredWeights)</code>. */  
  66.   @Override  
  67.   public float queryNorm(float sumOfSquaredWeights) {  
  68.     return (float)(1.0 / Math.sqrt(sumOfSquaredWeights));  
  69.   }  
  70.     
  71.   /** 
  72.    * Encodes a normalization factor for storage in an index. 
  73.    * <p> 
  74.    * The encoding uses a three-bit mantissa, a five-bit exponent, and the 
  75.    * zero-exponent point at 15, thus representing values from around 7x10^9 to 
  76.    * 2x10^-9 with about one significant decimal digit of accuracy. Zero is also 
  77.    * represented. Negative numbers are rounded up to zero. Values too large to 
  78.    * represent are rounded down to the largest representable value. Positive 
  79.    * values too small to represent are rounded up to the smallest positive 
  80.    * representable value. 
  81.    *  
  82.    * @see org.apache.lucene.document.Field#setBoost(float) 
  83.    * @see org.apache.lucene.util.SmallFloat 
  84.    */  
  85.   @Override  
  86.   public final long encodeNormValue(float f) {  
  87.     return SmallFloat.floatToByte315(f);  
  88.   }  
  89.   
  90.   /** 
  91.    * Decodes the norm value, assuming it is a single byte. 
  92.    *  
  93.    * @see #encodeNormValue(float) 
  94.    */  
  95.   @Override  
  96.   public final float decodeNormValue(long norm) {  
  97.     return NORM_TABLE[(int) (norm & 0xFF)];  // & 0xFF maps negative bytes to positive above 127  
  98.   }  
  99.   
  100.   /** Implemented as 
  101.    *  <code>state.getBoost()*lengthNorm(numTerms)</code>, where 
  102.    *  <code>numTerms</code> is {@link FieldInvertState#getLength()} if {@link 
  103.    *  #setDiscountOverlaps} is false, else it's {@link 
  104.    *  FieldInvertState#getLength()} - {@link 
  105.    *  FieldInvertState#getNumOverlap()}. 
  106.    * 
  107.    *  @lucene.experimental */  
  108.   @Override  
  109.   public float lengthNorm(FieldInvertState state) {  
  110.     final int numTerms;  
  111.     if (discountOverlaps)  
  112.       numTerms = state.getLength() - state.getNumOverlap();  
  113.     else  
  114.       numTerms = state.getLength();  
  115.     return state.getBoost() * ((float) (1.0 / Math.sqrt(numTerms)));  
  116.   }  
  117.   
  118.   /** Implemented as <code>sqrt(freq)</code>. */  
  119.   @Override  
  120.   public float tf(float freq) {  
  121.     return (float)Math.sqrt(freq);  
  122.   }  
  123.       
  124.   /** Implemented as <code>1 / (distance + 1)</code>. */  
  125.   @Override  
  126.   public float sloppyFreq(int distance) {  
  127.     return 1.0f / (distance + 1);  
  128.   }  
  129.     
  130.   /** The default implementation returns <code>1</code> */  
  131.   @Override  
  132.   public float scorePayload(int doc, int start, int end, BytesRef payload) {  
  133.     return 1;  
  134.   }  
  135.   
  136.   /** Implemented as <code>log(numDocs/(docFreq+1)) + 1</code>. */  
  137.   @Override  
  138.   public float idf(long docFreq, long numDocs) {  
  139.     return (float)(Math.log(numDocs/(double)(docFreq+1)) + 1.0);  
  140.   }  
  141.       
  142.   /**  
  143.    * True if overlap tokens (tokens with a position of increment of zero) are 
  144.    * discounted from the document's length. 
  145.    */  
  146.   protected boolean discountOverlaps = true;  
  147.   
  148.   /** Determines whether overlap tokens (Tokens with 
  149.    *  0 position increment) are ignored when computing 
  150.    *  norm.  By default this is true, meaning overlap 
  151.    *  tokens do not count when computing norms. 
  152.    * 
  153.    *  @lucene.experimental 
  154.    * 
  155.    *  @see #computeNorm 
  156.    */  
  157.   public void setDiscountOverlaps(boolean v) {  
  158.     discountOverlaps = v;  
  159.   }  
  160.   
  161.   /** 
  162.    * Returns true if overlap tokens are discounted from the document's length.  
  163.    * @see #setDiscountOverlaps  
  164.    */  
  165.   public boolean getDiscountOverlaps() {  
  166.     return discountOverlaps;  
  167.   }  
  168.   
  169.   @Override  
  170.   public String toString() {  
  171.     return "DefaultSimilarity";  
  172.   }  
  173. }  

    看到里面的idf,tf,coord等函数,你们应该已经知道他们的作用了,你只要重写他们实现自己的统计算法即可。

    

    细心的你,应该会发现里面还有个scorePayload,计算payload分值。那什么是payload呢?Payload

其实在Lucene2.x时代就有了,它跟位置索引,位置增量作用类似,就是为Document提供一些额外的信息,实现一些特殊的功能,比如位置索引用来实现PhraseQuery短语查询以及关键字高亮功能。Payload可以用来实现更加灵活的索引技术,为了更加形象点,有助于你们理解payload,请认真仔细观摩看懂这张图:

 

比如你有这样两个文档:

    what is your trouble?

    What the fucking up?

    第二个what加粗了,你想加大它的权重,你可以这样实现:
   <b>What></b> the fucking up?

   然后分词的时候把<b>What></b>当作一个Term,然后判断如果有<b></b>标记就记录下是否有加粗信息存入payload。但注意要想把<b>XXXXXX></b>当一个整体,需要自定义分词器,因为<>这些符号会被剔除掉。不一定是在Term两头加<b></b>来附带额外信息,你也可以term{1.2}如java{1.2}表示把这个Term权重乘以1.2,我只是举个例子,关键是你要把term{1.2}能通过分词器当作一个整体给分出来,默认{}会被剔除,分词才是重点。

 

 

   下面是一个简单的Payload示例,直接上代码了:

Java代码   收藏代码
  1. package com.yida.framework.lucene5.score.payload;  
  2.   
  3. import java.io.IOException;  
  4.   
  5. import org.apache.lucene.analysis.TokenFilter;  
  6. import org.apache.lucene.analysis.TokenStream;  
  7. import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;  
  8. import org.apache.lucene.analysis.tokenattributes.PayloadAttribute;  
  9. import org.apache.lucene.util.BytesRef;  
  10.   
  11. import com.yida.framework.lucene5.util.Tools;  
  12.   
  13. public class BoldFilter extends TokenFilter {  
  14.     public static int IS_NOT_BOLD = 0;  
  15.     public static int IS_BOLD = 1;  
  16.     private CharTermAttribute termAtt;  
  17.     private PayloadAttribute payloadAtt;  
  18.   
  19.     protected BoldFilter(TokenStream input) {  
  20.         super(input);  
  21.         termAtt = addAttribute(CharTermAttribute.class);  
  22.         payloadAtt = addAttribute(PayloadAttribute.class);  
  23.     }  
  24.   
  25.     @Override  
  26.     public boolean incrementToken() throws IOException {  
  27.         if (input.incrementToken()) {  
  28.             final char[] buffer = termAtt.buffer();  
  29.             final int length = termAtt.length();  
  30.             String tokenstring = new String(buffer, 0, length).toLowerCase();  
  31.             //System.out.println("token:" + tokenstring);  
  32.             if (tokenstring.startsWith("<b>") && tokenstring.endsWith("</b>")) {  
  33.                 tokenstring = tokenstring.replace("<b>""");  
  34.                 tokenstring = tokenstring.replace("</b>""");  
  35.                 termAtt.copyBuffer(tokenstring.toCharArray(), 0, tokenstring.length());  
  36.                 //在分词阶段,设置payload信息  
  37.                 payloadAtt.setPayload(new BytesRef(Tools.int2bytes(IS_BOLD)));  
  38.             } else {  
  39.                 payloadAtt.setPayload(new BytesRef(Tools.int2bytes(IS_NOT_BOLD)));  
  40.             }  
  41.             return true;  
  42.         } else  
  43.             return false;  
  44.     }  
  45. }  

    

Java代码   收藏代码
  1. package com.yida.framework.lucene5.score.payload;  
  2.   
  3. import org.apache.lucene.analysis.Analyzer;  
  4. import org.apache.lucene.analysis.TokenStream;  
  5. import org.apache.lucene.analysis.Tokenizer;  
  6. import org.apache.lucene.analysis.core.LowerCaseFilter;  
  7. import org.apache.lucene.analysis.core.StopAnalyzer;  
  8. import org.apache.lucene.analysis.core.StopFilter;  
  9. import org.apache.lucene.analysis.core.WhitespaceTokenizer;  
  10.   
  11. public class BoldAnalyzer extends Analyzer {  
  12.     @Override  
  13.     protected TokenStreamComponents createComponents(String fieldName) {  
  14.         Tokenizer tokenizer = new WhitespaceTokenizer();  
  15.         TokenStream tokenStream = new BoldFilter(tokenizer);  
  16.         tokenStream = new LowerCaseFilter(tokenStream);  
  17.         tokenStream = new StopFilter(tokenStream,StopAnalyzer.ENGLISH_STOP_WORDS_SET);  
  18.         return new TokenStreamComponents(tokenizer, tokenStream);  
  19.     }  
  20. }  

    

Java代码   收藏代码
  1. package com.yida.framework.lucene5.score.payload;  
  2.   
  3. import org.apache.lucene.search.similarities.DefaultSimilarity;  
  4. import org.apache.lucene.util.BytesRef;  
  5.   
  6. import com.yida.framework.lucene5.util.Tools;  
  7.   
  8. public class PayloadSimilarity extends DefaultSimilarity {  
  9.     @Override  
  10.     public float scorePayload(int doc, int start, int end, BytesRef payload) {  
  11.         int isbold = Tools.bytes2int(payload.bytes);  
  12.         if (isbold == BoldFilter.IS_BOLD) {  
  13.             return 100f;  
  14.         }  
  15.         return 1f;  
  16.     }  
  17. }  

    

Java代码   收藏代码
  1. package com.yida.framework.lucene5.score.payload;  
  2.   
  3. import java.io.IOException;  
  4.   
  5. import org.apache.lucene.analysis.Analyzer;  
  6. import org.apache.lucene.document.Document;  
  7. import org.apache.lucene.document.Field;  
  8. import org.apache.lucene.document.Field.Store;  
  9. import org.apache.lucene.document.TextField;  
  10. import org.apache.lucene.index.DirectoryReader;  
  11. import org.apache.lucene.index.IndexReader;  
  12. import org.apache.lucene.index.IndexWriter;  
  13. import org.apache.lucene.index.IndexWriterConfig;  
  14. import org.apache.lucene.index.IndexWriterConfig.OpenMode;  
  15. import org.apache.lucene.index.Term;  
  16. import org.apache.lucene.search.IndexSearcher;  
  17. import org.apache.lucene.search.Query;  
  18. import org.apache.lucene.search.ScoreDoc;  
  19. import org.apache.lucene.search.TopDocs;  
  20. import org.apache.lucene.search.payloads.MaxPayloadFunction;  
  21. import org.apache.lucene.search.payloads.PayloadNearQuery;  
  22. import org.apache.lucene.search.spans.SpanQuery;  
  23. import org.apache.lucene.search.spans.SpanTermQuery;  
  24. import org.apache.lucene.store.RAMDirectory;  
  25. /** 
  26.  * Payload测试 
  27.  * @author Lanxiaowei 
  28.  * 
  29.  */  
  30. public class PayloadTest {  
  31.     public static void main(String[] args) throws IOException {  
  32.         RAMDirectory directory = new RAMDirectory();  
  33.         //Analyzer analyzer = new IKAnalyzer();  
  34.         Analyzer analyzer = new BoldAnalyzer();  
  35.         IndexWriterConfig config = new IndexWriterConfig(analyzer);  
  36.         config.setOpenMode(OpenMode.CREATE_OR_APPEND);  
  37.         IndexWriter writer = new IndexWriter(directory, config);  
  38.         Document doc1 = new Document();  
  39.         Field f1 = new TextField("title""Java <B>hello</B> world",Store.YES);  
  40.         doc1.add(f1);  
  41.         writer.addDocument(doc1);  
  42.   
  43.         Document doc2 = new Document();  
  44.         Field f2 = new TextField("title""Java ,I like it.",Store.YES);  
  45.         doc2.add(f2);  
  46.         writer.addDocument(doc2);  
  47.         writer.close();  
  48.           
  49.           
  50.         IndexReader reader = DirectoryReader.open(directory);  
  51.         IndexSearcher searcher = new IndexSearcher(reader);  
  52.         searcher.setSimilarity(new PayloadSimilarity());  
  53.         SpanQuery queryStart = new SpanTermQuery(new Term("title","java"));  
  54.         SpanQuery queryEnd = new SpanTermQuery(new Term("title","hello"));  
  55.         Query query = new PayloadNearQuery(new SpanQuery[] {  
  56.                 queryStart,queryEnd},2,true,new MaxPayloadFunction());  
  57.         TopDocs topDocs = searcher.search(query, Integer.MAX_VALUE);  
  58.         ScoreDoc[] docs = topDocs.scoreDocs;  
  59.         if(null == docs || docs.length == 0) {  
  60.             System.out.println("No results for this query.");  
  61.             return;  
  62.         }  
  63.         for (ScoreDoc scoreDoc : docs) {  
  64.             int docID = scoreDoc.doc;  
  65.             float score = scoreDoc.score;  
  66.             Document document = searcher.doc(docID);  
  67.             String title = document.get("title");  
  68.             System.out.println("docId:" + docID);  
  69.             System.out.println("title:" + title);  
  70.             System.out.println("score:" + score);  
  71.             System.out.println("\n");  
  72.         }  
  73.         reader.close();  
  74.         directory.close();  
  75.     }  
  76. }  

    

    上述所有示例代码我都会上传到底下的附件里。好了,有关Lucene的评分机制就说这么多了,关键还是要看懂Lucene的那个评分公式,当然,如果你想要完全推倒Lucene的默认评分计算公式,实现一套自己的评分公式,那你恐怕要实现一套自己的Query,Weight,Scorer,Similarity.

    

        如果你还有什么问题请加我Q-Q:7-3-6-0-3-1-3-0-5,

或者加裙
一起交流学习!

转载:http://iamyida.iteye.com/blog/2200331

目录
相关文章
|
1月前
|
自然语言处理 搜索推荐 算法
基于Lucene的搜索引擎的设计与实现
基于Lucene的搜索引擎的设计与实现
11 0
|
9月前
|
存储 算法 API
Elasticsearch评分相关度算法解析
Elasticsearch评分相关度算法解析
105 0
|
6月前
|
自然语言处理 算法 Java
11Lucene相关度排序
11Lucene相关度排序
28 0
|
11月前
|
算法 Java
白话Elasticsearch24- 深度探秘搜索技术之TF&IDF算法/向量空间模型算法/lucene的相关度分数算法
白话Elasticsearch24- 深度探秘搜索技术之TF&IDF算法/向量空间模型算法/lucene的相关度分数算法
62 0
|
存储 缓存 自然语言处理
推荐系统[一]:超详细知识介绍,一份完整的入门指南,解答推荐系统相关算法流程、衡量指标和应用,以及如何使用jieba分词库进行相似推荐
推荐系统[一]:超详细知识介绍,一份完整的入门指南,解答推荐系统相关算法流程、衡量指标和应用,以及如何使用jieba分词库进行相似推荐
推荐系统[一]:超详细知识介绍,一份完整的入门指南,解答推荐系统相关算法流程、衡量指标和应用,以及如何使用jieba分词库进行相似推荐
|
Java API Apache
lucene 相关性参考
假期梳理了之前在新浪博客的文档,将一些有用的内容搬到这里。本文是lucene序列原理分享之一:相关性原理。
63 0
|
搜索推荐 算法 数据挖掘
【转】基于lucene实现自己的推荐引擎
假期重新把之前在新浪博客里面的文字梳理了下,搬到这里
116 0
|
机器学习/深度学习 搜索推荐 数据处理
这就是搜索引擎读书笔记-day3-5.检索模型与搜索排序
搜索结果排序融合了上百种排序因子,而重要两因素是:用户查询和网页内容相关性 及 网页链接情况。本节介绍内容相关性介绍网页排序
这就是搜索引擎读书笔记-day3-5.检索模型与搜索排序
|
自然语言处理 算法 索引

热门文章

最新文章