Hits.java
001 package gate.creole.annic.apache.lucene.search;
002 
003 /**
004  * Copyright 2004 The Apache Software Foundation
005  *
006  * Licensed under the Apache License, Version 2.0 (the "License");
007  * you may not use this file except in compliance with the License.
008  * You may obtain a copy of the License at
009  *
010  *     http://www.apache.org/licenses/LICENSE-2.0
011  *
012  * Unless required by applicable law or agreed to in writing, software
013  * distributed under the License is distributed on an "AS IS" BASIS,
014  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015  * See the License for the specific language governing permissions and
016  * limitations under the License.
017  */
018 
019 import java.io.IOException;
020 import java.util.Vector;
021 
022 import gate.creole.annic.apache.lucene.document.Document;
023 
024 /** A ranked list of documents, used to hold search results. */
025 @SuppressWarnings({"rawtypes","unchecked"})
026 public final class Hits {
027   private Query query;
028   private Searcher searcher;
029   private Filter filter = null;
030   private Sort sort = null;
031 
032   private int length;          // the total number of hits
033   private Vector hitDocs = new Vector();    // cache of hits retrieved
034 
035   private HitDoc first;         // head of LRU cache
036   private HitDoc last;          // tail of LRU cache
037   private int numDocs = 0;      // number cached
038   private int maxDocs = 200;    // max to cache
039 
040   Hits(Searcher s, Query q, Filter fthrows IOException {
041     query = q;
042     searcher = s;
043     filter = f;
044     getMoreDocs(50)// retrieve 100 initially
045   }
046 
047   Hits(Searcher s, Query q, Filter f, Sort othrows IOException {
048     query = q;
049     searcher = s;
050     filter = f;
051     sort = o;
052     getMoreDocs(50)// retrieve 100 initially
053   }
054 
055   /**
056    * Tries to add new documents to hitDocs.
057    * Ensures that the hit numbered <code>min</code> has been retrieved.
058    */
059   private final void getMoreDocs(int minthrows IOException {
060     if (hitDocs.size() > min) {
061       min = hitDocs.size();
062     }
063 
064     int n = min * 2;  // double # retrieved
065     TopDocs topDocs = (sort == null? searcher.search(query, filter, n: searcher.search(query, filter, n, sort);
066     length = topDocs.totalHits;
067     ScoreDoc[] scoreDocs = topDocs.scoreDocs;
068 
069     float scoreNorm = 1.0f;
070     if (length > && scoreDocs[0].score > 1.0f) {
071       scoreNorm = 1.0f / scoreDocs[0].score;
072     }
073 
074     int end = scoreDocs.length < length ? scoreDocs.length : length;
075     for (int i = hitDocs.size(); i < end; i++) {
076       hitDocs.addElement(new HitDoc(scoreDocs[i].score * scoreNorm,
077                                     scoreDocs[i].doc));
078     }
079   }
080 
081   /** Returns the total number of hits available in this set. */
082   public final int length() {
083     return length;
084   }
085 
086   /** Returns the stored fields of the n<sup>th</sup> document in this set.
087    <p>Documents are cached, so that repeated requests for the same element may
088    return the same Document object. */
089   public final Document doc(int nthrows IOException {
090     HitDoc hitDoc = hitDoc(n);
091 
092     // Update LRU cache of documents
093     remove(hitDoc);               // remove from list, if there
094     addToFront(hitDoc);           // add to front of list
095     if (numDocs > maxDocs) {      // if cache is full
096       HitDoc oldLast = last;
097       remove(last);             // flush last
098       oldLast.doc = null;       // let doc get gc'd
099     }
100 
101     if (hitDoc.doc == null) {
102       hitDoc.doc = searcher.doc(hitDoc.id);  // cache miss: read document
103     }
104 
105     return hitDoc.doc;
106   }
107 
108   /** Returns the score for the nth document in this set. */
109   public final float score(int nthrows IOException {
110     return hitDoc(n).score;
111   }
112 
113   /** Returns the id for the nth document in this set. */
114   public final int id(int nthrows IOException {
115     return hitDoc(n).id;
116   }
117 
118 
119   private final HitDoc hitDoc(int nthrows IOException {
120     if (n >= length) {
121       throw new IndexOutOfBoundsException("Not a valid hit number: " + n);
122     }
123 
124     if (n >= hitDocs.size()) {
125       getMoreDocs(n);
126     }
127 
128     return (HitDochitDocs.elementAt(n);
129   }
130 
131   private final void addToFront(HitDoc hitDoc) {  // insert at front of cache
132     if (first == null) {
133       last = hitDoc;
134     else {
135       first.prev = hitDoc;
136     }
137 
138     hitDoc.next = first;
139     first = hitDoc;
140     hitDoc.prev = null;
141 
142     numDocs++;
143   }
144 
145   private final void remove(HitDoc hitDoc) {    // remove from cache
146     if (hitDoc.doc == null) {     // it's not in the list
147       return;            // abort
148     }
149 
150     if (hitDoc.next == null) {
151       last = hitDoc.prev;
152     else {
153       hitDoc.next.prev = hitDoc.prev;
154     }
155 
156     if (hitDoc.prev == null) {
157       first = hitDoc.next;
158     else {
159       hitDoc.prev.next = hitDoc.next;
160     }
161 
162     numDocs--;
163   }
164 }
165 
166 final class HitDoc {
167   float score;
168   int id;
169   Document doc = null;
170 
171   HitDoc next;  // in doubly-linked cache
172   HitDoc prev;  // in doubly-linked cache
173 
174   HitDoc(float s, int i) {
175     score = s;
176     id = i;
177   }
178 }