Lucene 分组统计方案——只能适合于小数据量,性能耗用太大

本文介绍了如何修改Lucene.Net的TopDocCollector来实现分组统计,并且指出这种方法仅适用于小数据量,因为性能消耗较大。通过修改Collect方法和构造函数,实现了根据Document类别进行统计的功能。

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

    转:http://tech.ddvip.com/2009-01/1231742812105589.html

     注意:本文内容仅适用于Lucene.Net,以2.1版为例,其它版本可能会有出入,Java版本差别更大一些。

  改动库先要有个思路。Lucene.Net的查询结果是一个Hits,而它有一个方法length可以得到总的结果。这个结果是一个精确值。这个值实际上是在TopDocCollector类的Collect方法计算出来的。要改精算为估算也就是在这里添加算法就可以了。

      修改一下其实也适合于 java版本的lucene,我就是用下面这个方法实现的:

       public  void Collect(int doc,float score)
    {
      if(score>0.0f)
      {
        totalHits++;
        if(hq.Size()<numHits||score>=minScore)
        {
          hq.Insert(new ScoreDoc(doc,score));
          minScore=((ScoreDoc)hq.Top()).score;//maintainminScore
        }
      }
    }

      这个方法中已经有了Document的id号,只要有办法拿到Document就能得到类别了。能拿到Document的类,IndexSearcher和IndexReader都可以。这里用IndexReader比较合算,因为IndexSearcher本身就包含IndexReader的。

  Collect方法会在几个地方被用到。都是Scorer一系的类中。比如TermScorer,BooleanScorer2等。所以按分类统计如果给Collect增加参数的话改动量可能会比较大。所以修改TopDocCollector的构造函数。

      private IndexReader reader;
    public TopDocCollector(int numHits,IndexReader reader)
    {

            this(numHits,newHitQueue(numHits),reader)
    }
    public TopDocCollector(int numHits,PriorityQueue hq,IndexReader reader)
    {
      this.numHits=numHits;
      this.hq=hq;
      this.reader=reader;
    }

      同时有两个调用构造函数的地方需要被修改。

  TopFieldDocCollector的构造函数:

      public TopFieldDocCollector(IndexReader reader,Sort sort,int numHits)
    {

           super(numHits,newFieldSortedHitQueue(reader,sort.fields,numHits),reader)
    }

   IndexSearcher的构造函数:

    public TopDocs Search(Weight weight,Filter filter,int nDocs)
    {
      
      if(nDocs<=0)
        //nullmightbereturnedfromhq.top()below.
        throw new System.ArgumentException("nDocsmustbe>0");
      TopDocCollector collector=new TopDocCollector(nDocs,this.reader);
      Search(weight,filter,collector);
      return collector.TopDocs();
    }

  现在TopDocCollector类就可以拿到分类了。

    public override void Collect(int doc,float score)
    {
      if(score>0.0f)
      {
        Documentd=reader.Document(doc);  //这里是最耗费,取一次大概耗费15-16毫秒
        int category=int.Parse(d.Get("category"));
        totalHits++;
        if(hq.Size()<numHits||score>=minScore)
        {
          hq.Insert(newScoreDoc(doc,score));
          minScore=((ScoreDoc)hq.Top()).score;//maintainminScore
        }
      }
    }

   最终这个统计的结构需要反映到Hits类去。返回结构和TopDocCollector的public  TopDocs TopDocs()方法有关。给TopDocs 增加一个字段:

  

public ArrayList<int,int> category_count;

  Collect方法改成:

    private ArrayList<int,int> category_count;

       category_count=new ArrayList<int,int>();
    public void Collect(int doc,float score)
    {
      if(score>0.0f)
      {
        Documentd=reader.Document(doc);
        int category=int.Parse(d.Get("category"));
        if(category_count.ContainsKey(category))
          category_count[category]++;
        else
          category_count.add(category,1);
        totalHits++;
        if(hq.Size()<numHits||score>=minScore)
        {
          hq.Insert(newScoreDoc(doc,score));
          minScore=((ScoreDoc)hq.Top()).score;//maintainminScore
        }
      }
    }

  TopDocs方法改成

    public TopDocsTopDocs()
    {
      ScoreDoc[]scoreDocs=new ScoreDoc[hq.Size()];
      for(inti=hq.Size()-1;i>=0;i--)
        //putdocsinarray
        scoreDocs[i]=(ScoreDoc)hq.Pop();
      
      float maxScore=(totalHits==0)?scoreDocs[0].score;
      TopDocs docs=new TopDocs(totalHits,scoreDocs,maxScore);
      docs.category_count=category_count;
      return docs;
    }

Hits类增加:

private ArrayList<int,int> category_count;
    public ArrayList<int,int> Category_Count{
      get{
        return category_count;
      }
    }

  同时修改:

    privatevoid GetMoreDocs(int min)
    {
      if(hitDocs.Count>min)
      {
        min=hitDocs.Count;
      }
      
      intn=min*2;//double#retrieved
      TopDocs topDocs=(sort==null)?searcher.Search(weight,filter,n):searcher.Search(weight,filter,n,sort);
      category_count=topDocs.category_count;
      length=topDocs.totalHits;
      ScoreDoc[]scoreDocs=topDocs.scoreDocs;
      
      floatscoreNorm=1.0f;
      
      if(length>0&&topDocs.GetMaxScore()>1.0f)
      {
        scoreNorm=1.0f/topDocs.GetMaxScore();
      }
      
      intend=scoreDocs.Length<length?scoreDocs.Length:length;
      for(inti=hitDocs.Count;i<end;i++)
      {
        hitDocs.Add(newHitDoc(scoreDocs[i].score*scoreNorm,scoreDocs[i].doc));
      }
    }

  至此就OK了。从结果中取的时候,比如ID为1的分类,则

  hits.Category_Count[1]就出来了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值