~ubuntu-branches/ubuntu/trusty/pylucene/trusty

« back to all changes in this revision

Viewing changes to lucene-java-3.5.0/lucene/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java

  • Committer: Package Import Robot
  • Author(s): Dmitry Nezhevenko
  • Date: 2012-04-23 16:43:55 UTC
  • mfrom: (1.1.1)
  • Revision ID: package-import@ubuntu.com-20120423164355-grqtepnwtecdjfk2
Tags: 3.5.0-1
* New maintainer (closes: 670179)
* New upstream release
* Switch to dpkg-source 3.0 (quilt) format
* Switch to machine-readable debian/copyright
* Bump debian/compat to 8, drop debian/pycompat
* Switch from cdbs to dh
* Add watch file
* Build for all supported versions of python2 (closes: 581198, 632240)
* Rename binary package to python-lucene (closes: 581197)
* Add -dbg package

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package org.apache.lucene.search;
 
2
 
 
3
/**
 
4
 * Copyright 2004 The Apache Software Foundation
 
5
 *
 
6
 * Licensed under the Apache License, Version 2.0 (the "License");
 
7
 * you may not use this file except in compliance with the License.
 
8
 * You may obtain a copy of the License at
 
9
 *
 
10
 *     http://www.apache.org/licenses/LICENSE-2.0
 
11
 *
 
12
 * Unless required by applicable law or agreed to in writing, software
 
13
 * distributed under the License is distributed on an "AS IS" BASIS,
 
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
15
 * See the License for the specific language governing permissions and
 
16
 * limitations under the License.
 
17
 */
 
18
 
 
19
import java.io.IOException;
 
20
import java.util.ArrayList;
 
21
import java.util.Collection;
 
22
import java.util.Iterator;
 
23
import java.util.Set;
 
24
 
 
25
import org.apache.lucene.index.IndexReader;
 
26
import org.apache.lucene.index.Term;
 
27
 
 
28
/**
 
29
 * A query that generates the union of documents produced by its subqueries, and that scores each document with the maximum
 
30
 * score for that document as produced by any subquery, plus a tie breaking increment for any additional matching subqueries.
 
31
 * This is useful when searching for a word in multiple fields with different boost factors (so that the fields cannot be
 
32
 * combined equivalently into a single search field).  We want the primary score to be the one associated with the highest boost,
 
33
 * not the sum of the field scores (as BooleanQuery would give).
 
34
 * If the query is "albino elephant" this ensures that "albino" matching one field and "elephant" matching
 
35
 * another gets a higher score than "albino" matching both fields.
 
36
 * To get this result, use both BooleanQuery and DisjunctionMaxQuery:  for each term a DisjunctionMaxQuery searches for it in
 
37
 * each field, while the set of these DisjunctionMaxQuery's is combined into a BooleanQuery.
 
38
 * The tie breaker capability allows results that include the same term in multiple fields to be judged better than results that
 
39
 * include this term in only the best of those multiple fields, without confusing this with the better case of two different terms
 
40
 * in the multiple fields.
 
41
 */
 
42
public class DisjunctionMaxQuery extends Query implements Iterable<Query> {
 
43
 
 
44
  /* The subqueries */
 
45
  private ArrayList<Query> disjuncts = new ArrayList<Query>();
 
46
 
 
47
  /* Multiple of the non-max disjunct scores added into our final score.  Non-zero values support tie-breaking. */
 
48
  private float tieBreakerMultiplier = 0.0f;
 
49
 
 
50
  /** Creates a new empty DisjunctionMaxQuery.  Use add() to add the subqueries.
 
51
   * @param tieBreakerMultiplier the score of each non-maximum disjunct for a document is multiplied by this weight
 
52
   *        and added into the final score.  If non-zero, the value should be small, on the order of 0.1, which says that
 
53
   *        10 occurrences of word in a lower-scored field that is also in a higher scored field is just as good as a unique
 
54
   *        word in the lower scored field (i.e., one that is not in any higher scored field.
 
55
   */
 
56
  public DisjunctionMaxQuery(float tieBreakerMultiplier) {
 
57
    this.tieBreakerMultiplier = tieBreakerMultiplier;
 
58
  }
 
59
 
 
60
  /**
 
61
   * Creates a new DisjunctionMaxQuery
 
62
   * @param disjuncts a Collection<Query> of all the disjuncts to add
 
63
   * @param tieBreakerMultiplier   the weight to give to each matching non-maximum disjunct
 
64
   */
 
65
  public DisjunctionMaxQuery(Collection<Query> disjuncts, float tieBreakerMultiplier) {
 
66
    this.tieBreakerMultiplier = tieBreakerMultiplier;
 
67
    add(disjuncts);
 
68
  }
 
69
 
 
70
  /** Add a subquery to this disjunction
 
71
   * @param query the disjunct added
 
72
   */
 
73
  public void add(Query query) {
 
74
    disjuncts.add(query);
 
75
  }
 
76
 
 
77
  /** Add a collection of disjuncts to this disjunction
 
78
   * via Iterable<Query>
 
79
   */
 
80
  public void add(Collection<Query> disjuncts) {
 
81
    this.disjuncts.addAll(disjuncts);
 
82
  }
 
83
 
 
84
  /** An Iterator<Query> over the disjuncts */
 
85
  public Iterator<Query> iterator() {
 
86
    return disjuncts.iterator();
 
87
  }
 
88
 
 
89
  /**
 
90
   * Expert: the Weight for DisjunctionMaxQuery, used to
 
91
   * normalize, score and explain these queries.
 
92
   *
 
93
   * <p>NOTE: this API and implementation is subject to
 
94
   * change suddenly in the next release.</p>
 
95
   */
 
96
  protected class DisjunctionMaxWeight extends Weight {
 
97
    /** The Similarity implementation. */
 
98
    protected Similarity similarity;
 
99
 
 
100
    /** The Weights for our subqueries, in 1-1 correspondence with disjuncts */
 
101
    protected ArrayList<Weight> weights = new ArrayList<Weight>();  // The Weight's for our subqueries, in 1-1 correspondence with disjuncts
 
102
 
 
103
    /** Construct the Weight for this Query searched by searcher.  Recursively construct subquery weights. */
 
104
    public DisjunctionMaxWeight(Searcher searcher) throws IOException {
 
105
      this.similarity = searcher.getSimilarity();
 
106
      for (Query disjunctQuery : disjuncts) {
 
107
        weights.add(disjunctQuery.createWeight(searcher));
 
108
      }
 
109
    }
 
110
 
 
111
    /** Return our associated DisjunctionMaxQuery */
 
112
    @Override
 
113
    public Query getQuery() { return DisjunctionMaxQuery.this; }
 
114
 
 
115
    /** Return our boost */
 
116
    @Override
 
117
    public float getValue() { return getBoost(); }
 
118
 
 
119
    /** Compute the sub of squared weights of us applied to our subqueries.  Used for normalization. */
 
120
    @Override
 
121
    public float sumOfSquaredWeights() throws IOException {
 
122
      float max = 0.0f, sum = 0.0f;
 
123
      for (Weight currentWeight : weights) {
 
124
        float sub = currentWeight.sumOfSquaredWeights();
 
125
        sum += sub;
 
126
        max = Math.max(max, sub);
 
127
        
 
128
      }
 
129
      float boost = getBoost();
 
130
      return (((sum - max) * tieBreakerMultiplier * tieBreakerMultiplier) + max) * boost * boost;
 
131
    }
 
132
 
 
133
    /** Apply the computed normalization factor to our subqueries */
 
134
    @Override
 
135
    public void normalize(float norm) {
 
136
      norm *= getBoost();  // Incorporate our boost
 
137
      for (Weight wt : weights) {
 
138
        wt.normalize(norm);
 
139
      }
 
140
    }
 
141
 
 
142
    /** Create the scorer used to score our associated DisjunctionMaxQuery */
 
143
    @Override
 
144
    public Scorer scorer(IndexReader reader, boolean scoreDocsInOrder,
 
145
        boolean topScorer) throws IOException {
 
146
      Scorer[] scorers = new Scorer[weights.size()];
 
147
      int idx = 0;
 
148
      for (Weight w : weights) {
 
149
        Scorer subScorer = w.scorer(reader, true, false);
 
150
        if (subScorer != null && subScorer.nextDoc() != DocIdSetIterator.NO_MORE_DOCS) {
 
151
          scorers[idx++] = subScorer;
 
152
        }
 
153
      }
 
154
      if (idx == 0) return null; // all scorers did not have documents
 
155
      DisjunctionMaxScorer result = new DisjunctionMaxScorer(this, tieBreakerMultiplier, similarity, scorers, idx);
 
156
      return result;
 
157
    }
 
158
 
 
159
    /** Explain the score we computed for doc */
 
160
    @Override
 
161
    public Explanation explain(IndexReader reader, int doc) throws IOException {
 
162
      if (disjuncts.size() == 1) return weights.get(0).explain(reader,doc);
 
163
      ComplexExplanation result = new ComplexExplanation();
 
164
      float max = 0.0f, sum = 0.0f;
 
165
      result.setDescription(tieBreakerMultiplier == 0.0f ? "max of:" : "max plus " + tieBreakerMultiplier + " times others of:");
 
166
      for (Weight wt : weights) {
 
167
        Explanation e = wt.explain(reader, doc);
 
168
        if (e.isMatch()) {
 
169
          result.setMatch(Boolean.TRUE);
 
170
          result.addDetail(e);
 
171
          sum += e.getValue();
 
172
          max = Math.max(max, e.getValue());
 
173
        }
 
174
      }
 
175
      result.setValue(max + (sum - max) * tieBreakerMultiplier);
 
176
      return result;
 
177
    }
 
178
    
 
179
  }  // end of DisjunctionMaxWeight inner class
 
180
 
 
181
  /** Create the Weight used to score us */
 
182
  @Override
 
183
  public Weight createWeight(Searcher searcher) throws IOException {
 
184
    return new DisjunctionMaxWeight(searcher);
 
185
  }
 
186
 
 
187
  /** Optimize our representation and our subqueries representations
 
188
   * @param reader the IndexReader we query
 
189
   * @return an optimized copy of us (which may not be a copy if there is nothing to optimize) */
 
190
  @Override
 
191
  public Query rewrite(IndexReader reader) throws IOException {
 
192
    int numDisjunctions = disjuncts.size();
 
193
    if (numDisjunctions == 1) {
 
194
      Query singleton = disjuncts.get(0);
 
195
      Query result = singleton.rewrite(reader);
 
196
      if (getBoost() != 1.0f) {
 
197
        if (result == singleton) result = (Query)result.clone();
 
198
        result.setBoost(getBoost() * result.getBoost());
 
199
      }
 
200
      return result;
 
201
    }
 
202
    DisjunctionMaxQuery clone = null;
 
203
    for (int i = 0 ; i < numDisjunctions; i++) {
 
204
      Query clause = disjuncts.get(i);
 
205
      Query rewrite = clause.rewrite(reader);
 
206
      if (rewrite != clause) {
 
207
        if (clone == null) clone = (DisjunctionMaxQuery)this.clone();
 
208
        clone.disjuncts.set(i, rewrite);
 
209
      }
 
210
    }
 
211
    if (clone != null) return clone;
 
212
    else return this;
 
213
  }
 
214
 
 
215
  /** Create a shallow copy of us -- used in rewriting if necessary
 
216
   * @return a copy of us (but reuse, don't copy, our subqueries) */
 
217
  @Override @SuppressWarnings("unchecked")
 
218
  public Object clone() {
 
219
    DisjunctionMaxQuery clone = (DisjunctionMaxQuery)super.clone();
 
220
    clone.disjuncts = (ArrayList<Query>) this.disjuncts.clone();
 
221
    return clone;
 
222
  }
 
223
 
 
224
  // inherit javadoc
 
225
  @Override
 
226
  public void extractTerms(Set<Term> terms) {
 
227
    for (Query query : disjuncts) {
 
228
      query.extractTerms(terms);
 
229
    }
 
230
  }
 
231
 
 
232
  /** Prettyprint us.
 
233
   * @param field the field to which we are applied
 
234
   * @return a string that shows what we do, of the form "(disjunct1 | disjunct2 | ... | disjunctn)^boost"
 
235
   */
 
236
  @Override
 
237
  public String toString(String field) {
 
238
    StringBuilder buffer = new StringBuilder();
 
239
    buffer.append("(");
 
240
    int numDisjunctions = disjuncts.size();
 
241
    for (int i = 0 ; i < numDisjunctions; i++) {
 
242
      Query subquery = disjuncts.get(i);
 
243
      if (subquery instanceof BooleanQuery) {   // wrap sub-bools in parens
 
244
        buffer.append("(");
 
245
        buffer.append(subquery.toString(field));
 
246
        buffer.append(")");
 
247
      }
 
248
      else buffer.append(subquery.toString(field));
 
249
      if (i != numDisjunctions-1) buffer.append(" | ");
 
250
    }
 
251
    buffer.append(")");
 
252
    if (tieBreakerMultiplier != 0.0f) {
 
253
      buffer.append("~");
 
254
      buffer.append(tieBreakerMultiplier);
 
255
    }
 
256
    if (getBoost() != 1.0) {
 
257
      buffer.append("^");
 
258
      buffer.append(getBoost());
 
259
    }
 
260
    return buffer.toString();
 
261
  }
 
262
 
 
263
  /** Return true iff we represent the same query as o
 
264
   * @param o another object
 
265
   * @return true iff o is a DisjunctionMaxQuery with the same boost and the same subqueries, in the same order, as us
 
266
   */
 
267
  @Override
 
268
  public boolean equals(Object o) {
 
269
    if (! (o instanceof DisjunctionMaxQuery) ) return false;
 
270
    DisjunctionMaxQuery other = (DisjunctionMaxQuery)o;
 
271
    return this.getBoost() == other.getBoost()
 
272
            && this.tieBreakerMultiplier == other.tieBreakerMultiplier
 
273
            && this.disjuncts.equals(other.disjuncts);
 
274
  }
 
275
 
 
276
  /** Compute a hash code for hashing us
 
277
   * @return the hash code
 
278
   */
 
279
  @Override
 
280
  public int hashCode() {
 
281
    return Float.floatToIntBits(getBoost())
 
282
            + Float.floatToIntBits(tieBreakerMultiplier)
 
283
            + disjuncts.hashCode();
 
284
  }
 
285
 
 
286
}