??xml version="1.0" encoding="utf-8" standalone="yes"?>日本国产精品,最新中文字幕在线视频,欧美精品一二三http://www.aygfsteel.com/ashutc/category/45558.html沈阳求职Qjava3q以上经验)Qashutc@126.comzh-cnFri, 15 Apr 2011 09:12:56 GMTFri, 15 Apr 2011 09:12:56 GMT60lucene评分分析http://www.aygfsteel.com/ashutc/archive/2011/04/15/348339.html西瓜西瓜Fri, 15 Apr 2011 03:02:00 GMThttp://www.aygfsteel.com/ashutc/archive/2011/04/15/348339.htmlhttp://www.aygfsteel.com/ashutc/comments/348339.htmlhttp://www.aygfsteel.com/ashutc/archive/2011/04/15/348339.html#Feedback0http://www.aygfsteel.com/ashutc/comments/commentRss/348339.htmlhttp://www.aygfsteel.com/ashutc/services/trackbacks/348339.html

在IndexSearchercM有一个管理Lucene得分情况的方法,如下所C:

public Explanation explain(Weight weight, int doc) throws IOException {
    return weight.explain(reader, doc);
}

q回的这个Explanation的实例解释了Lucene中Document的得分情c我们可以测试一下,直观地感觉一下到底这个Explanation的实例都记录了一个Document的哪些信息?/span>

写一个测试类Q如下所C:

package org.shirdrn.lucene.learn;

import java.io.IOException;
import java.util.Date;

import net.teamhot.lucene.ThesaurusAnalyzer;

import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.TermDocs;
import org.apache.lucene.search.Explanation;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.store.LockObtainFailedException;

public class AboutLuceneScore {

private String path = "E:\\Lucene\\index";

public void createIndex(){
   IndexWriter writer;
   try {
    writer = new IndexWriter(path,new ThesaurusAnalyzer(),true);
   
    Field fieldA = new Field("contents","一?,Field.Store.YES,Field.Index.TOKENIZED);
    Document docA = new Document();
    docA.add(fieldA);
   
    Field fieldB = new Field("contents","一?之交 一Z?,Field.Store.YES,Field.Index.TOKENIZED);
    Document docB = new Document();
    docB.add(fieldB);
   
    Field fieldC = new Field("contents","一?之下 一Z?,Field.Store.YES,Field.Index.TOKENIZED);
    Document docC = new Document();
    docC.add(fieldC);
   
    Field fieldD = new Field("contents","一?做事 一人当 一人做事一人当",Field.Store.YES,Field.Index.TOKENIZED);
    Document docD = new Document();
    docD.add(fieldD);
   
    Field fieldE = new Field("contents","一?做事 一人當 一人做事一人當",Field.Store.YES,Field.Index.TOKENIZED);
    Document docE = new Document();
    docE.add(fieldE);

    writer.addDocument(docA);
    writer.addDocument(docB);
    writer.addDocument(docC);
    writer.addDocument(docD);
    writer.addDocument(docE);
   
    writer.close();
   } catch (CorruptIndexException e) {
    e.printStackTrace();
   } catch (LockObtainFailedException e) {
    e.printStackTrace();
   } catch (IOException e) {
    e.printStackTrace();
   }
}

public static void main(String[] args) {
   AboutLuceneScore aus = new AboutLuceneScore();
   aus.createIndex();   
// 建立索引
   try {
    String keyword = "一?;
    Term term = new Term("contents",keyword);
    Query query = new TermQuery(term);
    IndexSearcher searcher = new IndexSearcher(aus.path);
    Date startTime = new Date();
    Hits hits = searcher.search(query);
    TermDocs termDocs = searcher.getIndexReader().termDocs(term);
    while(termDocs.next()){
     System.out.print("搜烦关键?lt;"+keyword+">在编号ؓ "+termDocs.doc());
     System.out.println(" 的Document中出现过 "+termDocs.freq()+" ?);
    }
    System.out.println("********************************************************************");
    for(int i=0;i<hits.length();i++){
     System.out.println("Document的内部编号ؓ Q?"+hits.id(i));
     System.out.println("Document内容?Q?"+hits.doc(i));
     System.out.println("Document得分?Q?"+hits.score(i));
     Explanation e = searcher.explain(query, hits.id(i));
     System.out.println("Explanation?Q?\n"+e);
     System.out.println("Document对应的Explanation的一些参数值如下: ");
     System.out.println("Explanation的getValue()?Q?"+e.getValue());
     System.out.println("Explanation的getDescription()?Q?"+e.getDescription());
     System.out.println("********************************************************************");
    }
    System.out.println("共检索出W合条g的Document "+hits.length()+" 个?);
    Date finishTime = new Date();
    long timeOfSearch = finishTime.getTime() - startTime.getTime();
    System.out.println("本次搜烦所用的旉?"+timeOfSearch+" ms");
   } catch (CorruptIndexException e) {
    e.printStackTrace();
   } catch (IOException e) {
    e.printStackTrace();
   }
  
}
}

该测试类中实C一个徏立烦引的ҎcreateIndex()ҎQ然后通过索一个关键字“一?#8221;Q获取到与它相关的Document的信息?/span>

打印出结果的W一部分为:q个索关键字“一?#8221;在每个Document中出现的ơ数?/span>

打印出结果的W二部分为:相关的Explanation及其得分情况的信息?/span>

试l果输出如下所C:

搜烦关键?lt;一?gt;在编号ؓ 0 的Document中出现过 1 ?br /> 搜烦关键?lt;一?gt;在编号ؓ 1 的Document中出现过 1 ?br /> 搜烦关键?lt;一?gt;在编号ؓ 2 的Document中出现过 1 ?br /> 搜烦关键?lt;一?gt;在编号ؓ 3 的Document中出现过 2 ?br /> 搜烦关键?lt;一?gt;在编号ؓ 4 的Document中出现过 2 ?br /> ********************************************************************
Document的内部编号ؓ Q?0
Document内容?Q?Document<stored/uncompressed,indexed,tokenized<contents:一?gt;>
Document得分?Q?0.81767845
Explanation?Q?
0.81767845 = (MATCH) fieldWeight(contents:一?in 0), product of:
1.0 = tf(termFreq(contents:一?=1)
0.81767845 = idf(docFreq=5)
1.0 = fieldNorm(field=contents, doc=0)

Document对应的Explanation的一些参数值如下:
Explanation的getValue()?Q?0.81767845
Explanation的getDescription()?Q?fieldWeight(contents:一?in 0), product of:
********************************************************************
Document的内部编号ؓ Q?3
Document内容?Q?Document<stored/uncompressed,indexed,tokenized<contents:一?做事 一人当 一人做事一人当>>
Document得分?Q?0.5059127
Explanation?Q?
0.5059127 = (MATCH) fieldWeight(contents:一?in 3), product of:
1.4142135 = tf(termFreq(contents:一?=2)
0.81767845 = idf(docFreq=5)
0.4375 = fieldNorm(field=contents, doc=3)

Document对应的Explanation的一些参数值如下:
Explanation的getValue()?Q?0.5059127
Explanation的getDescription()?Q?fieldWeight(contents:一?in 3), product of:
********************************************************************
Document的内部编号ؓ Q?4
Document内容?Q?Document<stored/uncompressed,indexed,tokenized<contents:一?做事 一人當 一人做事一人當>>
Document得分?Q?0.5059127
Explanation?Q?
0.5059127 = (MATCH) fieldWeight(contents:一?in 4), product of:
1.4142135 = tf(termFreq(contents:一?=2)
0.81767845 = idf(docFreq=5)
0.4375 = fieldNorm(field=contents, doc=4)

Document对应的Explanation的一些参数值如下:
Explanation的getValue()?Q?0.5059127
Explanation的getDescription()?Q?fieldWeight(contents:一?in 4), product of:
********************************************************************
Document的内部编号ؓ Q?1
Document内容?Q?Document<stored/uncompressed,indexed,tokenized<contents:一?之交 一Z?gt;>
Document得分?Q?0.40883923
Explanation?Q?
0.40883923 = (MATCH) fieldWeight(contents:一?in 1), product of:
1.0 = tf(termFreq(contents:一?=1)
0.81767845 = idf(docFreq=5)
0.5 = fieldNorm(field=contents, doc=1)

Document对应的Explanation的一些参数值如下:
Explanation的getValue()?Q?0.40883923
Explanation的getDescription()?Q?fieldWeight(contents:一?in 1), product of:
********************************************************************
Document的内部编号ؓ Q?2
Document内容?Q?Document<stored/uncompressed,indexed,tokenized<contents:一?之下 一Z?gt;>
Document得分?Q?0.40883923
Explanation?Q?
0.40883923 = (MATCH) fieldWeight(contents:一?in 2), product of:
1.0 = tf(termFreq(contents:一?=1)
0.81767845 = idf(docFreq=5)
0.5 = fieldNorm(field=contents, doc=2)

Document对应的Explanation的一些参数值如下:
Explanation的getValue()?Q?0.40883923
Explanation的getDescription()?Q?fieldWeight(contents:一?in 2), product of:
********************************************************************
共检索出W合条g的Document 5 个?br /> 本次搜烦所用的旉?79 ms

先从试的输出结果进行分析,可以获得到如下信息:

?试cMhits.score(i)的gExplanation的getValue()的值是一LQ即Lucene默认使用的得分;

?默认情况下,Lucene按照Document的得分进行排序检索结果;

?默认情况下,如果两个Document的得分相同,按照Document的内部编可行排序,比如上面~号?3?)?1?)是两l得分相同的DocumentQ结果排序时按照Document的编可行了排序Q?/span>

通过从IndexSearchercM的explainҎQ?/span>

public Explanation explain(Weight weight, int doc) throws IOException {
    return weight.explain(reader, doc);
}

可以看出Q实际上是调用了Weight接口cM的explain()ҎQ而Weight是与一个Query相关的,它记录了一ơ查询构造的Query的情况,从而保证一个Query实例可以重用?/span>

具体圎ͼ可以在实现Weight接口的具体类TermWeight中追溯到explain()ҎQ而TermWeightcL一个内部类Q定义在TermQuerycd部。TermWeightcȝexplain()Ҏ如下所C:

    public Explanation explain(IndexReader reader, int doc)
      throws IOException {

      ComplexExplanation result = new ComplexExplanation();
      result.setDescription("weight("+getQuery()+" in "+doc+"), product of:");

      Explanation idfExpl = new Explanation(idf, "idf(docFreq=" + reader.docFreq(term) + ")");

      // explain query weight
      Explanation queryExpl = new Explanation();
      queryExpl.setDescription("queryWeight(" + getQuery() + "), product of:");

      Explanation boostExpl = new Explanation(getBoost(), "boost");
      if (getBoost() != 1.0f)
        queryExpl.addDetail(boostExpl);
      queryExpl.addDetail(idfExpl);

      Explanation queryNormExpl = new Explanation(queryNorm,"queryNorm");
      queryExpl.addDetail(queryNormExpl);

      queryExpl.setValue(boostExpl.getValue() *idfExpl.getValue() *queryNormExpl.getValue());

      result.addDetail(queryExpl);

      // 说明Field的权?br />       String field = term.field();
      ComplexExplanation fieldExpl = new ComplexExplanation();
      fieldExpl.setDescription("fieldWeight("+term+" in "+doc+"), product of:");

      Explanation tfExpl = scorer(reader).explain(doc);
      fieldExpl.addDetail(tfExpl);
      fieldExpl.addDetail(idfExpl);

      Explanation fieldNormExpl = new Explanation();
      byte[] fieldNorms = reader.norms(field);
      float fieldNorm =
        fieldNorms!=null ? Similarity.decodeNorm(fieldNorms[doc]) : 0.0f;
      fieldNormExpl.setValue(fieldNorm);
      fieldNormExpl.setDescription("fieldNorm(field="+field+", doc="+doc+")");
      fieldExpl.addDetail(fieldNormExpl);
     
      fieldExpl.setMatch(Boolean.valueOf(tfExpl.isMatch()));
      fieldExpl.setValue(tfExpl.getValue() *idfExpl.getValue() *fieldNormExpl.getValue());

      result.addDetail(fieldExpl);
      result.setMatch(fieldExpl.getMatch());
     
     
// combine them
      result.setValue(queryExpl.getValue() * fieldExpl.getValue());

      if (queryExpl.getValue() == 1.0f)
        return fieldExpl;

      return result;
    }

Ҏ索结果,以及上面的TermWeightcȝexplain()ҎQ可以看出的字符串部分正好一一对应Q比如:idf(Inverse Document FrequencyQ即反{文档频率)、fieldNorm、fieldWeight?/span>

索结果的W一个Document的信息:

Document的内部编号ؓ Q?0
Document内容?Q?Document<stored/uncompressed,indexed,tokenized<contents:一?gt;>
Document得分?Q?0.81767845
Explanation?Q?
0.81767845 = (MATCH) fieldWeight(contents:一?in 0), product of:
1.0 = tf(termFreq(contents:一?=1)
0.81767845 = idf(docFreq=5)
1.0 = fieldNorm(field=contents, doc=0)

Document对应的Explanation的一些参数值如下:
Explanation的getValue()?Q?0.81767845
Explanation的getDescription()?Q?fieldWeight(contents:一?in 0), product of:

tf的计?/span>

上面的tf值Term FrequencyQ即词条频率Q可以在org.apache.lucene.search.SimilaritycM看到具体地说明。在Lucene中,q不是直接用的词条的频率,而实际用的词条频率的^ҎQ即Q?/span>

tf(t in d) = frequency½

q是使用org.apache.lucene.search.Similaritycȝ子类DefaultSimilarity中的Ҏ计算的,如下Q?/span>

/** Implemented as <code>sqrt(freq)</code>. */
public float tf(float freq) {
    return (float)Math.sqrt(freq);
}

卻I某个Document的tf = 索的词条在该Document中出现次数freq取^Ҏ?/span>

也就是freq的^Ҏ?/span>

例如Q从我们的检索结果来看:

搜烦关键?lt;一?gt;在编号ؓ 0 的Document中出现过 1 ?br /> 搜烦关键?lt;一?gt;在编号ؓ 1 的Document中出现过 1 ?br /> 搜烦关键?lt;一?gt;在编号ؓ 2 的Document中出现过 1 ?br /> 搜烦关键?lt;一?gt;在编号ؓ 3 的Document中出现过 2 ?br /> 搜烦关键?lt;一?gt;在编号ؓ 4 的Document中出现过 2 ?/span>

各个Document的tf计算如下所C:

~号?的Document?tf 为: (float)Math.sqrt(1) = 1.0Q?br /> ~号?的Document?tf 为: (float)Math.sqrt(1) = 1.0Q?br /> ~号?的Document?tf 为: (float)Math.sqrt(1) = 1.0Q?br /> ~号?的Document?tf 为: (float)Math.sqrt(2) = 1.4142135Q?br /> ~号?的Document?tf 为: (float)Math.sqrt(2) = 1.4142135Q?/span>

idf的计?/span>

索结果中Q每个检索出来的Document的都对应一个idfQ在DefaultSimilaritycM可以看到idf计算的实现方法,如下Q?/span>

/** Implemented as <code>log(numDocs/(docFreq+1)) + 1</code>. */
public float idf(int docFreq, int numDocs) {
    return (float)(Math.log(numDocs/(double)(docFreq+1)) + 1.0);
}

其中QdocFreq是根据指定关键字q行索,索到的Document的数量,我们试的docFreq=5QnumDocs是指索引文g中d的Document的数量,我们的测试比较特D,全部的Document都检索出来了Q我们测试的numDocs=5?/span>

各个Document的idf的计如下所C:

~号?的Document?idf 为:(float)(Math.log(5/(double)(5+1)) + 1.0) = 0.81767845Q?br /> ~号?的Document?idf 为:(float)(Math.log(5/(double)(5+1)) + 1.0) = 0.81767845Q?br /> ~号?的Document?idf 为:(float)(Math.log(5/(double)(5+1)) + 1.0) = 0.81767845Q?br /> ~号?的Document?idf 为:(float)(Math.log(5/(double)(5+1)) + 1.0) = 0.81767845Q?br /> ~号?的Document?idf 为:(float)(Math.log(5/(double)(5+1)) + 1.0) = 0.81767845Q?/span>

lengthNorm的计?/span>

在DefaultSimilaritycM可以看到lengthNorm计算的实现方法,如下Q?/span>

public float lengthNorm(String fieldName, int numTerms) {
    return (float)(1.0 / Math.sqrt(numTerms));
}

各个Document的lengthNorm的计如下所C:

~号?的Document?lengthNorm 为:(float)(1.0 / Math.sqrt(1)) = 1.0/1.0 = 1.0Q?br /> ~号?的Document?lengthNorm 为:(float)(1.0 / Math.sqrt(1)) = 1.0/1.0 = 1.0Q?br /> ~号?的Document?lengthNorm 为:(float)(1.0 / Math.sqrt(1)) = 1.0/1.0 = 1.0Q?br /> ~号?的Document?lengthNorm 为:(float)(1.0 / Math.sqrt(2)) = 1.0/1.4142135 = 0.7071068Q?br /> ~号?的Document?lengthNorm 为:(float)(1.0 / Math.sqrt(2)) = 1.0/1.4142135 = 0.7071068Q?/span>

 

 

 

 

 

 

 

 

 

 

 

 

 

 

关于fieldNorm

fieldNorm是在建立索引的时候写入的Q而检索的时候需要从索引文g中读取,然后通过解码Q得到fieldNorm的float型|用于计算Document的得分?/span>

在org.apache.lucene.search.TermQuery.TermWeightcMQexplainҎ通过打开的IndexReader读取fieldNormQ写入烦引文件的是byte[]cdQ需要解码,如下所C:

byte[] fieldNorms = reader.norms(field);
      float fieldNorm = fieldNorms!=null ? Similarity.decodeNorm(fieldNorms[doc]) : 0.0f;

调用SimilaritycȝdecodeNormҎQ将byte[]cdD{化ؓfloat点|

public static float decodeNorm(byte b) {
    return NORM_TABLE[b & 0xFF]; // & 0xFF maps negative bytes to positive above 127
}

q样Q一个Q点型的fieldNorm的值就被读取出来了Q可以参加一些运,最l实现Lucene的Document的得分的计算?/span>

queryWeight的计?/span>

queryWeight的计可以在org.apache.lucene.search.TermQuery.TermWeightcM的sumOfSquaredWeightsҎ中看到计的实现Q?/span>

    public float sumOfSquaredWeights() {
      queryWeight = idf * getBoost();            
// compute query weight
      return queryWeight * queryWeight;         
// square it
    }

其实默认情况下,queryWeight = idfQ因为Lucune中默认的Ȁ励因子boost = 1.0?/span>

各个Document的queryWeight的计如下所C:

queryWeight = 0.81767845 * 0.81767845 = 0.6685980475944025Q?/span>

queryNorm的计?/span>

queryNorm的计在DefaultSimilaritycM实现Q如下所C:

/** Implemented as <code>1/sqrt(sumOfSquaredWeights)</code>. */
public float queryNorm(float sumOfSquaredWeights) {
    return (float)(1.0 / Math.sqrt(sumOfSquaredWeights));
}

q里QsumOfSquaredWeights的计是在org.apache.lucene.search.TermQuery.TermWeightcM的sumOfSquaredWeightsҎ实现Q?/span>

    public float sumOfSquaredWeights() {
      queryWeight = idf * getBoost();            
// compute query weight
      return queryWeight * queryWeight;         
// square it
    }

其实默认情况下,sumOfSquaredWeights = idf * idfQ因为Lucune中默认的Ȁ励因子boost = 1.0?/span>

上面试例子中sumOfSquaredWeights的计如下所C:

sumOfSquaredWeights = 0.81767845*0.81767845 = 0.6685980475944025Q?/span>

然后Q就可以计算queryNorm的gQ计如下所C:

queryNorm = (float)(1.0 / Math.sqrt(0.6685980475944025) = 1.2229746301862302962735534977105Q?/span>

value的计?/span>

org.apache.lucene.search.TermQuery.TermWeightcȝ中还定义了一个value成员Q?/span>

private float value;

关于value的计,可以在它的子corg.apache.lucene.search.TermQuery.TermWeightcM看到计算的实玎ͼ

    public void normalize(float queryNorm) {
      this.queryNorm = queryNorm;
      queryWeight *= queryNorm;                   // normalize query weight
      value = queryWeight * idf;                  // idf for document
    }

q里Q用normalizeҎ计算value的|卻I

value = queryNorm * queryWeight * idf;

上面试例子中value的D如下:

value = 1.2229746301862302962735534977105 * 0.6685980475944025 * 0.81767845 = 0.66859804759440249999999999999973Q?/span>

关于fieldWeight

从检索结果中Q可以看刎ͼ

0.81767845 = (MATCH) fieldWeight(contents:一?in 0), product of:

字符?(MATCH) "的输在ComplexExplanationcM的getSummaryҎ中可以看刎ͼ

protected String getSummary() {
    if (null == getMatch())
      return super.getSummary();
   
    return getValue() + " = "
      + (isMatch() ? "(MATCH) " : "(NON-MATCH) ")
      + getDescription();
}

q个fieldWeight的值其实和Document的得分是相等的,先看q个fieldWeight是如何计出来的Q在org.apache.lucene.search.TermQuery.TermWeightcM的explainҎ中可以看刎ͼ

      ComplexExplanation fieldExpl = new ComplexExplanation();
      fieldExpl.setDescription("fieldWeight("+term+" in "+doc+
                               "), product of:");

      Explanation tfExpl = scorer(reader).explain(doc);
      fieldExpl.addDetail(tfExpl);
      fieldExpl.addDetail(idfExpl);

      Explanation fieldNormExpl = new Explanation();
      byte[] fieldNorms = reader.norms(field);
      float fieldNorm =
        fieldNorms!=null ? Similarity.decodeNorm(fieldNorms[doc]) : 0.0f;
      fieldNormExpl.setValue(fieldNorm);
      fieldNormExpl.setDescription("fieldNorm(field="+field+", doc="+doc+")");
      fieldExpl.addDetail(fieldNormExpl);
     
      fieldExpl.setMatch(Boolean.valueOf(tfExpl.isMatch()));
      fieldExpl.setValue(tfExpl.getValue() *
                         idfExpl.getValue() *
                         fieldNormExpl.getValue());

      result.addDetail(fieldExpl);
      result.setMatch(fieldExpl.getMatch());
     
      // combine them
      result.setValue(queryExpl.getValue() * fieldExpl.getValue());

      if (queryExpl.getValue() == 1.0f)
        return fieldExpl;

上面QComplexExplanation fieldExpl被设|了很多内容,我们׃q里来获取fieldWeight的计的实现?/span>

关键是在下面q行了计:

fieldExpl.setValue(tfExpl.getValue() *
                         idfExpl.getValue() *
                         fieldNormExpl.getValue());

使用计算式表C就?/span>

fieldWeight = tf * idf * fieldNorm

fieldNorm的值因为是在徏立烦引的时候写入到索引文g中的Q烦引只需要从上面的测试结果中取来Q进行如下关于Document的分数的计算的验证?/span>

使用我们q个例子来计检索出来的Docuyment的fieldWeightQ需要用到前面计出来的l果Q如下所C:

~号?的Document?fieldWeight 为:1.0 * 0.81767845 * 1.0 = 0.81767845Q?br /> ~号?的Document?fieldWeight 为:1.0 * 0.81767845 * 0.5 = 0.408839225Q?br /> ~号?的Document?fieldWeight 为:1.0 * 0.81767845 * 0.5 = 0.408839225Q?br /> ~号?的Document?fieldWeight 为:1.4142135 * 0.81767845 * 0.4375 = 0.5059127074089703125Q?br /> ~号?的Document?fieldWeight 为:1.4142135 * 0.81767845 * 0.4375 = 0.5059127074089703125Q?/span>

Ҏ一下,其实索结果中Document的得分就是这个fieldWeight的|验证后,正好相符(注意Q我q里没有q行舍入q算)?/span>

ȝ说明

上面的计得分是按照Lucene默认讄的情况下q行的,比如Ȁ励因子的默认gؓ1.0Q它体现的是一个Document的重要性,x谓的fieldWeight?/span>

不仅可以通过Z个Document讄Ȁ励因子boostQ而且可以通过Z个Document中的Field讄boostQ因Z个Document的权重体现在它当中的Field上,即上面计出来的fieldWeight与Document的得分是相等的?/span>

提高一个Document的激励因子boostQ可以该Document被检索出来的默认排序靠前Q即说明比较重要。也是_修改Ȁ励因子boost能够改变索结果的排序?/span>








西瓜 2011-04-15 11:02 发表评论
]]>
Sphinxhttp://www.aygfsteel.com/ashutc/archive/2011/04/01/347467.html西瓜西瓜Fri, 01 Apr 2011 06:13:00 GMThttp://www.aygfsteel.com/ashutc/archive/2011/04/01/347467.htmlhttp://www.aygfsteel.com/ashutc/comments/347467.htmlhttp://www.aygfsteel.com/ashutc/archive/2011/04/01/347467.html#Feedback0http://www.aygfsteel.com/ashutc/comments/commentRss/347467.htmlhttp://www.aygfsteel.com/ashutc/services/trackbacks/347467.html 首先我们要从 Sphinx 官网?http://www.sphinxsearch.com/downloads.html 下蝲 mysql-5.0.45-sphinxse-0.9.8-win32.zip ? sphinx-0.9.8.1-win32.zipQ假设你已经安装好了 MySQL

先将 mysql 服务停掉 解压 mysql-5.0.45-sphinxse-0.9.8-win32.zip ?bin ?share 覆盖?mysql 目录中的 bin ?share 解压 sphinx-0.9.8.1-win32.zip 到独立的目录Q如:d:/www/sphinx/?

接着开?mysql 服务Q徏?"test" 数据库,q导?sql 语句,如下Q?

-----------------------------------------------------------

CREATE TABLE `documents` (
`id` int(11) NOT NULL auto_increment,
`group_id` int(11) NOT NULL,
`group_id2` int(11) NOT NULL,
`date_added` datetime NOT NULL,
`title` varchar(255) NOT NULL,
`content` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5;

INSERT INTO `documents` VALUES ('1', '1', '5', '2008-09-13 21:37:47', 'test one', 'this is my test document number one. also checking search within phrases.');
INSERT INTO `documents` VALUES ('2', '1', '6', '2008-09-13 21:37:47', 'test two', 'this is my test document number two');
INSERT INTO `documents` VALUES ('3', '2', '7', '2008-09-13 21:37:47', 'another doc', 'this is another group');
INSERT INTO `documents` VALUES ('4', '2', '8', '2008-09-13 21:37:47', 'doc number four', 'this is to test groups');

-------------------------------------------实际上,q个新徏立的表就?Sphinx 中的 example.sql

我们的测试表已经建立完成Q接下来我们要配|?sphinx-doc.conf 文gQ重要)

先将 sphinx 下的 sphinx-min.conf 复制一份改名ؓ sphinx-doc.confQ接着 修改?

----------------------------------------------------------------------

#
# Minimal Sphinx configuration sample (clean, simple, functional)
#
# type----------------------------------------数据库类型,目前支持 mysql ?pgsql
# strip_html--------------------------------是否Lhtml 标签
# sql_host----------------------------------数据库主机地址
# sql_user----------------------------------数据库用户名
# sql_pass----------------------------------数据库密?
# sql_db-------------------------------------数据库名U?
# sql_port-----------------------------------数据库采用的端口
# sql_query_pre--------------------------执行sql前要讄的字W集Q用utf8必须SET NAMES utf8
# sql_query---------------------------------全文索要昄的内容,在这里尽可能不用where? group byQ将 where ?groupby 的内容交l?sphinxQ由 sphinx q行条gqo?groupby 效率会更?
# 注意: select 出来的字D必至包括一个唯一主键 (ARTICLESID) 以及要全文检索的字段Q你计划原本?where 中要用到的字D也?select 出来
# q里不用使用orderby
# sql_attr_ 开头的表示一些属性字D,你原计划要用?where, orderby, groupby 中的字段要在q里定义(# 己添加的注释内容)

#source 数据源名:

source documents
{
type             = mysql
sql_host     = localhost
sql_user      = root
sql_pass     = yourpassword
sql_db         = test
sql_port       = 3306 # optional, default is 3306

sql_query_pre     = SET NAMES utf8
sql_query     = \
   SELECT id, group_id, UNIX_TIMESTAMP(date_added) AS date_added, title, content \
   FROM documents

sql_attr_uint    = group_id
sql_attr_timestamp   = date_added

sql_query_info    = SELECT * FROM documents WHERE id=$id
}


index documents
{
source      = documents

#path   索引记录存放目录Q如 d:/sphinx/data/cgfinal ,实际存放时会存放?d:/sphinx/data 目录Q然后创建多?cgfinal 名称Q不同扩展名的烦引文件?
path          = d:/www/sphinx/data/doc
docinfo      = extern
enable_star     = 1

min_word_len     = 3
min_prefix_len     = 0
min_infix_len     = 3
charset_type    = sbcs

# 其他的配|如 min_word_len, charset_type, charset_table, ngrams_chars, ngram_len q些则是支持中文索需要设|的内容?
# 如果索的不是中文Q则 charset_table, ngrams_chars, min_word_len p讄不同的内容,具体官方|站的论坛中有很多,大家可以L索看看?
}

# mem_limit 索引使用内存最大限ӞҎ机器情况而定Q默认是32MQ太的会媄响烦引的性能?
indexer
{
mem_limit     = 32M
}

# 搜烦的守护进E配|?
# 在进行全文检索过E中Qsearchd要先开启,mysql在全文检索时才能q接到sphinxQ由sphinxq行全文索,再将l果q回lmysql
# address 侦听h的地址Q不讄则侦听所有地址
# port 侦听端口
searchd
{
port     = 3312
log       =d:/www/sphinx/logs/searched_doc.log
query_log     = d:/www/sphinx/logs/query_doc.log
read_timeout    = 5
max_children    = 30
pid_file     = d:/www/sphinx/logs/searched-doc.pid
max_matches     = 1000
seamless_rotate    = 0
preopen_indexes    = 0
unlink_old     = 1
}


----------------------------------------------------------------------


Z试Q我们的 Sphinx 配置文g已经写好Q确保我们的 Mysql 数据库已l启动,如果没有启动则在 cmd 中键? net start mysql "

接下来,我们的测试正式开始:

1Q生成数据烦引或重徏索引Q?

Q最好再复制一?sphinx-doc.conf 配置文gQƈ把它攑օ bin 文g夹中Q下面的举例 假设我们已经q样做)Q?

?cmd 模式下:输入Q?

d:/www/sphinx/bin/indexer.exe --config d:/www/sphinx/bin/sphinx-doc.conf documents

2Q运行检索守护进E?searchd.exeQ?

d:/www/sphinx/bin/searchd.exe --config d:/www/sphinx/bin/sphinx-doc.conf

如过q两步没有报错的话,说明我们?Sphinx 已经正常q行了!可以通过 netstat -an 查看是否 3312 端口是否处如监听状态?

3Q现在来?sphinx 自带的工?search.exe 来测试一下:

试Q?

索引关键字: this is m

D:\www\sphinx\bin>search.exe -c d:/www/sphinx/bin/sphinx-doc.conf this is m

l果Q?

Sphinx 0.9.8-release (r1371)
Copyright (c) 2001-2008, Andrew Aksyonoff

using config file 'd:/www/sphinx/bin/sphinx-doc.conf'...
WARNING: index 'documents': invalid morphology option 'extern' - IGNORED
index 'documents': query 'this is m ': returned 4 matches of 4 total in 0.000 s
c

displaying matches:
1. document=1, weight=1, group_id=1, date_added=Sat Sep 13 21:37:47 2008
        id=1
        group_id=1
        group_id2=5
        date_added=2008-09-13 21:37:47
        title=test one
        content=this is my test document number one. also checking search withi
phrases.
2. document=2, weight=1, group_id=1, date_added=Sat Sep 13 21:37:47 2008
        id=2
        group_id=1
        group_id2=6
        date_added=2008-09-13 21:37:47
        title=test two
        content=this is my test document number two
3. document=3, weight=1, group_id=2, date_added=Sat Sep 13 21:37:47 2008
        id=3
        group_id=2
        group_id2=7
        date_added=2008-09-13 21:37:47
        title=another doc
        content=this is another group
4. document=4, weight=1, group_id=2, date_added=Sat Sep 13 21:37:47 2008
        id=4
        group_id=2
        group_id2=8
        date_added=2008-09-13 21:37:47
        title=doc number four
        content=this is to test groups

words:
1. 'this': 4 documents, 4 hits

-------------------

索引关键字: this is another group

D:\www\sphinx\bin>search.exe -c d:/www/sphinx/bin/sphinx-doc.conf this is another group

l果Q?

Sphinx 0.9.8-release (r1371)
Copyright (c) 2001-2008, Andrew Aksyonoff

-------------------

到此sphinx在win上算正常q行了,sphinx-doc.conf文g配置比较灉|Q根据你需要烦引的数据库进行灵z配|来辑ֈ你需要的效果

如果配置q程中出现运行参数配|问题可以查?doc/sphinx.html文gQ里面对各种参数都要详细的说?


using config file 'd:/www/sphinx/bin/sphinx-doc.conf'...
WARNING: index 'documents': invalid morphology option 'extern' - IGNORED
index 'documents': query 'this is another group ': returned 1 matches of 1 total
in 0.000 sec

displaying matches:
1. document=3, weight=4, group_id=2, date_added=Sat Sep 13 21:37:47 2008
        id=3
        group_id=2
        group_id2=7
        date_added=2008-09-13 21:37:47
        title=another doc
        content=this is another group

words:
1. 'this': 4 documents, 4 hits
2. 'another': 1 documents, 2 hits
3. 'group': 1 documents, 1 hits

















西瓜 2011-04-01 14:13 发表评论
]]>
lucene优化http://www.aygfsteel.com/ashutc/archive/2010/09/02/330669.html西瓜西瓜Thu, 02 Sep 2010 01:56:00 GMThttp://www.aygfsteel.com/ashutc/archive/2010/09/02/330669.htmlhttp://www.aygfsteel.com/ashutc/comments/330669.htmlhttp://www.aygfsteel.com/ashutc/archive/2010/09/02/330669.html#Feedback0http://www.aygfsteel.com/ashutc/comments/commentRss/330669.htmlhttp://www.aygfsteel.com/ashutc/services/trackbacks/330669.html

Boosting Documents and Fields
setBoost(float) 讄Documents和Fields在index中的重要?br />
可以ldocument讄boostQ也可以lfield讄boost
讄boost会删除原来的document然后重新建立索引

doc.setBoost();
field.setBoost();

 

boost是怎样存储到index中的Q利用norms
在徏立烦引过E中生成的boosts会被l合在一起变成一个QҎQ然后每个文档每个字D?br /> 都会存ؓ一个byte。在查询q程中,每个field的norms会被装入内存Q重新解码ؓ一个QҎ

即norms在徏立烦引的q程中得刎ͼ我们也可以用IndexReader的setNormҎ来改?br />
norms会在搜烦q程中消耗过多的内存
我们可以norms关闭QField.setOmitNorms(true)Q这h可能影响评分Q但是媄响效?br /> 可以忽略

indexing dates&times
DataTools.dateToString(new Date(),DateTools.Resolution.DAY);

Indexing numbers
lucene利用词典~排来给field排序Q也是说如果有3个数Q?Q?1Q?0Q正常的排序是:7Q?0Q?1。但是词典排序是Q?0Q?Q?1。一个简单和通用的方法是l数字加前缀0Q?07Q?20Q?71

indexing fields for sorting

field建立索引但是不分词Field.Index.NOT_ANALYZEDQ字D必d储Integers,Floats,Strings

Field truncation
比如说你只想l一个文前200个字建立索引
在indexWriter的构造方法中传递MaxFieldLength参数
pȝ讑֮的值MaxFieldLength.UNLIMITED和MaxFieldLength.LIMITED

可以调用setMaxFieldLength()Ҏ来修?br />
IndexWriter.setInfoStream(System.out) 关于合ƈQ删除的信息以及当maxFieldLength到达会显CZ?br />
Optimizing an index

索引优化只能提高搜烦的速度Q不会加快徏立烦引的速度,不进行优化也有可能获得很好的搜烦吞吐?/p>


IndexWriter提供4个优化方?/p>

  • optimize()Q将index减少C个segmentQ只到操作完成才q回
  • optimize(int maxNumSeqments)Q部分优化,一般来_index合ƈ到最后一个segment最消耗时_所以优化到5个segment会比优化?个segment?/li>
  • optimize(boolean doWait)Q同optimize()一P只是当doWait为false的时候,该方法会立刻q回Q合q烦引操作在后台q行
  • optimize(int maxNumSegments,boolean doWait)


西瓜 2010-09-02 09:56 发表评论
]]>
Lucene打分公式http://www.aygfsteel.com/ashutc/archive/2010/07/29/327432.html西瓜西瓜Thu, 29 Jul 2010 07:15:00 GMThttp://www.aygfsteel.com/ashutc/archive/2010/07/29/327432.htmlhttp://www.aygfsteel.com/ashutc/comments/327432.htmlhttp://www.aygfsteel.com/ashutc/archive/2010/07/29/327432.html#Feedback0http://www.aygfsteel.com/ashutc/comments/commentRss/327432.htmlhttp://www.aygfsteel.com/ashutc/services/trackbacks/327432.html在进行Lucene的搜索过E解析之前,有必要单独的一张把Lucene score公式的推|各部分的意义阐述一下。因为Lucene的搜索过E,很重要的一个步骤就是逐步的计各部分的分数?/p>

Lucene的打分公式非常复杂,如下Q?/p>

 

 

在推g前,先逐个介绍每部分的意义Q?/p>

  • tQTermQ这里的Term是指包含域信息的TermQ也即title:hello和content:hello是不同的Term
  • coord(q,d)Q一ơ搜索可能包含多个搜索词Q而一文中也可能包含多个搜索词Q此表C,当一文中包含的搜索词多Q则此文则打分高?
  • queryNorm(q)Q计每个查询条目的方差和,此值ƈ不媄响排序,而仅仅得不同的query之间的分数可以比较。其公式如下Q?
  • tf(t in d)QTerm t在文d中出现的词频
  • idf(t)QTerm t在几文档中出现q?
  • norm(t, d)Q标准化因子Q它包括三个参数Q?
    • Document boostQ此D大,说明此文越重要?
    • Field boostQ此域越大,说明此域重要?
    • lengthNorm(field) = (1.0 / Math.sqrt(numTerms))Q一个域中包含的TermL多Q也x越长,此D,文短Q此D大?

 

 

  • 各类Boost?
    • t.getBoost()Q查询语句中每个词的权重Q可以在查询中设定某个词更加重要Qcommon^4 hello
    • d.getBoost()Q文档权重,在烦引阶D写入nrm文gQ表明某些文比其他文更重要?
    • f.getBoost()Q域的权重,在烦引阶D写入nrm文gQ表明某些域比其他的域更重要?

以上在Lucene的文档中已经详细提到Qƈ在很多文章中也被阐述q,如何调整上面的各部分Q以影响文的打分,请参?a >有关Lucene的问?4):影响LuceneҎ打分的四种方式一文?/p>

然而上面各部分Z么要q样计算在一起呢Q这么复杂的公式是怎么得出来的呢?下面我们来推对{?/p>

首先Q将以上各部分代入score(q, d)公式Q将得到一个非常复杂的公式Q让我们忽略所有的boostQ因些属于h为的调整Q也省略coordQ这和公式所要表辄原理无关。得C面的公式Q?/p>

 

然后Q有Lucene学习ȝ之一Q全文检索的基本原理中的描述我们知道QLucene的打分机制是采用向量I间模型的:

我们把文档看作一pd?Term)Q每一个词(Term)都有一个权?Term weight)Q不同的?Term)Ҏ自己在文中的权重来影响文相关性的打分计算?

于是我们把所有此文中词(term)的权?term weight) 看作一个向量?

Document = {term1, term2, …… ,term N}

Document Vector = {weight1, weight2, …… ,weight N}

同样我们把查询语句看作一个简单的文Q也用向量来表示?

Query = {term1, term 2, …… , term N}

Query Vector = {weight1, weight2, …… , weight N}

我们把所有搜索出的文向量及查询向量攑ֈ一个Nl空间中Q每个词(term)是一l?

 

我们认ؓ两个向量之间的夹角越,相关性越大?

所以我们计夹角的余ug为相x的打分Q夹角越,余uD大,打分高Q相x越大?

余u公式如下Q?/p>

 

下面我们假设Q?/p>

查询向量为Vq = <w(t1, q), w(t2, q), ……, w(tn, q)>

文向量为Vd = <w(t1, d), w(t2, d), ……, w(tn, d)>

向量I间l数为nQ是查询语句和文的q的长度,当某个Term不在查询语句中出现的时候,w(t, q)为零Q当某个Term不在文中出现的时候,w(t, d)为零?/p>

w代表weightQ计公式一般ؓtf*idf?/p>

我们首先计算余u公式的分子部分,也即两个向量的点U:

Vq*Vd = w(t1, q)*w(t1, d) + w(t2, q)*w(t2, d) + …… + w(tn ,q)*w(tn, d)

把w的公式代入,则ؓ

Vq*Vd = tf(t1, q)*idf(t1, q)*tf(t1, d)*idf(t1, d) + tf(t2, q)*idf(t2, q)*tf(t2, d)*idf(t2, d) + …… + tf(tn ,q)*idf(tn, q)*tf(tn, d)*idf(tn, d)

在这里有三点需要指出:

  • ׃是点U,则此处的t1, t2, ……, tn只有查询语句和文的q有非零|只在查询语句出现的或只在文中出现的Term的项的gؓ零?
  • 在查询的时候,很少有h会在查询语句中输入同L词,因而可以假设tf(t, q)都ؓ1
  • idf是指Term在多篇文中出现过Q其中也包括查询语句q篇文,因而idf(t, q)和idf(t, d)其实是一LQ是索引中的文L加一Q当索引中的文L_大的时候,查询语句q篇文档可以忽略,因而可以假设idf(t, q) = idf(t, d) = idf(t)

Z上述三点Q点U公式ؓQ?/p>

Vq*Vd = tf(t1, d) * idf(t1) * idf(t1) + tf(t2, d) * idf(t2) * idf(t2) + …… + tf(tn, d) * idf(tn) * idf(tn)

所以余弦公式变为:

 

下面要推导的是查询语句的长度了?/p>

׃面的讨论Q查询语句中tf都ؓ1Qidf都忽略查询语句这小文档Q得到如下公?/p>

 

所以余弦公式变为:

 

下面推导的就是文的长度了,本来文档长度的公式应该如下:

 

q里需要讨论的是,Z么在打分q程中,需要除以文档的长度呢?

因ؓ在烦引中Q不同的文长度不一P很显Ӟ对于L一个termQ在长的文中的tf要大的多Q因而分C高Q这样对的文不公qI举一? 极端的例子,在一?000万个词的鸿篇巨著中,"lucene"q个词出C11ơ,而在一?2个词的短文中Q?lucene"q个词出C10 ơ,如果不考虑长度在内Q当焉`巨著应该分数更高,然而显然这小文档才是真正x"lucene"的?/p>

然而如果按照标准的余u计算公式Q完全消除文档长度的影响Q则又对长文不公^(毕竟它是包含了更多的信息)Q偏向于首先q回短小的文的Q这样在实际应用中得搜索结果很隄?/p>

所以在Lucene中,Similarity的lengthNorm接口是开攑և来,用户可以Ҏ自己应用的需要,改写lengthNorm的计? 公式。比如我惛_一个经学论文的搜索系l,l过一定时间的调研Q发现大多数的经学论文的长度在8000?0000词,因而lengthNorm的公 式应该是一个倒抛物线型的Q?000?10000词的论文分数最高,更短或更长的分数都应该偏低,方能够返回给用户最好的数据?/p>

在默认状况下QLucene采用DefaultSimilarityQ认为在计算文的向量长度的时候,每个Term的权重就不再考虑在内了,而是全部Z?/p>

而从Term的定义我们可以知道,Term是包含域信息的,也即title:hello和content:hello是不同的TermQ也即一个Term只可能在文中的一个域中出现?/p>

所以文长度的公式为:

 

代入余u公式Q?/p>

 

再加上各Uboost和coordQ则可得出Lucene的打分计公式?/p>

西瓜 2010-07-29 15:15 发表评论
]]>
Lucene的检索优化(转)http://www.aygfsteel.com/ashutc/archive/2010/07/19/326501.html西瓜西瓜Mon, 19 Jul 2010 03:46:00 GMThttp://www.aygfsteel.com/ashutc/archive/2010/07/19/326501.htmlhttp://www.aygfsteel.com/ashutc/comments/326501.htmlhttp://www.aygfsteel.com/ashutc/archive/2010/07/19/326501.html#Feedback0http://www.aygfsteel.com/ashutc/comments/commentRss/326501.htmlhttp://www.aygfsteel.com/ashutc/services/trackbacks/326501.html而尽可能减少IndexSearcher的创建和Ҏ索结果的前台的缓存也是必要的?/p>

Lucene面向全文索的优化在于首次索引索后Qƈ不把所有的记录QDocumentQ具体内容读取出来,而是只将所有结果中匚w度最高的?br /> 100条结果(TopDocsQ的ID攑ֈl果集缓存中q返回,q里可以比较一下数据库索:如果是一?0,000条的数据库检索结果集Q数据库是一?br /> 要把所有记录内定w取得以后再开始返回给应用l果集的。所以即使检索匹配L很多QLucene的结果集占用的内存空间也不会很多。对于一般的模糊索应
用是用不到这么多的结果的Q头100条已l可以满?0%以上的检索需求?/p>

如果首批~存l果数用完后q要d更后面的l果时Searcher会再ơ检索ƈ生成一个上ơ的搜烦~存数大1倍的~存Qƈ再重新向后抓取。所以如?br /> 构造一个SearcherL1Q?20条结果,Searcher其实是进行了2ơ搜索过E:?00条取完后Q缓存结果用完,Searcher重新?br /> 再构造一?00条的l果~存Q依此类推,400条缓存,800条缓存。由于每ơSearcher对象消失后,q些~存也访问那不到了,你有可能惛_l果
记录~存下来Q缓存数量保证?00以下以充分利用首ơ的l果~存Q不让Lucene费多次索,而且可以分q行l果~存?/p>

Lucene的另外一个特Ҏ在收集结果的q程中将匚w度低的结果自动过滤掉了。这也是和数据库应用需要将搜烦的结果全部返回不同之处?/p>

刚刚开始学LuceneQ看的是Lucene in
Action。顺着看下去,很自然的是使用Hits来访问Search的结果。但是用v来,发现Search的速度是很快,不过如果l果很多的话Q比 ?W个)Q通过Hits讉K所有的l果速度非常慢,是单地从每个结果中M个FieldQ在我的机器上用了接q?分钟。因为我的应用烦引的只是我的 数据的两个域包含文本信息的域Q我本希望通过Lucene查找出符合需求的数据IDQ再通过IDd断数据库中的其他域来军_最l的l果。这栯取ID? 需?分钟Q我的应用可受不了?/p>

W一个想到的Ҏ是把我的全部数据域都做成Lucene的烦引,然后全部通过LuceneL索。但是由于我的很多域是数字,全部转换? Lucene能接受的字符Ԍ感觉性能不会好。另外如果我想针Ҏ索的l果做统计,也没法避免需要遍历全部的搜烦l果Q如?W个结果就需?分钟的话Q? q不用处理其他的域Q也是不能忍受的?/p>

开源Y件的好处是可以M码。通过阅读Hits的代码,l于扑ֈ了解决问题的办法?/p>

Lucene
的代码看hq不是特别Professional。比如下面这两个Hits的初始化函数。首先里面的q,s,f什么的让h看v来就不是太舒服(其他的代? 里还用i,j做@环变量)。其ơ这两个函数只有o那一个赋g一P明显应该只写一个,让另一个来调用。最后程序里面直接用?0q个常数Q编E的? 忌。(50在其他函数里面也有)

Hits(Searcher s, Query q, Filter f) throws IOException {
    weight =
q.weight(s);
    searcher =
s;
    filter =
f;
    nDeletions =
countDeletions(s);
   
getMoreDocs(50); // retrieve 100 initially
   
lengthAtStart = length;
  }

  Hits(Searcher s, Query q, Filter f, Sort o)
throws IOException {
    weight =
q.weight(s);
    searcher =
s;
    filter =
f;
    sort =
o;
    nDeletions =
countDeletions(s);
   
getMoreDocs(50); // retrieve 100 initially
   
lengthAtStart = length;
  }
通过q两个函敎ͼ应该看出Hits初始化的时候只调入了前100个文档?/p>

一般我们是通过Document doc(int
n)函数来访问的。这个函数里面先判断了有多少数据已经被调入了Q如果要讉K的数据不在,去调用getMoreDocs函数QgetMoreDocs? 取得需要的2倍文进来?/p>

但是getMoreDocs的代码比较让人疑惑,里面一D代码是q样的:
    int n = min
* 2;    //
double # retrieved
    TopDocs
topDocs = (sort == null) ? searcher.search(weight, filter, n) :
searcher.search(weight, filter, n, sort);
q不成了每次d的时候都要去调search重新查找吗?除非search里面有缓存,否则性能一定指C降啊Q?br /> 实际上Hits最l用的也是TopDocsQSearcherl合来实现输出结果,那不如我们来直接使用下层一点的对象了。我原来的代码是Q?/p>

Hits hits = searcher.search(query);
for( int i=0;i<hits .lengthQ);i++) {
    Document doc
= hits .doc(i );
   
szTest.add(doc);
}
现在改ؓQ?br /> TopDocs topDoc = searcher.search(query.weight(searcher), null,
100000);//注意最后一个参敎ͼ是searchq回的结果数量,应该比你最大可能返回的数量大,否则ScoreDoc里面是你设|的数量?/p>

ScoreDoc[] scoreDocs = topDoc.scoreDocs;
for( int i=0;i<scoreDocs.length;i++) {
    Document doc
= searcher.doc(scoreDocs[i].doc );
   
szTest.add(doc);
}
l果?2000个ID加入ArrayList用时0.4U,快了几百倍?/p>

{等Q还没完?br /> 我只需要ID字段Q但是返回整个DocQ其他两个文本Field也返回了。因为Lucene是倒烦引保存信息的Q每一个文本Field需要重新组合成原始 的字W串Q这也是要耗时间的。searcher的doc函数有一个可以限定只取部分域的:

Document doc(int n, FieldSelector fieldSelector)

我下面定义一个FieldSelectorQ只取某一个给定名字的Field
class SpecialFieldSelector implements FieldSelector {
    protected
String m_szFieldName;
    public
SpecialFieldSelector( String szFieldName ) {
       
m_szFieldName = szFieldName;
    }
   
    public
FieldSelectorResult accept(String fieldName) {
       
if( fieldName.equalsIgnoreCase(m_szFieldName)) {
           
return  FieldSelectorResult.LOAD;
       
}
       
else {
           
return  FieldSelectorResult.NO_LOAD;
       
}
   
}   
}
再修Ҏ的代码:
ScoreDoc[] scoreDocs = topDoc.scoreDocs;
ArrayList<Document> szTest = new
ArrayList<Document>();
FieldSelector fieldSelector = new
SpecialFieldSelector(FIELD_ID);
for( int i=0;i<scoreDocs.length;i++) {
       
Document doc = searcher.doc(scoreDocs[i].doc, fieldSelector);
       
szTest.add(doc);
}
现在q回1.2W个ID耗时0.25U。虽然比前面只少了大U?50毫秒Q但是是接近40%的提高了Q在负蝲比较大的应用中还是很重要的?/p>

注:
   有些可以借鉴?br />






西瓜 2010-07-19 11:46 发表评论
]]>
LuceneW记http://www.aygfsteel.com/ashutc/archive/2010/07/16/326274.html西瓜西瓜Fri, 16 Jul 2010 03:04:00 GMThttp://www.aygfsteel.com/ashutc/archive/2010/07/16/326274.htmlhttp://www.aygfsteel.com/ashutc/comments/326274.htmlhttp://www.aygfsteel.com/ashutc/archive/2010/07/16/326274.html#Feedback0http://www.aygfsteel.com/ashutc/comments/commentRss/326274.htmlhttp://www.aygfsteel.com/ashutc/services/trackbacks/326274.html
    // tokenStream分词
    public static String analyze(Analyzer analyzer, String keyword) throws IOException {
        StringBuffer sb 
= new StringBuffer();
        TokenStream tokenStream 
= analyzer.tokenStream("content"new StringReader(keyword));
        
for (Token token = new Token(); (token = tokenStream.next(token)) != null;) {

            sb.append(token.term() 
+ " ");
        }
        
return sb.toString();

    }

讄关键词之间空格的与或关系

queryParser_and.setDefaultOperator(QueryParser.AND_OPERATOR);
queryParser_or.setDefaultOperator(QueryParser.OR_OPERATOR);


qoҎ字符

    Query query_and = queryParser_and.parse(QueryParser.escape(keyword));


遇到多余一个空格后的处?br />
    /**
     * 
     * 把超q一个空格后面的转化?nbsp;OR 可辑ּ
     * 
     * 
@param wd
     * 
@return eg: ibm t60 mp3 液晶 ibm t60 OR mp3 OR 液晶
     
*/
    
public static String nvl(String value) {
        
return value == null ? "" : value;
    }

    
public static String parseWd(String wd) {
        String retwd 
= nvl(wd).replaceAll(" "" ").replaceAll("  "" ");
        String[] arr 
= nvl(retwd).split(" ");
        
if (arr != null && arr.length > 2) {
            retwd 
= (arr[0].trim().equals("OR"? "" : arr[0+ " "+ (arr[1].trim().equals("OR"? "" : arr[1]);
            
for (int i = 2; i < arr.length; i++) {
                
if (!arr[i].trim().equals("OR")) {
                    retwd 
+= " OR " + arr[i];
                }
            }
        }
        
return retwd;
    }




西瓜 2010-07-16 11:04 发表评论
]]>
Lucene 2.9.0 使用http://www.aygfsteel.com/ashutc/archive/2010/07/12/325844.html西瓜西瓜Mon, 12 Jul 2010 03:49:00 GMThttp://www.aygfsteel.com/ashutc/archive/2010/07/12/325844.htmlhttp://www.aygfsteel.com/ashutc/comments/325844.htmlhttp://www.aygfsteel.com/ashutc/archive/2010/07/12/325844.html#Feedback0http://www.aygfsteel.com/ashutc/comments/commentRss/325844.htmlhttp://www.aygfsteel.com/ashutc/services/trackbacks/325844.html最?.9的IndexWriter 建立方式Q?/font>

Directory directory = new SimpleFSDirectory(new File(path),new SimpleFSLockFactory());  // 先要建立directory   
IndexWriter writer = new IndexWriter(directory,new WhitespaceAnalyzer(), cover,IndexWriter.MaxFieldLength.UNLIMITED); // q里最大字D长度无限(大字D|contentQ,cover为true表示覆盖写用于初始化Qfalse用于更新Q这里就? WhitespaceAnalyzer()分词?nbsp; 
Directory directory = new SimpleFSDirectory(new File(path),new SimpleFSLockFactory());  // 先要建立directory
IndexWriter writer = new IndexWriter(directory,new WhitespaceAnalyzer(), cover,IndexWriter.MaxFieldLength.UNLIMITED); // q里最大字D长度无限(大字D|contentQ,cover为true表示覆盖写用于初始化Qfalse用于更新Q这里就? WhitespaceAnalyzer()分词?/font>

IndexWriter 参数调整

writer.setMergeFactor(50); // 多少个合q一?nbsp;  
writer.setMaxMergeDocs(5000); // 一个segment最多有多少个documentQnbsp; 
writer.setMergeFactor(50); // 多少个合q一?br style="word-wrap: break-word;" /> writer.setMaxMergeDocs(5000); // 一个segment最多有多少个documentQ?font>

 


把其他格式{化ؓlucene需要的documentQ式

documentQdoc = new documentQ?;  //每一个doc相当于数据库的一条记?nbsp;  
doc.add(new Field("uid", line.getUid().toString(), Store.YES,Index.NO));  //每一个fieldQ相当于数据库的字段   
  
doc.add(new Field("title", line.getTitle(), Store.NO,Index.ANALYZED));    
doc.add(new Field("content", line.getContent(),Store.NO, Index.ANALYZED));   
documentQdoc = new documentQ?;  //每一个doc相当于数据库的一条记?br style="word-wrap: break-word;" /> doc.add(new Field("uid", line.getUid().toString(), Store.YES,Index.NO));  //每一个fieldQ相当于数据库的字段

doc.add(new Field("title", line.getTitle(), Store.NO,Index.ANALYZED)); 
doc.add(new Field("content", line.getContent(),Store.NO, Index.ANALYZED)); 

向IndexWriterddocQ可以插入多条doc

writer.adddocumentQdoc);   
writer.adddocumentQdoc2);   
writer.adddocumentQdoc3);  
writer.adddocumentQdoc);
writer.adddocumentQdoc2);
writer.adddocumentQdoc3);

开始写入(close的时候ؓ实际写入q程Q?/p>

writer.close();   
writer = null;  
writer.close();
writer = null;

d写入的烦引数

writer.numDocs()   
writer.maxDoc()  
writer.numDocs()
writer.maxDoc()

在close之前可以q行优化Q不在徏立烦引时候用)

writer.optimize()

2、清I烦?br style="word-wrap: break-word;" /> Directory directory = new SimpleFSDirectory(new File(path),new SimpleFSLockFactory());   
IndexWriter.unlock(directory);  //关键是这一步要q行目录解锁Q这里解的是write.lock?nbsp;  
IndexWriter writer = new IndexWriter(directory,new WhitespaceAnalyzer(), false,IndexWriter.MaxFieldLength.LIMITED);   
writer.deleteAll();  //标识删除全部   
writer.optimize();  //q个步骤才是实际删除的过E?nbsp;  
writer.close();   
Directory directory = new SimpleFSDirectory(new File(path),new SimpleFSLockFactory());
IndexWriter.unlock(directory);  //关键是这一步要q行目录解锁Q这里解的是write.lock?br style="word-wrap: break-word;" /> IndexWriter writer = new IndexWriter(directory,new WhitespaceAnalyzer(), false,IndexWriter.MaxFieldLength.LIMITED);
writer.deleteAll();  //标识删除全部
writer.optimize();  //q个步骤才是实际删除的过E?br style="word-wrap: break-word;" /> writer.close(); 

3、删除指定烦引(和清I差不多Q?br style="word-wrap: break-word;" /> writer.deletedocumentQ?new Term("uri", uri));  //q里是删除term满条g的一条或多条   
writer.deletedocumentQ?query); //q里是删除一个查询出来的内容  
writer.deletedocumentQ?new Term("uri", uri));  //q里是删除term满条g的一条或多条
writer.deletedocumentQ?query); //q里是删除一个查询出来的内容

4、更新烦?br style="word-wrap: break-word;" /> 是先删除再d的过E,没有直接update的办?/p>

5、读取徏立的索引分词
TermEnum terms = indexReader.terms(new Term(index, ""));   
Term term = terms.term();  //获取一条烦?nbsp;  
term().field(); //获取索引的fieldQ字D名Q?nbsp;  
term().text(); //获取索引的?nbsp; 
TermEnum terms = indexReader.terms(new Term(index, ""));
Term term = terms.term();  //获取一条烦?br style="word-wrap: break-word;" /> term().field(); //获取索引的fieldQ字D名Q?br style="word-wrap: break-word;" /> term().text(); //获取索引的?/p>

6、搜?br style="word-wrap: break-word;" /> 最?.9的IndexSearcher 建立方式Q?/p>

Directory directory = new SimpleFSDirectory(new File(path),new SimpleFSLockFactory());   
IndexSearcher indexSearcher = new IndexSearcher(directory, true);  
Directory directory = new SimpleFSDirectory(new File(path),new SimpleFSLockFactory());
IndexSearcher indexSearcher = new IndexSearcher(directory, true);

创徏查询条gQ这里徏一个最复杂的,Ҏ多个限定条g查找Qƈ 且有的限定条件放在多个field中查找,有精限定和范围限定Q?/p>

 


BooleanQuery bQuery = new BooleanQuery();   
Query query1 = null, query2 = null, query3 = null;   
BooleanClause.Occur[] flags = new BooleanClause.Occur[] {BooleanClause.Occur.SHOULD, BooleanClause.Occur.SHOULD };   
query1 = MultiFieldQueryParser.parse(params.get("keywords"),new String[] { "title", "content" }, flags, new WhitespaceAnalyzer());   
bQuery.add(query1, Occur.MUST); //query1是把关键字分别在title和content中匹配!   
query2 = new TermQuery(new Term("startgui", params.get("startgui")));   
bQuery.add(query2, Occur.MUST); //query2是精匹?nbsp;  
Long minPriceLong = Long.parseLong(params.get("minPrice"));   
Long maxPriceLong = Long.parseLong(params.get("maxPrice"));   
query5 = NumericRangeQuery.newLongRange("price", minPriceLong,   
maxPriceLong, true, true);   
bQuery.add(query5, Occur.MUST);  //query3是按范围匚w  
BooleanQuery bQuery = new BooleanQuery();
Query query1 = null, query2 = null, query3 = null;
BooleanClause.Occur[] flags = new BooleanClause.Occur[] {BooleanClause.Occur.SHOULD, BooleanClause.Occur.SHOULD };
query1 = MultiFieldQueryParser.parse(params.get("keywords"),new String[] { "title", "content" }, flags, new WhitespaceAnalyzer());
bQuery.add(query1, Occur.MUST); //query1是把关键字分别在title和content中匹配!
query2 = new TermQuery(new Term("startgui", params.get("startgui")));
bQuery.add(query2, Occur.MUST); //query2是精匹?br style="word-wrap: break-word;" /> Long minPriceLong = Long.parseLong(params.get("minPrice"));
Long maxPriceLong = Long.parseLong(params.get("maxPrice"));
query5 = NumericRangeQuery.newLongRange("price", minPriceLong,
maxPriceLong, true, true);
bQuery.add(query5, Occur.MUST);  //query3是按范围匚w
 

排序情况

 

SortField[] sortField = new SortField[] { SortField.FIELD_SCORE,new SortField(null, SortField.DOC, true) }; // 默认排序   
SortField sortPriceField = new SortField("sortPrice",SortField.LONG, sortPrice);   
sortField = new SortField[] { sortPriceField,SortField.FIELD_SCORE,new SortField(null, SortField.DOC, true) };  //按自定义h排序  
SortField[] sortField = new SortField[] { SortField.FIELD_SCORE,new SortField(null, SortField.DOC, true) }; // 默认排序
SortField sortPriceField = new SortField("sortPrice",SortField.LONG, sortPrice);
sortField = new SortField[] { sortPriceField,SortField.FIELD_SCORE,new SortField(null, SortField.DOC, true) };  //按自定义h排序
 

2.9最新查询方式,只是获取id

TopFieldDocs docs = indexSearcher.search(query, null, indexSearcher.maxDoc(), new Sort(sortField));   
ScoreDoc[] scoreDocs = docs.scoreDocs;   
docCount = scoreDocs.length;   
TopFieldDocs docs = indexSearcher.search(query, null, indexSearcher.maxDoc(), new Sort(sortField));
ScoreDoc[] scoreDocs = docs.scoreDocs;
docCount = scoreDocs.length; 

加入分页

List<documentQgt; docList = new ArrayList<documentQgt;();   
int max = ((startIndex + pageSize) >= docCount) ? docCount : (startIndex + pageSize); // max防止arrayindexoutofbounds   
for (int i = startIndex; i < max; i++) {    
    ScoreDoc scoredoc = scoreDocs[i];   
    documentQdoc = indexSearcher.doc(scoredoc.doc); // 新的使用Ҏ   
    docList.add(doc);   
}  
List<documentQgt; docList = new ArrayList<documentQgt;();
int max = ((startIndex + pageSize) >= docCount) ? docCount : (startIndex + pageSize); // max防止arrayindexoutofbounds
for (int i = startIndex; i < max; i++) { 
 ScoreDoc scoredoc = scoreDocs[i];
 documentQdoc = indexSearcher.doc(scoredoc.doc); // 新的使用Ҏ
 docList.add(doc);
}
 

循环解析docList中的documentQ取所需要的?/p>

doc.get("title");

...

7、关于分?br style="word-wrap: break-word;" /> 注意建立索引和搜索时候的analyzer必须一_而且建立索引和搜索时候目录也要保持一?/p>

lucene自带的一些分词器

StandardAnalyzer() 会按I格和标点符号划?/p>

WhitespaceAnalyzer() 会按I格划分

中文分词q里使用的是paoding的中文分?/p>

是先按词库划分,当词库中不存在时按二分法q行划分








西瓜 2010-07-12 11:49 发表评论
]]>
վ֩ģ壺 ƽ| | ʯ| | | ̩| | | | | | | Ͳ| | | | | | | Ͽ| ˱| | | ƾ| | | Ȫ| ʯ| | | ʲ| ߱| | | | ֬| ɽ| Խ| | ɽ| |