pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / src / java / org / apache / lucene / search / TermQuery.java
1 package org.apache.lucene.search;
2
3 /**
4  * Licensed to the Apache Software Foundation (ASF) under one or more
5  * contributor license agreements.  See the NOTICE file distributed with
6  * this work for additional information regarding copyright ownership.
7  * The ASF licenses this file to You under the Apache License, Version 2.0
8  * (the "License"); you may not use this file except in compliance with
9  * the License.  You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 import java.io.IOException;
21 import java.util.HashSet;
22 import java.util.Set;
23
24 import org.apache.lucene.index.Term;
25 import org.apache.lucene.index.TermDocs;
26 import org.apache.lucene.index.IndexReader;
27 import org.apache.lucene.search.Explanation.IDFExplanation;
28 import org.apache.lucene.util.ReaderUtil;
29 import org.apache.lucene.util.ToStringUtils;
30
31 /** A Query that matches documents containing a term.
32   This may be combined with other terms with a {@link BooleanQuery}.
33   */
34 public class TermQuery extends Query {
35   private Term term;
36
37   private class TermWeight extends Weight {
38     private final Similarity similarity;
39     private float value;
40     private float idf;
41     private float queryNorm;
42     private float queryWeight;
43     private IDFExplanation idfExp;
44     private final Set<Integer> hash;
45
46     public TermWeight(Searcher searcher)
47       throws IOException {
48       this.similarity = getSimilarity(searcher);
49       if (searcher instanceof IndexSearcher) {
50         hash = new HashSet<Integer>();
51         IndexReader ir = ((IndexSearcher)searcher).getIndexReader();
52         final int dfSum[] = new int[1];
53         new ReaderUtil.Gather(ir) {
54           @Override
55           protected void add(int base, IndexReader r) throws IOException {
56             int df = r.docFreq(term);
57             dfSum[0] += df;
58             if (df > 0) {
59               hash.add(r.hashCode());
60             }
61           }
62         }.run();
63
64         idfExp = similarity.idfExplain(term, searcher, dfSum[0]);
65       } else {
66         idfExp = similarity.idfExplain(term, searcher);
67         hash = null;
68       }
69
70       idf = idfExp.getIdf();
71     }
72
73     @Override
74     public String toString() { return "weight(" + TermQuery.this + ")"; }
75
76     @Override
77     public Query getQuery() { return TermQuery.this; }
78
79     @Override
80     public float getValue() { return value; }
81
82     @Override
83     public float sumOfSquaredWeights() {
84       queryWeight = idf * getBoost();             // compute query weight
85       return queryWeight * queryWeight;           // square it
86     }
87
88     @Override
89     public void normalize(float queryNorm) {
90       this.queryNorm = queryNorm;
91       queryWeight *= queryNorm;                   // normalize query weight
92       value = queryWeight * idf;                  // idf for document
93     }
94
95     @Override
96     public Scorer scorer(IndexReader reader, boolean scoreDocsInOrder, boolean topScorer) throws IOException {
97       // only use the early exit condition if we have an atomic reader, because Lucene 3.x still supports non-atomic readers here:
98       if (hash != null && reader.getSequentialSubReaders() == null && !hash.contains(reader.hashCode())) {
99         return null;
100       }
101       
102       TermDocs termDocs = reader.termDocs(term);
103
104       if (termDocs == null)
105         return null;
106
107       return new TermScorer(this, termDocs, similarity, reader.norms(term.field()));
108     }
109
110     @Override
111     public Explanation explain(IndexReader reader, int doc)
112       throws IOException {
113
114       ComplexExplanation result = new ComplexExplanation();
115       result.setDescription("weight("+getQuery()+" in "+doc+"), product of:");
116
117       Explanation expl = new Explanation(idf, idfExp.explain());
118
119       // explain query weight
120       Explanation queryExpl = new Explanation();
121       queryExpl.setDescription("queryWeight(" + getQuery() + "), product of:");
122
123       Explanation boostExpl = new Explanation(getBoost(), "boost");
124       if (getBoost() != 1.0f)
125         queryExpl.addDetail(boostExpl);
126       queryExpl.addDetail(expl);
127
128       Explanation queryNormExpl = new Explanation(queryNorm,"queryNorm");
129       queryExpl.addDetail(queryNormExpl);
130
131       queryExpl.setValue(boostExpl.getValue() *
132                          expl.getValue() *
133                          queryNormExpl.getValue());
134
135       result.addDetail(queryExpl);
136
137       // explain field weight
138       String field = term.field();
139       ComplexExplanation fieldExpl = new ComplexExplanation();
140       fieldExpl.setDescription("fieldWeight("+term+" in "+doc+
141                                "), product of:");
142
143       Explanation tfExplanation = new Explanation();
144       int tf = 0;
145       TermDocs termDocs = reader.termDocs(term);
146       if (termDocs != null) {
147         try {
148           if (termDocs.skipTo(doc) && termDocs.doc() == doc) {
149             tf = termDocs.freq();
150           }
151         } finally {
152           termDocs.close();
153         }
154         tfExplanation.setValue(similarity.tf(tf));
155         tfExplanation.setDescription("tf(termFreq("+term+")="+tf+")");
156       } else {
157         tfExplanation.setValue(0.0f);
158         tfExplanation.setDescription("no matching term");
159       }
160       fieldExpl.addDetail(tfExplanation);
161       fieldExpl.addDetail(expl);
162
163       Explanation fieldNormExpl = new Explanation();
164       byte[] fieldNorms = reader.norms(field);
165       float fieldNorm =
166         fieldNorms!=null ? similarity.decodeNormValue(fieldNorms[doc]) : 1.0f;
167       fieldNormExpl.setValue(fieldNorm);
168       fieldNormExpl.setDescription("fieldNorm(field="+field+", doc="+doc+")");
169       fieldExpl.addDetail(fieldNormExpl);
170       
171       fieldExpl.setMatch(Boolean.valueOf(tfExplanation.isMatch()));
172       fieldExpl.setValue(tfExplanation.getValue() *
173                          expl.getValue() *
174                          fieldNormExpl.getValue());
175
176       result.addDetail(fieldExpl);
177       result.setMatch(fieldExpl.getMatch());
178       
179       // combine them
180       result.setValue(queryExpl.getValue() * fieldExpl.getValue());
181
182       if (queryExpl.getValue() == 1.0f)
183         return fieldExpl;
184
185       return result;
186     }
187   }
188
189   /** Constructs a query for the term <code>t</code>. */
190   public TermQuery(Term t) {
191     term = t;
192   }
193
194   /** Returns the term of this query. */
195   public Term getTerm() { return term; }
196
197   @Override
198   public Weight createWeight(Searcher searcher) throws IOException {
199     return new TermWeight(searcher);
200   }
201
202   @Override
203   public void extractTerms(Set<Term> terms) {
204     terms.add(getTerm());
205   }
206
207   /** Prints a user-readable version of this query. */
208   @Override
209   public String toString(String field) {
210     StringBuilder buffer = new StringBuilder();
211     if (!term.field().equals(field)) {
212       buffer.append(term.field());
213       buffer.append(":");
214     }
215     buffer.append(term.text());
216     buffer.append(ToStringUtils.boost(getBoost()));
217     return buffer.toString();
218   }
219
220   /** Returns true iff <code>o</code> is equal to this. */
221   @Override
222   public boolean equals(Object o) {
223     if (!(o instanceof TermQuery))
224       return false;
225     TermQuery other = (TermQuery)o;
226     return (this.getBoost() == other.getBoost())
227       && this.term.equals(other.term);
228   }
229
230   /** Returns a hash code value for this object.*/
231   @Override
232   public int hashCode() {
233     return Float.floatToIntBits(getBoost()) ^ term.hashCode();
234   }
235
236 }