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.Set;
22
23 import org.apache.lucene.index.Term;
24 import org.apache.lucene.index.TermDocs;
25 import org.apache.lucene.index.IndexReader;
26 import org.apache.lucene.search.Explanation.IDFExplanation;
27 import org.apache.lucene.util.ToStringUtils;
28
29 /** A Query that matches documents containing a term.
30 This may be combined with other terms with a {@link BooleanQuery}.
31 */
32 public class TermQuery extends Query {
33 private Term term;
34
35 private class TermWeight extends Weight {
36 private Similarity similarity;
37 private float value;
38 private float idf;
39 private float queryNorm;
40 private float queryWeight;
41 private IDFExplanation idfExp;
42
43 public TermWeight(Searcher searcher)
44 throws IOException {
45 this.similarity = getSimilarity(searcher);
46 idfExp = similarity.idfExplain(term, searcher);
47 idf = idfExp.getIdf();
48 }
49
50 @Override
51 public String toString() { return "weight(" + TermQuery.this + ")"; }
52
53 @Override
54 public Query getQuery() { return TermQuery.this; }
55
56 @Override
57 public float getValue() { return value; }
58
59 @Override
60 public float sumOfSquaredWeights() {
61 queryWeight = idf * getBoost(); // compute query weight
62 return queryWeight * queryWeight; // square it
63 }
64
65 @Override
66 public void normalize(float queryNorm) {
67 this.queryNorm = queryNorm;
68 queryWeight *= queryNorm; // normalize query weight
69 value = queryWeight * idf; // idf for document
70 }
71
72 @Override
73 public Scorer scorer(IndexReader reader, boolean scoreDocsInOrder, boolean topScorer) throws IOException {
74 TermDocs termDocs = reader.termDocs(term);
75
76 if (termDocs == null)
77 return null;
78
79 return new TermScorer(this, termDocs, similarity, reader.norms(term.field()));
80 }
81
82 @Override
83 public Explanation explain(IndexReader reader, int doc)
84 throws IOException {
85
86 ComplexExplanation result = new ComplexExplanation();
87 result.setDescription("weight("+getQuery()+" in "+doc+"), product of:");
88
89 Explanation expl = new Explanation(idf, idfExp.explain());
90
91 // explain query weight
92 Explanation queryExpl = new Explanation();
93 queryExpl.setDescription("queryWeight(" + getQuery() + "), product of:");
94
95 Explanation boostExpl = new Explanation(getBoost(), "boost");
96 if (getBoost() != 1.0f)
97 queryExpl.addDetail(boostExpl);
98 queryExpl.addDetail(expl);
99
100 Explanation queryNormExpl = new Explanation(queryNorm,"queryNorm");
101 queryExpl.addDetail(queryNormExpl);
102
103 queryExpl.setValue(boostExpl.getValue() *
104 expl.getValue() *
105 queryNormExpl.getValue());
106
107 result.addDetail(queryExpl);
108
109 // explain field weight
110 String field = term.field();
111 ComplexExplanation fieldExpl = new ComplexExplanation();
112 fieldExpl.setDescription("fieldWeight("+term+" in "+doc+
113 "), product of:");
114
115 Explanation tfExplanation = new Explanation();
116 int tf = 0;
117 TermDocs termDocs = reader.termDocs(term);
118 if (termDocs != null) {
119 try {
120 if (termDocs.skipTo(doc) && termDocs.doc() == doc) {
121 tf = termDocs.freq();
122 }
123 } finally {
124 termDocs.close();
125 }
126 tfExplanation.setValue(similarity.tf(tf));
127 tfExplanation.setDescription("tf(termFreq("+term+")="+tf+")");
128 } else {
129 tfExplanation.setValue(0.0f);
130 tfExplanation.setDescription("no matching term");
131 }
132 fieldExpl.addDetail(tfExplanation);
133 fieldExpl.addDetail(expl);
134
135 Explanation fieldNormExpl = new Explanation();
136 byte[] fieldNorms = reader.norms(field);
137 float fieldNorm =
138 fieldNorms!=null ? Similarity.decodeNorm(fieldNorms[doc]) : 1.0f;
139 fieldNormExpl.setValue(fieldNorm);
140 fieldNormExpl.setDescription("fieldNorm(field="+field+", doc="+doc+")");
141 fieldExpl.addDetail(fieldNormExpl);
142
143 fieldExpl.setMatch(Boolean.valueOf(tfExplanation.isMatch()));
144 fieldExpl.setValue(tfExplanation.getValue() *
145 expl.getValue() *
146 fieldNormExpl.getValue());
147
148 result.addDetail(fieldExpl);
149 result.setMatch(fieldExpl.getMatch());
150
151 // combine them
152 result.setValue(queryExpl.getValue() * fieldExpl.getValue());
153
154 if (queryExpl.getValue() == 1.0f)
155 return fieldExpl;
156
157 return result;
158 }
159 }
160
161 /** Constructs a query for the term <code>t</code>. */
162 public TermQuery(Term t) {
163 term = t;
164 }
165
166 /** Returns the term of this query. */
167 public Term getTerm() { return term; }
168
169 @Override
170 public Weight createWeight(Searcher searcher) throws IOException {
171 return new TermWeight(searcher);
172 }
173
174 @Override
175 public void extractTerms(Set<Term> terms) {
176 terms.add(getTerm());
177 }
178
179 /** Prints a user-readable version of this query. */
180 @Override
181 public String toString(String field) {
182 StringBuilder buffer = new StringBuilder();
183 if (!term.field().equals(field)) {
184 buffer.append(term.field());
185 buffer.append(":");
186 }
187 buffer.append(term.text());
188 buffer.append(ToStringUtils.boost(getBoost()));
189 return buffer.toString();
190 }
191
192 /** Returns true iff <code>o</code> is equal to this. */
193 @Override
194 public boolean equals(Object o) {
195 if (!(o instanceof TermQuery))
196 return false;
197 TermQuery other = (TermQuery)o;
198 return (this.getBoost() == other.getBoost())
199 && this.term.equals(other.term);
200 }
201
202 /** Returns a hash code value for this object.*/
203 @Override
204 public int hashCode() {
205 return Float.floatToIntBits(getBoost()) ^ term.hashCode();
206 }
207
208 }