BooleanQuery.cs
上传用户:zhangkuixh
上传日期:2013-09-30
资源大小:5473k
文件大小:19k
源码类别:

搜索引擎

开发平台:

C#

  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.
  15.  */
  16. using System;
  17. using IndexReader = Lucene.Net.Index.IndexReader;
  18. using ToStringUtils = Lucene.Net.Util.ToStringUtils;
  19. namespace Lucene.Net.Search
  20. {
  21. /// <summary>A Query that matches documents matching boolean combinations of other
  22. /// queries, e.g. {@link TermQuery}s, {@link PhraseQuery}s or other
  23. /// BooleanQuerys.
  24. /// </summary>
  25. [Serializable]
  26. public class BooleanQuery : Query, System.ICloneable
  27. {
  28. private class AnonymousClassSimilarityDelegator : SimilarityDelegator
  29. {
  30. private void  InitBlock(BooleanQuery enclosingInstance)
  31. {
  32. this.enclosingInstance = enclosingInstance;
  33. }
  34. private BooleanQuery enclosingInstance;
  35. public BooleanQuery Enclosing_Instance
  36. {
  37. get
  38. {
  39. return enclosingInstance;
  40. }
  41. }
  42. internal AnonymousClassSimilarityDelegator(BooleanQuery enclosingInstance, Lucene.Net.Search.Similarity Param1):base(Param1)
  43. {
  44. InitBlock(enclosingInstance);
  45. }
  46. public override float Coord(int overlap, int maxOverlap)
  47. {
  48. return 1.0f;
  49. }
  50. }
  51. /// <deprecated> use {@link #SetMaxClauseCount(int)} instead
  52. /// </deprecated>
  53. public static int maxClauseCount = 1024;
  54. /// <summary>Thrown when an attempt is made to add more than {@link
  55. /// #GetMaxClauseCount()} clauses. This typically happens if
  56. /// a PrefixQuery, FuzzyQuery, WildcardQuery, or RangeQuery 
  57. /// is expanded to many terms during search. 
  58. /// </summary>
  59. [Serializable]
  60. public class TooManyClauses : System.SystemException
  61. {
  62. }
  63. /// <summary>Return the maximum number of clauses permitted, 1024 by default.
  64. /// Attempts to add more than the permitted number of clauses cause {@link
  65. /// TooManyClauses} to be thrown.
  66. /// </summary>
  67. /// <seealso cref="SetMaxClauseCount(int)">
  68. /// </seealso>
  69. public static int GetMaxClauseCount()
  70. {
  71. return maxClauseCount;
  72. }
  73. /// <summary>Set the maximum number of clauses permitted per BooleanQuery.
  74. /// Default value is 1024.
  75. /// <p>TermQuery clauses are generated from for example prefix queries and
  76. /// fuzzy queries. Each TermQuery needs some buffer space during search,
  77. /// so this parameter indirectly controls the maximum buffer requirements for
  78. /// query search.
  79. /// <p>When this parameter becomes a bottleneck for a Query one can use a
  80. /// Filter. For example instead of a {@link RangeQuery} one can use a
  81. /// {@link RangeFilter}.
  82. /// <p>Normally the buffers are allocated by the JVM. When using for example
  83. /// {@link Lucene.Net.store.MMapDirectory} the buffering is left to
  84. /// the operating system.
  85. /// </summary>
  86. public static void  SetMaxClauseCount(int maxClauseCount)
  87. {
  88. if (maxClauseCount < 1)
  89. throw new System.ArgumentException("maxClauseCount must be >= 1");
  90. BooleanQuery.maxClauseCount = maxClauseCount;
  91. }
  92. private System.Collections.ArrayList clauses = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10));
  93. private bool disableCoord;
  94. /// <summary>Constructs an empty boolean query. </summary>
  95. public BooleanQuery()
  96. {
  97. }
  98. /// <summary>Constructs an empty boolean query.
  99. /// 
  100. /// {@link Similarity#Coord(int,int)} may be disabled in scoring, as
  101. /// appropriate. For example, this score factor does not make sense for most
  102. /// automatically generated queries, like {@link WildcardQuery} and {@link
  103. /// FuzzyQuery}.
  104. /// 
  105. /// </summary>
  106. /// <param name="disableCoord">disables {@link Similarity#Coord(int,int)} in scoring.
  107. /// </param>
  108. public BooleanQuery(bool disableCoord)
  109. {
  110. this.disableCoord = disableCoord;
  111. }
  112. /// <summary>Returns true iff {@link Similarity#Coord(int,int)} is disabled in
  113. /// scoring for this query instance.
  114. /// </summary>
  115. /// <seealso cref="BooleanQuery(boolean)">
  116. /// </seealso>
  117. public virtual bool IsCoordDisabled()
  118. {
  119. return disableCoord;
  120. }
  121. // Implement coord disabling.
  122. // Inherit javadoc.
  123. public override Similarity GetSimilarity(Searcher searcher)
  124. {
  125. Similarity result = base.GetSimilarity(searcher);
  126. if (disableCoord)
  127. {
  128. // disable coord as requested
  129. result = new AnonymousClassSimilarityDelegator(this, result);
  130. }
  131. return result;
  132. }
  133. /// <summary> Specifies a minimum number of the optional BooleanClauses
  134. /// which must be satisifed.
  135. /// 
  136. /// <p>
  137. /// By default no optional clauses are neccessary for a match
  138. /// (unless there are no required clauses).  If this method is used,
  139. /// then the specified numebr of clauses is required.
  140. /// </p>
  141. /// <p>
  142. /// Use of this method is totally independant of specifying that
  143. /// any specific clauses are required (or prohibited).  This number will
  144. /// only be compared against the number of matching optional clauses.
  145. /// </p>
  146. /// <p>
  147. /// EXPERT NOTE: Using this method will force the use of BooleanWeight2,
  148. /// regardless of wether setUseScorer14(true) has been called.
  149. /// </p>
  150. /// 
  151. /// </summary>
  152. /// <param name="min">the number of optional clauses that must match
  153. /// </param>
  154. /// <seealso cref="setUseScorer14">
  155. /// </seealso>
  156. public virtual void  SetMinimumNumberShouldMatch(int min)
  157. {
  158. this.minNrShouldMatch = min;
  159. }
  160. protected internal int minNrShouldMatch = 0;
  161. /// <summary> Gets the minimum number of the optional BooleanClauses
  162. /// which must be satisifed.
  163. /// </summary>
  164. public virtual int GetMinimumNumberShouldMatch()
  165. {
  166. return minNrShouldMatch;
  167. }
  168. /// <summary>Adds a clause to a boolean query.  Clauses may be:
  169. /// <ul>
  170. /// <li><code>required</code> which means that documents which <i>do not</i>
  171. /// match this sub-query will <i>not</i> match the boolean query;
  172. /// <li><code>prohibited</code> which means that documents which <i>do</i>
  173. /// match this sub-query will <i>not</i> match the boolean query; or
  174. /// <li>neither, in which case matched documents are neither prohibited from
  175. /// nor required to match the sub-query. However, a document must match at
  176. /// least 1 sub-query to match the boolean query.
  177. /// </ul>
  178. /// It is an error to specify a clause as both <code>required</code> and
  179. /// <code>prohibited</code>.
  180. /// 
  181. /// </summary>
  182. /// <deprecated> use {@link #Add(Query, BooleanClause.Occur)} instead:
  183. /// <ul>
  184. /// <li>For add(query, true, false) use add(query, BooleanClause.Occur.MUST)
  185. /// <li>For add(query, false, false) use add(query, BooleanClause.Occur.SHOULD)
  186. /// <li>For add(query, false, true) use add(query, BooleanClause.Occur.MUST_NOT)
  187. /// </ul>
  188. /// </deprecated>
  189. public virtual void  Add(Query query, bool required, bool prohibited)
  190. {
  191. Add(new BooleanClause(query, required, prohibited));
  192. }
  193. /// <summary>Adds a clause to a boolean query.
  194. /// 
  195. /// </summary>
  196. /// <throws>  TooManyClauses if the new number of clauses exceeds the maximum clause number </throws>
  197. /// <seealso cref="GetMaxClauseCount()">
  198. /// </seealso>
  199. public virtual void  Add(Query query, BooleanClause.Occur occur)
  200. {
  201. Add(new BooleanClause(query, occur));
  202. }
  203. /// <summary>Adds a clause to a boolean query.</summary>
  204. /// <throws>  TooManyClauses if the new number of clauses exceeds the maximum clause number </throws>
  205. /// <seealso cref="GetMaxClauseCount()">
  206. /// </seealso>
  207. public virtual void  Add(BooleanClause clause)
  208. {
  209. if (clauses.Count >= maxClauseCount)
  210. throw new TooManyClauses();
  211. clauses.Add(clause);
  212. }
  213. /// <summary>Returns the set of clauses in this query. </summary>
  214. public virtual BooleanClause[] GetClauses()
  215. {
  216. return (BooleanClause[]) clauses.ToArray(typeof(BooleanClause));
  217. }
  218. [Serializable]
  219. private class BooleanWeight : Weight
  220. {
  221. private void  InitBlock(BooleanQuery enclosingInstance)
  222. {
  223. this.enclosingInstance = enclosingInstance;
  224. }
  225. private BooleanQuery enclosingInstance;
  226. public BooleanQuery Enclosing_Instance
  227. {
  228. get
  229. {
  230. return enclosingInstance;
  231. }
  232. }
  233. protected internal Similarity similarity;
  234. protected internal System.Collections.ArrayList weights = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10));
  235. public BooleanWeight(BooleanQuery enclosingInstance, Searcher searcher)
  236. {
  237. InitBlock(enclosingInstance);
  238. this.similarity = Enclosing_Instance.GetSimilarity(searcher);
  239. for (int i = 0; i < Enclosing_Instance.clauses.Count; i++)
  240. {
  241. BooleanClause c = (BooleanClause) Enclosing_Instance.clauses[i];
  242. weights.Add(c.GetQuery().CreateWeight(searcher));
  243. }
  244. }
  245. public virtual Query GetQuery()
  246. {
  247. return Enclosing_Instance;
  248. }
  249. public virtual float GetValue()
  250. {
  251. return Enclosing_Instance.GetBoost();
  252. }
  253. public virtual float SumOfSquaredWeights()
  254. {
  255. float sum = 0.0f;
  256. for (int i = 0; i < weights.Count; i++)
  257. {
  258. BooleanClause c = (BooleanClause) Enclosing_Instance.clauses[i];
  259. Weight w = (Weight) weights[i];
  260. if (!c.IsProhibited())
  261. sum += w.SumOfSquaredWeights(); // sum sub weights
  262. }
  263. sum *= Enclosing_Instance.GetBoost() * Enclosing_Instance.GetBoost(); // boost each sub-weight
  264. return sum;
  265. }
  266. public virtual void  Normalize(float norm)
  267. {
  268. norm *= Enclosing_Instance.GetBoost(); // incorporate boost
  269. for (int i = 0; i < weights.Count; i++)
  270. {
  271. BooleanClause c = (BooleanClause) Enclosing_Instance.clauses[i];
  272. Weight w = (Weight) weights[i];
  273. if (!c.IsProhibited())
  274. w.Normalize(norm);
  275. }
  276. }
  277. /// <returns> A good old 1.4 Scorer 
  278. /// </returns>
  279. public virtual Scorer Scorer(IndexReader reader)
  280. {
  281. // First see if the (faster) ConjunctionScorer will work.  This can be
  282. // used when all clauses are required.  Also, at this point a
  283. // BooleanScorer cannot be embedded in a ConjunctionScorer, as the hits
  284. // from a BooleanScorer are not always sorted by document number (sigh)
  285. // and hence BooleanScorer cannot implement skipTo() correctly, which is
  286. // required by ConjunctionScorer.
  287. bool allRequired = true;
  288. bool noneBoolean = true;
  289. for (int i = 0; i < weights.Count; i++)
  290. {
  291. BooleanClause c = (BooleanClause) Enclosing_Instance.clauses[i];
  292. if (!c.IsRequired())
  293. allRequired = false;
  294. if (c.GetQuery() is BooleanQuery)
  295. noneBoolean = false;
  296. }
  297. if (allRequired && noneBoolean)
  298. {
  299. // ConjunctionScorer is okay
  300. ConjunctionScorer result = new ConjunctionScorer(similarity);
  301. for (int i = 0; i < weights.Count; i++)
  302. {
  303. Weight w = (Weight) weights[i];
  304. Scorer subScorer = w.Scorer(reader);
  305. if (subScorer == null)
  306. return null;
  307. result.Add(subScorer);
  308. }
  309. return result;
  310. }
  311. // Use good-old BooleanScorer instead.
  312. BooleanScorer result2 = new BooleanScorer(similarity);
  313. for (int i = 0; i < weights.Count; i++)
  314. {
  315. BooleanClause c = (BooleanClause) Enclosing_Instance.clauses[i];
  316. Weight w = (Weight) weights[i];
  317. Scorer subScorer = w.Scorer(reader);
  318. if (subScorer != null)
  319. result2.Add(subScorer, c.IsRequired(), c.IsProhibited());
  320. else if (c.IsRequired())
  321. return null;
  322. }
  323. return result2;
  324. }
  325. public virtual Explanation Explain(IndexReader reader, int doc)
  326. {
  327. Explanation sumExpl = new Explanation();
  328. sumExpl.SetDescription("sum of:");
  329. int coord = 0;
  330. int maxCoord = 0;
  331. float sum = 0.0f;
  332. for (int i = 0; i < weights.Count; i++)
  333. {
  334. BooleanClause c = (BooleanClause) Enclosing_Instance.clauses[i];
  335. Weight w = (Weight) weights[i];
  336. Explanation e = w.Explain(reader, doc);
  337. if (!c.IsProhibited())
  338. maxCoord++;
  339. if (e.GetValue() > 0)
  340. {
  341. if (!c.IsProhibited())
  342. {
  343. sumExpl.AddDetail(e);
  344. sum += e.GetValue();
  345. coord++;
  346. }
  347. else
  348. {
  349. return new Explanation(0.0f, "match prohibited");
  350. }
  351. }
  352. else if (c.IsRequired())
  353. {
  354. return new Explanation(0.0f, "match required");
  355. }
  356. }
  357. sumExpl.SetValue(sum);
  358. if (coord == 1)
  359. // only one clause matched
  360. sumExpl = sumExpl.GetDetails()[0]; // eliminate wrapper
  361. float coordFactor = similarity.Coord(coord, maxCoord);
  362. if (coordFactor == 1.0f)
  363. // coord is no-op
  364. return sumExpl;
  365. // eliminate wrapper
  366. else
  367. {
  368. Explanation result = new Explanation();
  369. result.SetDescription("product of:");
  370. result.AddDetail(sumExpl);
  371. result.AddDetail(new Explanation(coordFactor, "coord(" + coord + "/" + maxCoord + ")"));
  372. result.SetValue(sum * coordFactor);
  373. return result;
  374. }
  375. }
  376. }
  377. [Serializable]
  378. private class BooleanWeight2 : BooleanWeight
  379. {
  380. private void  InitBlock(BooleanQuery enclosingInstance)
  381. {
  382. this.enclosingInstance = enclosingInstance;
  383. }
  384. private BooleanQuery enclosingInstance;
  385. public new BooleanQuery Enclosing_Instance
  386. {
  387. get
  388. {
  389. return enclosingInstance;
  390. }
  391. }
  392. /* Merge into BooleanWeight in case the 1.4 BooleanScorer is dropped */
  393. public BooleanWeight2(BooleanQuery enclosingInstance, Searcher searcher):base(enclosingInstance, searcher)
  394. {
  395. InitBlock(enclosingInstance);
  396. }
  397. /// <returns> An alternative Scorer that uses and provides skipTo(),
  398. /// and scores documents in document number order.
  399. /// </returns>
  400. public override Scorer Scorer(IndexReader reader)
  401. {
  402. BooleanScorer2 result = new BooleanScorer2(similarity, Enclosing_Instance.minNrShouldMatch);
  403. for (int i = 0; i < weights.Count; i++)
  404. {
  405. BooleanClause c = (BooleanClause) Enclosing_Instance.clauses[i];
  406. Weight w = (Weight) weights[i];
  407. Scorer subScorer = w.Scorer(reader);
  408. if (subScorer != null)
  409. result.Add(subScorer, c.IsRequired(), c.IsProhibited());
  410. else if (c.IsRequired())
  411. return null;
  412. }
  413. return result;
  414. }
  415. }
  416. /// <summary>Indicates whether to use good old 1.4 BooleanScorer. </summary>
  417. private static bool useScorer14 = false;
  418. public static void  SetUseScorer14(bool use14)
  419. {
  420. useScorer14 = use14;
  421. }
  422. public static bool GetUseScorer14()
  423. {
  424. return useScorer14;
  425. }
  426. protected internal override Weight CreateWeight(Searcher searcher)
  427. {
  428. if (0 < minNrShouldMatch)
  429. {
  430. // :TODO: should we throw an exception if getUseScorer14 ?
  431. return new BooleanWeight2(this, searcher);
  432. }
  433. return GetUseScorer14() ? (Weight) new BooleanWeight(this, searcher) : (Weight) new BooleanWeight2(this, searcher);
  434. }
  435. public override Query Rewrite(IndexReader reader)
  436. {
  437. if (clauses.Count == 1)
  438. {
  439. // optimize 1-clause queries
  440. BooleanClause c = (BooleanClause) clauses[0];
  441. if (!c.IsProhibited())
  442. {
  443. // just return clause
  444. Query query = c.GetQuery().Rewrite(reader); // rewrite first
  445. if (GetBoost() != 1.0f)
  446. {
  447. // incorporate boost
  448. if (query == c.GetQuery())
  449. // if rewrite was no-op
  450. query = (Query) query.Clone(); // then clone before boost
  451. query.SetBoost(GetBoost() * query.GetBoost());
  452. }
  453. return query;
  454. }
  455. }
  456. BooleanQuery clone = null; // recursively rewrite
  457. for (int i = 0; i < clauses.Count; i++)
  458. {
  459. BooleanClause c = (BooleanClause) clauses[i];
  460. Query query = c.GetQuery().Rewrite(reader);
  461. if (query != c.GetQuery())
  462. {
  463. // clause rewrote: must clone
  464. if (clone == null)
  465. clone = (BooleanQuery) this.Clone();
  466. clone.clauses[i] = new BooleanClause(query, c.GetOccur());
  467. }
  468. }
  469. if (clone != null)
  470. {
  471. return clone; // some clauses rewrote
  472. }
  473. else
  474. return this; // no clauses rewrote
  475. }
  476. // inherit javadoc
  477. public override void  ExtractTerms(System.Collections.Hashtable terms)
  478. {
  479. for (System.Collections.IEnumerator i = clauses.GetEnumerator(); i.MoveNext(); )
  480. {
  481. BooleanClause clause = (BooleanClause) i.Current;
  482. clause.GetQuery().ExtractTerms(terms);
  483. }
  484. }
  485. public override System.Object Clone()
  486. {
  487. BooleanQuery clone = (BooleanQuery) base.Clone();
  488. clone.clauses = (System.Collections.ArrayList) this.clauses.Clone();
  489. return clone;
  490. }
  491. /// <summary>Prints a user-readable version of this query. </summary>
  492. public override System.String ToString(System.String field)
  493. {
  494. System.Text.StringBuilder buffer = new System.Text.StringBuilder();
  495. bool needParens = (GetBoost() != 1.0) || (GetMinimumNumberShouldMatch() > 0);
  496. if (needParens)
  497. {
  498. buffer.Append("(");
  499. }
  500. for (int i = 0; i < clauses.Count; i++)
  501. {
  502. BooleanClause c = (BooleanClause) clauses[i];
  503. if (c.IsProhibited())
  504. buffer.Append("-");
  505. else if (c.IsRequired())
  506. buffer.Append("+");
  507. Query subQuery = c.GetQuery();
  508. if (subQuery is BooleanQuery)
  509. {
  510. // wrap sub-bools in parens
  511. buffer.Append("(");
  512. buffer.Append(c.GetQuery().ToString(field));
  513. buffer.Append(")");
  514. }
  515. else
  516. buffer.Append(c.GetQuery().ToString(field));
  517. if (i != clauses.Count - 1)
  518. buffer.Append(" ");
  519. }
  520. if (needParens)
  521. {
  522. buffer.Append(")");
  523. }
  524. if (GetMinimumNumberShouldMatch() > 0)
  525. {
  526. buffer.Append('~');
  527. buffer.Append(GetMinimumNumberShouldMatch());
  528. }
  529. if (GetBoost() != 1.0f)
  530. {
  531. buffer.Append(ToStringUtils.Boost(GetBoost()));
  532. }
  533. return buffer.ToString();
  534. }
  535. /// <summary>Returns true iff <code>o</code> is equal to this. </summary>
  536. public  override bool Equals(System.Object o)
  537. {
  538. if (!(o is BooleanQuery))
  539. return false;
  540. BooleanQuery other = (BooleanQuery) o;
  541. return (this.GetBoost() == other.GetBoost()) && this.clauses.Equals(other.clauses) && this.GetMinimumNumberShouldMatch() == other.GetMinimumNumberShouldMatch();
  542. }
  543. /// <summary>Returns a hash code value for this object.</summary>
  544. public override int GetHashCode()
  545. {
  546. return BitConverter.ToInt32(BitConverter.GetBytes(GetBoost()), 0) ^ clauses.GetHashCode() + GetMinimumNumberShouldMatch();
  547. }
  548. }
  549. }