Thumbnail file hits. Based on a patch from D Bera
[beagle.git] / beagled / Lucene.Net / Search / BooleanQuery.cs
blobfb5a99bb4cce0c9f92aeae48a5481d65ee7b2b4a
1 /*
2 * Copyright 2004 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 using System;
17 using IndexReader = Lucene.Net.Index.IndexReader;
18 namespace Lucene.Net.Search
21 /// <summary>A Query that matches documents matching boolean combinations of other
22 /// queries, typically {@link TermQuery}s or {@link PhraseQuery}s.
23 /// </summary>
24 [Serializable]
25 public class BooleanQuery:Query, System.ICloneable
28 /// <summary> Default value is 1024. Use <code>Lucene.Net.maxClauseCount</code>
29 /// system property to override.
30 /// </summary>
31 public static int maxClauseCount = System.Int32.Parse(SupportClass.AppSettings.Get("Lucene.Net.maxClauseCount", "1024"));
33 /// <summary>Thrown when an attempt is made to add more than {@link
34 /// #GetMaxClauseCount()} clauses.
35 /// </summary>
36 [Serializable]
37 public class TooManyClauses:System.SystemException
41 /// <summary>Return the maximum number of clauses permitted, 1024 by default.
42 /// Attempts to add more than the permitted number of clauses cause {@link
43 /// TooManyClauses} to be thrown.
44 /// </summary>
45 public static int GetMaxClauseCount()
47 return maxClauseCount;
50 /// <summary>Set the maximum number of clauses permitted. </summary>
51 public static void SetMaxClauseCount(int maxClauseCount)
53 BooleanQuery.maxClauseCount = maxClauseCount;
56 private System.Collections.ArrayList clauses = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10));
58 /// <summary>Constructs an empty boolean query. </summary>
59 public BooleanQuery()
63 /// <summary>Adds a clause to a boolean query. Clauses may be:
64 /// <ul>
65 /// <li><code>required</code> which means that documents which <i>do not</i>
66 /// match this sub-query will <i>not</i> match the boolean query;
67 /// <li><code>prohibited</code> which means that documents which <i>do</i>
68 /// match this sub-query will <i>not</i> match the boolean query; or
69 /// <li>neither, in which case matched documents are neither prohibited from
70 /// nor required to match the sub-query. However, a document must match at
71 /// least 1 sub-query to match the boolean query.
72 /// </ul>
73 /// It is an error to specify a clause as both <code>required</code> and
74 /// <code>prohibited</code>.
75 ///
76 /// </summary>
77 /// <seealso cref="#GetMaxClauseCount()">
78 /// </seealso>
79 public virtual void Add(Query query, bool required, bool prohibited)
81 Add(new BooleanClause(query, required, prohibited));
84 /// <summary>Adds a clause to a boolean query.</summary>
85 /// <seealso cref="#GetMaxClauseCount()">
86 /// </seealso>
87 public virtual void Add(BooleanClause clause)
89 if (clauses.Count >= maxClauseCount)
90 throw new TooManyClauses();
92 clauses.Add(clause);
95 /// <summary>Returns the set of clauses in this query. </summary>
96 public virtual BooleanClause[] GetClauses()
98 return (BooleanClause[]) clauses.ToArray(typeof(BooleanClause));
101 [Serializable]
102 private class BooleanWeight : Weight
104 private void InitBlock(BooleanQuery enclosingInstance)
106 this.enclosingInstance = enclosingInstance;
108 private BooleanQuery enclosingInstance;
109 virtual public Query Query
113 return Enclosing_Instance;
117 virtual public float Value
121 return Enclosing_Instance.GetBoost();
125 public BooleanQuery Enclosing_Instance
129 return enclosingInstance;
133 private Searcher searcher;
134 private System.Collections.ArrayList weights = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10));
136 public BooleanWeight(BooleanQuery enclosingInstance, Searcher searcher)
138 InitBlock(enclosingInstance);
139 this.searcher = searcher;
140 for (int i = 0; i < Enclosing_Instance.clauses.Count; i++)
142 BooleanClause c = (BooleanClause) Enclosing_Instance.clauses[i];
143 weights.Add(c.query.CreateWeight(searcher));
147 public virtual float SumOfSquaredWeights()
149 float sum = 0.0f;
150 for (int i = 0; i < weights.Count; i++)
152 BooleanClause c = (BooleanClause) Enclosing_Instance.clauses[i];
153 Weight w = (Weight) weights[i];
154 if (!c.prohibited)
155 sum += w.SumOfSquaredWeights(); // sum sub weights
158 sum *= Enclosing_Instance.GetBoost() * Enclosing_Instance.GetBoost(); // boost each sub-weight
160 return sum;
164 public virtual void Normalize(float norm)
166 norm *= Enclosing_Instance.GetBoost(); // incorporate boost
167 for (int i = 0; i < weights.Count; i++)
169 BooleanClause c = (BooleanClause) Enclosing_Instance.clauses[i];
170 Weight w = (Weight) weights[i];
171 if (!c.prohibited)
172 w.Normalize(norm);
176 public virtual Scorer Scorer(IndexReader reader)
178 // First see if the (faster) ConjunctionScorer will work. This can be
179 // used when all clauses are required. Also, at this point a
180 // BooleanScorer cannot be embedded in a ConjunctionScorer, as the hits
181 // from a BooleanScorer are not always sorted by document number (sigh)
182 // and hence BooleanScorer cannot implement skipTo() correctly, which is
183 // required by ConjunctionScorer.
184 bool allRequired = true;
185 bool noneBoolean = true;
186 for (int i = 0; i < weights.Count; i++)
188 BooleanClause c = (BooleanClause) Enclosing_Instance.clauses[i];
189 if (!c.required)
190 allRequired = false;
191 if (c.query is BooleanQuery)
192 noneBoolean = false;
195 if (allRequired && noneBoolean)
197 // ConjunctionScorer is okay
198 ConjunctionScorer result = new ConjunctionScorer(Enclosing_Instance.GetSimilarity(searcher));
199 for (int i = 0; i < weights.Count; i++)
201 Weight w = (Weight) weights[i];
202 Scorer subScorer = w.Scorer(reader);
203 if (subScorer == null)
204 return null;
205 result.Add(subScorer);
207 return result;
210 // Use good-old BooleanScorer instead.
211 BooleanScorer result2 = new BooleanScorer(Enclosing_Instance.GetSimilarity(searcher));
213 for (int i = 0; i < weights.Count; i++)
215 BooleanClause c = (BooleanClause) Enclosing_Instance.clauses[i];
216 Weight w = (Weight) weights[i];
217 Scorer subScorer = w.Scorer(reader);
218 if (subScorer != null)
219 result2.Add(subScorer, c.required, c.prohibited);
220 else if (c.required)
221 return null;
224 return result2;
227 public virtual Explanation Explain(IndexReader reader, int doc)
229 Explanation sumExpl = new Explanation();
230 sumExpl.SetDescription("sum of:");
231 int coord = 0;
232 int maxCoord = 0;
233 float sum = 0.0f;
234 for (int i = 0; i < weights.Count; i++)
236 BooleanClause c = (BooleanClause) Enclosing_Instance.clauses[i];
237 Weight w = (Weight) weights[i];
238 Explanation e = w.Explain(reader, doc);
239 if (!c.prohibited)
240 maxCoord++;
241 if (e.GetValue() > 0)
243 if (!c.prohibited)
245 sumExpl.AddDetail(e);
246 sum += e.GetValue();
247 coord++;
249 else
251 return new Explanation(0.0f, "match prohibited");
254 else if (c.required)
256 return new Explanation(0.0f, "match required");
259 sumExpl.SetValue(sum);
261 if (coord == 1)
262 // only one clause matched
263 sumExpl = sumExpl.GetDetails()[0]; // eliminate wrapper
265 float coordFactor = Enclosing_Instance.GetSimilarity(searcher).Coord(coord, maxCoord);
266 if (coordFactor == 1.0f)
267 // coord is no-op
268 return sumExpl;
269 // eliminate wrapper
270 else
272 Explanation result = new Explanation();
273 result.SetDescription("product of:");
274 result.AddDetail(sumExpl);
275 result.AddDetail(new Explanation(coordFactor, "coord(" + coord + "/" + maxCoord + ")"));
276 result.SetValue(sum * coordFactor);
277 return result;
282 protected internal override Weight CreateWeight(Searcher searcher)
284 return new BooleanWeight(this, searcher);
287 public override Query Rewrite(IndexReader reader)
289 if (clauses.Count == 1)
291 // optimize 1-clause queries
292 BooleanClause c = (BooleanClause) clauses[0];
293 if (!c.prohibited)
295 // just return clause
297 Query query = c.query.Rewrite(reader); // rewrite first
299 if (GetBoost() != 1.0f)
301 // incorporate boost
302 if (query == c.query) {
303 // if rewrite was no-op
304 query = (Query) query.Clone(); // then clone before boost
306 query.SetBoost(GetBoost() * query.GetBoost());
309 return query;
313 BooleanQuery clone = null; // recursively rewrite
314 for (int i = 0; i < clauses.Count; i++)
316 BooleanClause c = (BooleanClause) clauses[i];
317 Query query = c.query.Rewrite(reader);
318 if (query != c.query)
320 // clause rewrote: must clone
321 if (clone == null)
322 clone = (BooleanQuery) this.Clone();
323 clone.clauses[i] = new BooleanClause(query, c.required, c.prohibited);
326 if (clone != null)
328 return clone; // some clauses rewrote
330 else
331 return this; // no clauses rewrote
335 public override System.Object Clone()
337 BooleanQuery clone = (BooleanQuery) base.Clone();
338 clone.clauses = (System.Collections.ArrayList) this.clauses.Clone();
339 return clone;
342 /// <summary>Prints a user-readable version of this query. </summary>
343 public override System.String ToString(System.String field)
345 System.Text.StringBuilder buffer = new System.Text.StringBuilder();
346 if (GetBoost() != 1.0)
348 buffer.Append("(");
351 for (int i = 0; i < clauses.Count; i++)
353 BooleanClause c = (BooleanClause) clauses[i];
354 if (c.prohibited)
355 buffer.Append("-");
356 else if (c.required)
357 buffer.Append("+");
359 Query subQuery = c.query;
360 if (subQuery is BooleanQuery)
362 // wrap sub-bools in parens
363 buffer.Append("(");
364 buffer.Append(c.query.ToString(field));
365 buffer.Append(")");
367 else
368 buffer.Append(c.query.ToString(field));
370 if (i != clauses.Count - 1)
371 buffer.Append(" ");
374 if (GetBoost() != 1.0)
376 System.Globalization.NumberFormatInfo nfi = new System.Globalization.CultureInfo("en-US", false).NumberFormat;
377 nfi.NumberDecimalDigits = 1;
379 buffer.Append(")^");
380 buffer.Append(GetBoost().ToString("N", nfi));
382 //buffer.Append(")^");
383 //buffer.Append(GetBoost());
386 return buffer.ToString();
389 /// <summary>Returns true iff <code>o</code> is equal to this. </summary>
390 public override bool Equals(System.Object o)
392 if (!(o is BooleanQuery))
393 return false;
394 BooleanQuery other = (BooleanQuery) o;
395 return (this.GetBoost() == other.GetBoost()) && this.clauses.Equals(other.clauses);
398 /// <summary>Returns a hash code value for this object.</summary>
399 public override int GetHashCode()
401 int boostInt = BitConverter.ToInt32(BitConverter.GetBytes(GetBoost()), 0);
402 return boostInt ^ clauses.GetHashCode();