/* * Copyright 2004 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using Document = Lucene.Net.Documents.Document; namespace Lucene.Net.Search { /// A ranked list of documents, used to hold search results. public sealed class Hits { private Weight weight; private Searcher searcher; private Filter filter = null; private Sort sort = null; private int length; // the total number of hits private System.Collections.ArrayList hitDocs = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10)); // cache of hits retrieved private HitDoc first; // head of LRU cache private HitDoc last; // tail of LRU cache private int numDocs = 0; // number cached private int maxDocs = 200; // max to cache internal Hits(Searcher s, Query q, Filter f) { weight = q.Weight(s); searcher = s; filter = f; GetMoreDocs(50); // retrieve 100 initially } internal Hits(Searcher s, Query q, Filter f, Sort o) { weight = q.Weight(s); searcher = s; filter = f; sort = o; GetMoreDocs(50); // retrieve 100 initially } /// Tries to add new documents to hitDocs. /// Ensures that the hit numbered min has been retrieved. /// 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); length = topDocs.totalHits; ScoreDoc[] scoreDocs = topDocs.scoreDocs; float scoreNorm = 1.0f; if (length > 0 && scoreDocs[0].score > 1.0f) { scoreNorm = 1.0f / scoreDocs[0].score; } int end = scoreDocs.Length < length?scoreDocs.Length:length; for (int i = hitDocs.Count; i < end; i++) { hitDocs.Add(new HitDoc(scoreDocs[i].score * scoreNorm, scoreDocs[i].doc)); } } /// Returns the total number of hits available in this set. public int Length() { return length; } /// Returns the stored fields of the nth document in this set. ///

Documents are cached, so that repeated requests for the same element may /// return the same Document object. ///

public Document Doc(int n) { HitDoc hitDoc = HitDoc(n); // Update LRU cache of documents Remove(hitDoc); // remove from list, if there AddToFront(hitDoc); // add to front of list if (numDocs > maxDocs) { // if cache is full HitDoc oldLast = last; Remove(last); // flush last oldLast.doc = null; // let doc get gc'd } if (hitDoc.doc == null) { hitDoc.doc = searcher.Doc(hitDoc.id); // cache miss: read document } return hitDoc.doc; } /// Returns the score for the nth document in this set. public float Score(int n) { return HitDoc(n).score; } /// Returns the id for the nth document in this set. public int Id(int n) { return HitDoc(n).id; } private HitDoc HitDoc(int n) { if (n >= length) { throw new System.IndexOutOfRangeException("Not a valid hit number: " + n); } if (n >= hitDocs.Count) { GetMoreDocs(n); } return (HitDoc) hitDocs[n]; } private void AddToFront(HitDoc hitDoc) { // insert at front of cache if (first == null) { last = hitDoc; } else { first.prev = hitDoc; } hitDoc.next = first; first = hitDoc; hitDoc.prev = null; numDocs++; } private void Remove(HitDoc hitDoc) { // remove from cache if (hitDoc.doc == null) { // it's not in the list return ; // abort } if (hitDoc.next == null) { last = hitDoc.prev; } else { hitDoc.next.prev = hitDoc.prev; } if (hitDoc.prev == null) { first = hitDoc.next; } else { hitDoc.prev.next = hitDoc.next; } numDocs--; } } sealed class HitDoc { internal float score; internal int id; internal Document doc = null; internal HitDoc next; // in doubly-linked cache internal HitDoc prev; // in doubly-linked cache internal HitDoc(float s, int i) { score = s; id = i; } } }