2 * Copyright 2004 The Apache Software Foundation
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
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.
25 public class BooleanQuery
:Query
, System
.ICloneable
28 /// <summary> Default value is 1024. Use <code>Lucene.Net.maxClauseCount</code>
29 /// system property to override.
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.
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.
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>
63 /// <summary>Adds a clause to a boolean query. Clauses may be:
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.
73 /// It is an error to specify a clause as both <code>required</code> and
74 /// <code>prohibited</code>.
77 /// <seealso cref="#GetMaxClauseCount()">
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()">
87 public virtual void Add(BooleanClause clause
)
89 if (clauses
.Count
>= maxClauseCount
)
90 throw new TooManyClauses();
95 /// <summary>Returns the set of clauses in this query. </summary>
96 public virtual BooleanClause
[] GetClauses()
98 return (BooleanClause
[]) clauses
.ToArray(typeof(BooleanClause
));
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()
150 for (int i
= 0; i
< weights
.Count
; i
++)
152 BooleanClause c
= (BooleanClause
) Enclosing_Instance
.clauses
[i
];
153 Weight w
= (Weight
) weights
[i
];
155 sum
+= w
.SumOfSquaredWeights(); // sum sub weights
158 sum
*= Enclosing_Instance
.GetBoost() * Enclosing_Instance
.GetBoost(); // boost each sub-weight
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
];
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
];
191 if (c
.query
is BooleanQuery
)
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)
205 result
.Add(subScorer
);
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
);
227 public virtual Explanation
Explain(IndexReader reader
, int doc
)
229 Explanation sumExpl
= new Explanation();
230 sumExpl
.SetDescription("sum of:");
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
);
241 if (e
.GetValue() > 0)
245 sumExpl
.AddDetail(e
);
251 return new Explanation(0.0f
, "match prohibited");
256 return new Explanation(0.0f
, "match required");
259 sumExpl
.SetValue(sum
);
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
)
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
);
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];
295 // just return clause
297 Query query
= c
.query
.Rewrite(reader
); // rewrite first
299 if (GetBoost() != 1.0f
)
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());
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
322 clone
= (BooleanQuery
) this.Clone();
323 clone
.clauses
[i
] = new BooleanClause(query
, c
.required
, c
.prohibited
);
328 return clone
; // some clauses rewrote
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();
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)
351 for (int i
= 0; i
< clauses
.Count
; i
++)
353 BooleanClause c
= (BooleanClause
) clauses
[i
];
359 Query subQuery
= c
.query
;
360 if (subQuery
is BooleanQuery
)
362 // wrap sub-bools in parens
364 buffer
.Append(c
.query
.ToString(field
));
368 buffer
.Append(c
.query
.ToString(field
));
370 if (i
!= clauses
.Count
- 1)
374 if (GetBoost() != 1.0)
376 System
.Globalization
.NumberFormatInfo nfi
= new System
.Globalization
.CultureInfo("en-US", false).NumberFormat
;
377 nfi
.NumberDecimalDigits
= 1;
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
))
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();