2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
20 using IndexReader = Mono.Lucene.Net.Index.IndexReader;
21 using MultipleTermPositions = Mono.Lucene.Net.Index.MultipleTermPositions;
22 using Term = Mono.Lucene.Net.Index.Term;
23 using TermPositions = Mono.Lucene.Net.Index.TermPositions;
24 using ToStringUtils = Mono.Lucene.Net.Util.ToStringUtils;
26 namespace Mono.Lucene.Net.Search
29 /// <summary> MultiPhraseQuery is a generalized version of PhraseQuery, with an added
30 /// method {@link #Add(Term[])}.
31 /// To use this class, to search for the phrase "Microsoft app*" first use
32 /// add(Term) on the term "Microsoft", then find all terms that have "app" as
33 /// prefix using IndexReader.terms(Term), and use MultiPhraseQuery.add(Term[]
34 /// terms) to add them to the query.
40 public class MultiPhraseQuery:Query
42 private System.String field;
43 private System.Collections.ArrayList termArrays = new System.Collections.ArrayList();
44 private System.Collections.ArrayList positions = new System.Collections.ArrayList();
48 /// <summary>Sets the phrase slop for this query.</summary>
49 /// <seealso cref="PhraseQuery.SetSlop(int)">
51 public virtual void SetSlop(int s)
56 /// <summary>Sets the phrase slop for this query.</summary>
57 /// <seealso cref="PhraseQuery.GetSlop()">
59 public virtual int GetSlop()
64 /// <summary>Add a single term at the next position in the phrase.</summary>
65 /// <seealso cref="PhraseQuery.add(Term)">
67 public virtual void Add(Term term)
69 Add(new Term[]{term});
72 /// <summary>Add multiple terms at the next position in the phrase. Any of the terms
76 /// <seealso cref="PhraseQuery.add(Term)">
78 public virtual void Add(Term[] terms)
81 if (positions.Count > 0)
82 position = ((System.Int32) positions[positions.Count - 1]) + 1;
87 /// <summary> Allows to specify the relative position of terms within the phrase.
90 /// <seealso cref="PhraseQuery.Add(Term, int)">
92 /// <param name="terms">
94 /// <param name="position">
96 public virtual void Add(Term[] terms, int position)
98 if (termArrays.Count == 0)
99 field = terms[0].Field();
101 for (int i = 0; i < terms.Length; i++)
103 if ((System.Object) terms[i].Field() != (System.Object) field)
105 throw new System.ArgumentException("All phrase terms must be in the same field (" + field + "): " + terms[i]);
109 termArrays.Add(terms);
110 positions.Add((System.Int32) position);
113 /// <summary> Returns a List<Term[]> of the terms in the multiphrase.
114 /// Do not modify the List or its contents.
116 public virtual System.Collections.IList GetTermArrays()
118 return (System.Collections.IList) System.Collections.ArrayList.ReadOnly(new System.Collections.ArrayList(termArrays));
121 /// <summary> Returns the relative positions of terms in this phrase.</summary>
122 public virtual int[] GetPositions()
124 int[] result = new int[positions.Count];
125 for (int i = 0; i < positions.Count; i++)
126 result[i] = ((System.Int32) positions[i]);
131 public override void ExtractTerms(System.Collections.Hashtable terms)
133 for (System.Collections.IEnumerator iter = termArrays.GetEnumerator(); iter.MoveNext(); )
135 Term[] arr = (Term[]) iter.Current;
136 for (int i = 0; i < arr.Length; i++)
138 SupportClass.CollectionsHelper.AddIfNotContains(terms, arr[i]);
145 private class MultiPhraseWeight:Weight
147 private void InitBlock(MultiPhraseQuery enclosingInstance)
149 this.enclosingInstance = enclosingInstance;
151 private MultiPhraseQuery enclosingInstance;
152 public MultiPhraseQuery Enclosing_Instance
156 return enclosingInstance;
160 private Similarity similarity;
161 private float value_Renamed;
163 private float queryNorm;
164 private float queryWeight;
166 public MultiPhraseWeight(MultiPhraseQuery enclosingInstance, Searcher searcher)
168 InitBlock(enclosingInstance);
169 this.similarity = Enclosing_Instance.GetSimilarity(searcher);
172 System.Collections.IEnumerator i = Enclosing_Instance.termArrays.GetEnumerator();
175 Term[] terms = (Term[]) i.Current;
176 for (int j = 0; j < terms.Length; j++)
178 idf += Enclosing_Instance.GetSimilarity(searcher).Idf(terms[j], searcher);
183 public override Query GetQuery()
185 return Enclosing_Instance;
187 public override float GetValue()
189 return value_Renamed;
192 public override float SumOfSquaredWeights()
194 queryWeight = idf * Enclosing_Instance.GetBoost(); // compute query weight
195 return queryWeight * queryWeight; // square it
198 public override void Normalize(float queryNorm)
200 this.queryNorm = queryNorm;
201 queryWeight *= queryNorm; // normalize query weight
202 value_Renamed = queryWeight * idf; // idf for document
205 public override Scorer Scorer(IndexReader reader, bool scoreDocsInOrder, bool topScorer)
207 if (Enclosing_Instance.termArrays.Count == 0)
208 // optimize zero-term case
211 TermPositions[] tps = new TermPositions[Enclosing_Instance.termArrays.Count];
212 for (int i = 0; i < tps.Length; i++)
214 Term[] terms = (Term[]) Enclosing_Instance.termArrays[i];
217 if (terms.Length > 1)
218 p = new MultipleTermPositions(reader, terms);
220 p = reader.TermPositions(terms[0]);
228 if (Enclosing_Instance.slop == 0)
229 return new ExactPhraseScorer(this, tps, Enclosing_Instance.GetPositions(), similarity, reader.Norms(Enclosing_Instance.field));
231 return new SloppyPhraseScorer(this, tps, Enclosing_Instance.GetPositions(), similarity, Enclosing_Instance.slop, reader.Norms(Enclosing_Instance.field));
234 public override Explanation Explain(IndexReader reader, int doc)
236 ComplexExplanation result = new ComplexExplanation();
237 result.SetDescription("weight(" + GetQuery() + " in " + doc + "), product of:");
239 Explanation idfExpl = new Explanation(idf, "idf(" + GetQuery() + ")");
241 // explain query weight
242 Explanation queryExpl = new Explanation();
243 queryExpl.SetDescription("queryWeight(" + GetQuery() + "), product of:");
245 Explanation boostExpl = new Explanation(Enclosing_Instance.GetBoost(), "boost");
246 if (Enclosing_Instance.GetBoost() != 1.0f)
247 queryExpl.AddDetail(boostExpl);
249 queryExpl.AddDetail(idfExpl);
251 Explanation queryNormExpl = new Explanation(queryNorm, "queryNorm");
252 queryExpl.AddDetail(queryNormExpl);
254 queryExpl.SetValue(boostExpl.GetValue() * idfExpl.GetValue() * queryNormExpl.GetValue());
256 result.AddDetail(queryExpl);
258 // explain field weight
259 ComplexExplanation fieldExpl = new ComplexExplanation();
260 fieldExpl.SetDescription("fieldWeight(" + GetQuery() + " in " + doc + "), product of:");
262 Scorer scorer = Scorer(reader, true, false);
265 return new Explanation(0.0f, "no matching docs");
267 Explanation tfExpl = scorer.Explain(doc);
268 fieldExpl.AddDetail(tfExpl);
269 fieldExpl.AddDetail(idfExpl);
271 Explanation fieldNormExpl = new Explanation();
272 byte[] fieldNorms = reader.Norms(Enclosing_Instance.field);
273 float fieldNorm = fieldNorms != null?Similarity.DecodeNorm(fieldNorms[doc]):1.0f;
274 fieldNormExpl.SetValue(fieldNorm);
275 fieldNormExpl.SetDescription("fieldNorm(field=" + Enclosing_Instance.field + ", doc=" + doc + ")");
276 fieldExpl.AddDetail(fieldNormExpl);
278 fieldExpl.SetMatch(tfExpl.IsMatch());
279 fieldExpl.SetValue(tfExpl.GetValue() * idfExpl.GetValue() * fieldNormExpl.GetValue());
281 result.AddDetail(fieldExpl);
282 System.Boolean? tempAux = fieldExpl.GetMatch();
283 result.SetMatch(tempAux);
286 result.SetValue(queryExpl.GetValue() * fieldExpl.GetValue());
288 if (queryExpl.GetValue() == 1.0f)
295 public override Query Rewrite(IndexReader reader)
297 if (termArrays.Count == 1)
299 // optimize one-term case
300 Term[] terms = (Term[]) termArrays[0];
301 BooleanQuery boq = new BooleanQuery(true);
302 for (int i = 0; i < terms.Length; i++)
304 boq.Add(new TermQuery(terms[i]), BooleanClause.Occur.SHOULD);
306 boq.SetBoost(GetBoost());
315 public override Weight CreateWeight(Searcher searcher)
317 return new MultiPhraseWeight(this, searcher);
320 /// <summary>Prints a user-readable version of this query. </summary>
321 public override System.String ToString(System.String f)
323 System.Text.StringBuilder buffer = new System.Text.StringBuilder();
324 if (!field.Equals(f))
326 buffer.Append(field);
331 System.Collections.IEnumerator i = termArrays.GetEnumerator();
344 Term[] terms = (Term[]) i.Current;
345 if (terms.Length > 1)
348 for (int j = 0; j < terms.Length; j++)
350 buffer.Append(terms[j].Text());
351 if (j < terms.Length - 1)
358 buffer.Append(terms[0].Text());
369 buffer.Append(ToStringUtils.Boost(GetBoost()));
371 return buffer.ToString();
375 /// <summary>Returns true if <code>o</code> is equal to this. </summary>
376 public override bool Equals(System.Object o)
378 if (!(o is MultiPhraseQuery))
380 MultiPhraseQuery other = (MultiPhraseQuery) o;
381 bool eq = this.GetBoost() == other.GetBoost() && this.slop == other.slop;
386 eq = this.termArrays.Count.Equals(other.termArrays.Count);
392 for (int i = 0; i < this.termArrays.Count; i++)
394 if (!SupportClass.Compare.CompareTermArrays((Term[])this.termArrays[i], (Term[])other.termArrays[i]))
403 eq = this.positions.Count.Equals(other.positions.Count);
408 for (int i = 0; i < this.positions.Count; i++)
410 if (!((int)this.positions[i] == (int)other.positions[i]))
418 /// <summary>Returns a hash code value for this object.</summary>
419 public override int GetHashCode()
422 foreach(int pos in positions)
424 posHash += pos.GetHashCode();
426 return BitConverter.ToInt32(BitConverter.GetBytes(GetBoost()), 0) ^ slop ^ TermArraysHashCode() ^ posHash ^ 0x4AC65113;
429 // Breakout calculation of the termArrays hashcode
430 private int TermArraysHashCode()
433 System.Collections.IEnumerator iterator = termArrays.GetEnumerator();
434 while (iterator.MoveNext())
436 Term[] termArray = (Term[]) iterator.Current;
437 hashCode = 31 * hashCode + (termArray == null?0:ArraysHashCode(termArray));
442 private int ArraysHashCode(Term[] termArray)
444 if (termArray == null)
449 for (int i = 0; i < termArray.Length; i++)
451 Term term = termArray[i];
452 result = 31 * result + (term == null?0:term.GetHashCode());
458 // Breakout calculation of the termArrays equals
459 private bool TermArraysEquals(System.Collections.IList termArrays1, System.Collections.IList termArrays2)
461 if (termArrays1.Count != termArrays2.Count)
465 System.Collections.IEnumerator iterator1 = termArrays1.GetEnumerator();
466 System.Collections.IEnumerator iterator2 = termArrays2.GetEnumerator();
467 while (iterator1.MoveNext())
469 Term[] termArray1 = (Term[]) iterator1.Current;
470 Term[] termArray2 = (Term[]) iterator2.Current;
471 if (!(termArray1 == null ? termArray2 == null : TermEquals(termArray1, termArray2)))
479 public static bool TermEquals(System.Array array1, System.Array array2)
482 if ((array1 == null) && (array2 == null))
484 else if ((array1 != null) && (array2 != null))
486 if (array1.Length == array2.Length)
488 int length = array1.Length;
490 for (int index = 0; index < length; index++)
492 if (!(array1.GetValue(index).Equals(array2.GetValue(index))))