天天看點

[轉載]Lucene.Net 按類别統計搜尋結果數

    如何按類别統計搜尋結果數?是不是要循環一個個類别去查詢出總數啊?

    以Lucene.Net現在的API,隻能這樣做。當然這樣做一般會帶來性能問題,是以更好的解決方案就是改動庫檔案了。

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

改動庫先要有個思路。Lucene.Net的查詢結果是一個Hits,而它有一個方法length可以得到總的結果。這個結果是一個精确值。這個值實際上是在TopDocCollector類的Collect方法計算出來的。要改精算為估算也就是在這裡添加算法就可以了。 

        public override 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; // maintain minScore

                }

            }

        }

    這個方法中已經有了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, new HitQueue(numHits), reader)

        internal 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)

            : base(numHits, new FieldSortedHitQueue(reader, sort.fields, numHits), reader) {

IndexSearcher的構造函數:

        public override TopDocs Search(Weight weight, Filter filter, int nDocs)

            if (nDocs <= 0)

                // null might be returned from hq.top() below.

                throw new System.ArgumentException("nDocs must be > 0");

            TopDocCollector collector = new TopDocCollector(nDocs, this.reader);

            Search(weight, filter, collector);

            return collector.TopDocs();

現在TopDocCollector類就可以拿到分類了。 

                Document d = reader.Document(doc);

                int category = int.Parse(d.Get("category"));

最終這個統計的結構需要反映到Hits類去。傳回結構和TopDocCollector的public virtual TopDocs TopDocs()方法有關。給TopDocs 增加一個字段:

public System.Collections.Generic.Dictionary<int, int> category_count;

Collect方法改成: 

        private System.Collections.Generic.Dictionary<int, int> category_count = new System.Collections.Generic.Dictionary<int,int>();

                if (category_count.ContainsKey(category))

                    category_count[category]++;

                else

                    category_count.Add(category, 1);

TopDocs方法改成

        public virtual TopDocs TopDocs()

            ScoreDoc[] scoreDocs = new ScoreDoc[hq.Size()];

            for (int i = hq.Size() - 1; i >= 0; i--)

                // put docs in array

                scoreDocs = (ScoreDoc) hq.Pop();

            float maxScore = (totalHits == 0) ? System.Single.NegativeInfinity : scoreDocs[0].score;

            TopDocs docs = new TopDocs(totalHits, scoreDocs, maxScore);

            docs.category_count = category_count;

            return docs;

Hits類增加: 

        private Dictionary<int, int> category_count;

        public Dictionary<int, int> Category_Count {

            get {

                return category_count;

同時修改:

        private void  GetMoreDocs(int min)

            if (hitDocs.Count > min)

                min = hitDocs.Count;

            int n = 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;

            float scoreNorm = 1.0f;

            if (length > 0 && topDocs.GetMaxScore() > 1.0f)

                scoreNorm = 1.0f / topDocs.GetMaxScore();

            int end = scoreDocs.Length < length?scoreDocs.Length:length;

            for (int i = hitDocs.Count; i < end; i++)

                hitDocs.Add(new HitDoc(scoreDocs.score * scoreNorm, scoreDocs.doc));

至此就OK了。從結果中取的時候,比如ID為1的分類,則hits.Category_Count[1]就出來了。