pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / src / java / org / apache / lucene / search / spans / FieldMaskingSpanQuery.java
1 package org.apache.lucene.search.spans;
2
3 /**
4  * Licensed to the Apache Software Foundation (ASF) under one or more
5  * contributor license agreements.  See the NOTICE file distributed with
6  * this work for additional information regarding copyright ownership.
7  * The ASF licenses this file to You under the Apache License, Version 2.0
8  * (the "License"); you may not use this file except in compliance with
9  * the License.  You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 import java.io.IOException;
21 import java.util.Set;
22
23 import org.apache.lucene.index.IndexReader;
24 import org.apache.lucene.index.Term;
25 import org.apache.lucene.search.Query;
26 import org.apache.lucene.search.Weight;
27 import org.apache.lucene.search.Searcher;
28 import org.apache.lucene.search.Similarity;
29 import org.apache.lucene.util.ToStringUtils;
30
31 /**
32  * <p>Wrapper to allow {@link SpanQuery} objects participate in composite 
33  * single-field SpanQueries by 'lying' about their search field. That is, 
34  * the masked SpanQuery will function as normal, 
35  * but {@link SpanQuery#getField()} simply hands back the value supplied 
36  * in this class's constructor.</p>
37  * 
38  * <p>This can be used to support Queries like {@link SpanNearQuery} or 
39  * {@link SpanOrQuery} across different fields, which is not ordinarily 
40  * permitted.</p>
41  * 
42  * <p>This can be useful for denormalized relational data: for example, when 
43  * indexing a document with conceptually many 'children': </p>
44  * 
45  * <pre>
46  *  teacherid: 1
47  *  studentfirstname: james
48  *  studentsurname: jones
49  *  
50  *  teacherid: 2
51  *  studenfirstname: james
52  *  studentsurname: smith
53  *  studentfirstname: sally
54  *  studentsurname: jones
55  * </pre>
56  * 
57  * <p>a SpanNearQuery with a slop of 0 can be applied across two 
58  * {@link SpanTermQuery} objects as follows:
59  * <pre>
60  *    SpanQuery q1  = new SpanTermQuery(new Term("studentfirstname", "james"));
61  *    SpanQuery q2  = new SpanTermQuery(new Term("studentsurname", "jones"));
62  *    SpanQuery q2m = new FieldMaskingSpanQuery(q2, "studentfirstname");
63  *    Query q = new SpanNearQuery(new SpanQuery[]{q1, q2m}, -1, false);
64  * </pre>
65  * to search for 'studentfirstname:james studentsurname:jones' and find 
66  * teacherid 1 without matching teacherid 2 (which has a 'james' in position 0 
67  * and 'jones' in position 1). </p>
68  * 
69  * <p>Note: as {@link #getField()} returns the masked field, scoring will be 
70  * done using the norms of the field name supplied. This may lead to unexpected
71  * scoring behaviour.</p>
72  */
73 public class FieldMaskingSpanQuery extends SpanQuery {
74   private SpanQuery maskedQuery;
75   private String field;
76     
77   public FieldMaskingSpanQuery(SpanQuery maskedQuery, String maskedField) {
78     this.maskedQuery = maskedQuery;
79     this.field = maskedField;
80   }
81
82   @Override
83   public String getField() {
84     return field;
85   }
86
87   public SpanQuery getMaskedQuery() {
88     return maskedQuery;
89   }
90
91   // :NOTE: getBoost and setBoost are not proxied to the maskedQuery
92   // ...this is done to be more consistent with things like SpanFirstQuery
93   
94   @Override
95   public Spans getSpans(IndexReader reader) throws IOException {
96     return maskedQuery.getSpans(reader);
97   }
98
99   @Override
100   public void extractTerms(Set<Term> terms) {
101     maskedQuery.extractTerms(terms);
102   }  
103
104   @Override
105   public Weight createWeight(Searcher searcher) throws IOException {
106     return maskedQuery.createWeight(searcher);
107   }
108
109   @Override
110   public Query rewrite(IndexReader reader) throws IOException {
111     FieldMaskingSpanQuery clone = null;
112
113     SpanQuery rewritten = (SpanQuery) maskedQuery.rewrite(reader);
114     if (rewritten != maskedQuery) {
115       clone = (FieldMaskingSpanQuery) this.clone();
116       clone.maskedQuery = rewritten;
117     }
118
119     if (clone != null) {
120       return clone;
121     } else {
122       return this;
123     }
124   }
125
126   @Override
127   public String toString(String field) {
128     StringBuilder buffer = new StringBuilder();
129     buffer.append("mask(");
130     buffer.append(maskedQuery.toString(field));
131     buffer.append(")");
132     buffer.append(ToStringUtils.boost(getBoost()));
133     buffer.append(" as ");
134     buffer.append(this.field);
135     return buffer.toString();
136   }
137   
138   @Override
139   public boolean equals(Object o) {
140     if (!(o instanceof FieldMaskingSpanQuery))
141       return false;
142     FieldMaskingSpanQuery other = (FieldMaskingSpanQuery) o;
143     return (this.getField().equals(other.getField())
144             && (this.getBoost() == other.getBoost())
145             && this.getMaskedQuery().equals(other.getMaskedQuery()));
146
147   }
148   
149   @Override
150   public int hashCode() {
151     return getMaskedQuery().hashCode()
152       ^ getField().hashCode()
153       ^ Float.floatToRawIntBits(getBoost());
154   }
155 }