pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / src / java / org / apache / lucene / search / spans / FieldMaskingSpanQuery.java
diff --git a/lucene-java-3.5.0/lucene/src/java/org/apache/lucene/search/spans/FieldMaskingSpanQuery.java b/lucene-java-3.5.0/lucene/src/java/org/apache/lucene/search/spans/FieldMaskingSpanQuery.java
new file mode 100644 (file)
index 0000000..f6eeecb
--- /dev/null
@@ -0,0 +1,155 @@
+package org.apache.lucene.search.spans;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.util.Set;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Weight;
+import org.apache.lucene.search.Searcher;
+import org.apache.lucene.search.Similarity;
+import org.apache.lucene.util.ToStringUtils;
+
+/**
+ * <p>Wrapper to allow {@link SpanQuery} objects participate in composite 
+ * single-field SpanQueries by 'lying' about their search field. That is, 
+ * the masked SpanQuery will function as normal, 
+ * but {@link SpanQuery#getField()} simply hands back the value supplied 
+ * in this class's constructor.</p>
+ * 
+ * <p>This can be used to support Queries like {@link SpanNearQuery} or 
+ * {@link SpanOrQuery} across different fields, which is not ordinarily 
+ * permitted.</p>
+ * 
+ * <p>This can be useful for denormalized relational data: for example, when 
+ * indexing a document with conceptually many 'children': </p>
+ * 
+ * <pre>
+ *  teacherid: 1
+ *  studentfirstname: james
+ *  studentsurname: jones
+ *  
+ *  teacherid: 2
+ *  studenfirstname: james
+ *  studentsurname: smith
+ *  studentfirstname: sally
+ *  studentsurname: jones
+ * </pre>
+ * 
+ * <p>a SpanNearQuery with a slop of 0 can be applied across two 
+ * {@link SpanTermQuery} objects as follows:
+ * <pre>
+ *    SpanQuery q1  = new SpanTermQuery(new Term("studentfirstname", "james"));
+ *    SpanQuery q2  = new SpanTermQuery(new Term("studentsurname", "jones"));
+ *    SpanQuery q2m = new FieldMaskingSpanQuery(q2, "studentfirstname");
+ *    Query q = new SpanNearQuery(new SpanQuery[]{q1, q2m}, -1, false);
+ * </pre>
+ * to search for 'studentfirstname:james studentsurname:jones' and find 
+ * teacherid 1 without matching teacherid 2 (which has a 'james' in position 0 
+ * and 'jones' in position 1). </p>
+ * 
+ * <p>Note: as {@link #getField()} returns the masked field, scoring will be 
+ * done using the norms of the field name supplied. This may lead to unexpected
+ * scoring behaviour.</p>
+ */
+public class FieldMaskingSpanQuery extends SpanQuery {
+  private SpanQuery maskedQuery;
+  private String field;
+    
+  public FieldMaskingSpanQuery(SpanQuery maskedQuery, String maskedField) {
+    this.maskedQuery = maskedQuery;
+    this.field = maskedField;
+  }
+
+  @Override
+  public String getField() {
+    return field;
+  }
+
+  public SpanQuery getMaskedQuery() {
+    return maskedQuery;
+  }
+
+  // :NOTE: getBoost and setBoost are not proxied to the maskedQuery
+  // ...this is done to be more consistent with things like SpanFirstQuery
+  
+  @Override
+  public Spans getSpans(IndexReader reader) throws IOException {
+    return maskedQuery.getSpans(reader);
+  }
+
+  @Override
+  public void extractTerms(Set<Term> terms) {
+    maskedQuery.extractTerms(terms);
+  }  
+
+  @Override
+  public Weight createWeight(Searcher searcher) throws IOException {
+    return maskedQuery.createWeight(searcher);
+  }
+
+  @Override
+  public Query rewrite(IndexReader reader) throws IOException {
+    FieldMaskingSpanQuery clone = null;
+
+    SpanQuery rewritten = (SpanQuery) maskedQuery.rewrite(reader);
+    if (rewritten != maskedQuery) {
+      clone = (FieldMaskingSpanQuery) this.clone();
+      clone.maskedQuery = rewritten;
+    }
+
+    if (clone != null) {
+      return clone;
+    } else {
+      return this;
+    }
+  }
+
+  @Override
+  public String toString(String field) {
+    StringBuilder buffer = new StringBuilder();
+    buffer.append("mask(");
+    buffer.append(maskedQuery.toString(field));
+    buffer.append(")");
+    buffer.append(ToStringUtils.boost(getBoost()));
+    buffer.append(" as ");
+    buffer.append(this.field);
+    return buffer.toString();
+  }
+  
+  @Override
+  public boolean equals(Object o) {
+    if (!(o instanceof FieldMaskingSpanQuery))
+      return false;
+    FieldMaskingSpanQuery other = (FieldMaskingSpanQuery) o;
+    return (this.getField().equals(other.getField())
+            && (this.getBoost() == other.getBoost())
+            && this.getMaskedQuery().equals(other.getMaskedQuery()));
+
+  }
+  
+  @Override
+  public int hashCode() {
+    return getMaskedQuery().hashCode()
+      ^ getField().hashCode()
+      ^ Float.floatToRawIntBits(getBoost());
+  }
+}