| MultiFieldQueryParser.java |
1 package org.apache.lucene.queryParser;
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.util.ArrayList;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Vector;
24
25 import org.apache.lucene.analysis.Analyzer;
26 import org.apache.lucene.search.BooleanClause;
27 import org.apache.lucene.search.BooleanQuery;
28 import org.apache.lucene.search.MultiPhraseQuery;
29 import org.apache.lucene.search.PhraseQuery;
30 import org.apache.lucene.search.Query;
31
32 /**
33 * A QueryParser which constructs queries to search multiple fields.
34 *
35 *
36 * @version $Revision: 692921 $
37 */
38 public class MultiFieldQueryParser extends QueryParser
39 {
40 protected String[] fields;
41 protected Map boosts;
42
43 /**
44 * Creates a MultiFieldQueryParser.
45 * Allows passing of a map with term to Boost, and the boost to apply to each term.
46 *
47 * <p>It will, when parse(String query)
48 * is called, construct a query like this (assuming the query consists of
49 * two terms and you specify the two fields <code>title</code> and <code>body</code>):</p>
50 *
51 * <code>
52 * (title:term1 body:term1) (title:term2 body:term2)
53 * </code>
54 *
55 * <p>When setDefaultOperator(AND_OPERATOR) is set, the result will be:</p>
56 *
57 * <code>
58 * +(title:term1 body:term1) +(title:term2 body:term2)
59 * </code>
60 *
61 * <p>When you pass a boost (title=>5 body=>10) you can get </p>
62 *
63 * <code>
64 * +(title:term1^5.0 body:term1^10.0) +(title:term2^5.0 body:term2^10.0)
65 * </code>
66 *
67 * <p>In other words, all the query's terms must appear, but it doesn't matter in
68 * what fields they appear.</p>
69 */
70 public MultiFieldQueryParser(String[] fields, Analyzer analyzer, Map boosts) {
71 this(fields,analyzer);
72 this.boosts = boosts;
73 }
74
75 /**
76 * Creates a MultiFieldQueryParser.
77 *
78 * <p>It will, when parse(String query)
79 * is called, construct a query like this (assuming the query consists of
80 * two terms and you specify the two fields <code>title</code> and <code>body</code>):</p>
81 *
82 * <code>
83 * (title:term1 body:term1) (title:term2 body:term2)
84 * </code>
85 *
86 * <p>When setDefaultOperator(AND_OPERATOR) is set, the result will be:</p>
87 *
88 * <code>
89 * +(title:term1 body:term1) +(title:term2 body:term2)
90 * </code>
91 *
92 * <p>In other words, all the query's terms must appear, but it doesn't matter in
93 * what fields they appear.</p>
94 */
95 public MultiFieldQueryParser(String[] fields, Analyzer analyzer) {
96 super(null, analyzer);
97 this.fields = fields;
98 }
99
100 protected Query getFieldQuery(String field, String queryText, int slop) throws ParseException {
101 if (field == null) {
102 List clauses = new ArrayList();
103 for (int i = 0; i < fields.length; i++) {
104 Query q = super.getFieldQuery(fields[i], queryText);
105 if (q != null) {
106 //If the user passes a map of boosts
107 if (boosts != null) {
108 //Get the boost from the map and apply them
109 Float boost = (Float)boosts.get(fields[i]);
110 if (boost != null) {
111 q.setBoost(boost.floatValue());
112 }
113 }
114 applySlop(q,slop);
115 clauses.add(new BooleanClause(q, BooleanClause.Occur.SHOULD));
116 }
117 }
118 if (clauses.size() == 0) // happens for stopwords
119 return null;
120 return getBooleanQuery(clauses, true);
121 }
122 Query q = super.getFieldQuery(field, queryText);
123 applySlop(q,slop);
124 return q;
125 }
126
127 private void applySlop(Query q, int slop) {
128 if (q instanceof PhraseQuery) {
129 ((PhraseQuery) q).setSlop(slop);
130 } else if (q instanceof MultiPhraseQuery) {
131 ((MultiPhraseQuery) q).setSlop(slop);
132 }
133 }
134
135
136 protected Query getFieldQuery(String field, String queryText) throws ParseException {
137 return getFieldQuery(field, queryText, 0);
138 }
139
140
141 protected Query getFuzzyQuery(String field, String termStr, float minSimilarity) throws ParseException
142 {
143 if (field == null) {
144 List clauses = new ArrayList();
145 for (int i = 0; i < fields.length; i++) {
146 clauses.add(new BooleanClause(getFuzzyQuery(fields[i], termStr, minSimilarity),
147 BooleanClause.Occur.SHOULD));
148 }
149 return getBooleanQuery(clauses, true);
150 }
151 return super.getFuzzyQuery(field, termStr, minSimilarity);
152 }
153
154 protected Query getPrefixQuery(String field, String termStr) throws ParseException
155 {
156 if (field == null) {
157 List clauses = new ArrayList();
158 for (int i = 0; i < fields.length; i++) {
159 clauses.add(new BooleanClause(getPrefixQuery(fields[i], termStr),
160 BooleanClause.Occur.SHOULD));
161 }
162 return getBooleanQuery(clauses, true);
163 }
164 return super.getPrefixQuery(field, termStr);
165 }
166
167 protected Query getWildcardQuery(String field, String termStr) throws ParseException {
168 if (field == null) {
169 List clauses = new ArrayList();
170 for (int i = 0; i < fields.length; i++) {
171 clauses.add(new BooleanClause(getWildcardQuery(fields[i], termStr),
172 BooleanClause.Occur.SHOULD));
173 }
174 return getBooleanQuery(clauses, true);
175 }
176 return super.getWildcardQuery(field, termStr);
177 }
178
179
180 protected Query getRangeQuery(String field, String part1, String part2, boolean inclusive) throws ParseException {
181 if (field == null) {
182 List clauses = new ArrayList();
183 for (int i = 0; i < fields.length; i++) {
184 clauses.add(new BooleanClause(getRangeQuery(fields[i], part1, part2, inclusive),
185 BooleanClause.Occur.SHOULD));
186 }
187 return getBooleanQuery(clauses, true);
188 }
189 return super.getRangeQuery(field, part1, part2, inclusive);
190 }
191
192 /**
193 * Parses a query which searches on the fields specified.
194 * <p>
195 * If x fields are specified, this effectively constructs:
196 * <pre>
197 * <code>
198 * (field1:query1) (field2:query2) (field3:query3)...(fieldx:queryx)
199 * </code>
200 * </pre>
201 * @param queries Queries strings to parse
202 * @param fields Fields to search on
203 * @param analyzer Analyzer to use
204 * @throws ParseException if query parsing fails
205 * @throws IllegalArgumentException if the length of the queries array differs
206 * from the length of the fields array
207 */
208 public static Query parse(String[] queries, String[] fields,
209 Analyzer analyzer) throws ParseException
210 {
211 if (queries.length != fields.length)
212 throw new IllegalArgumentException("queries.length != fields.length");
213 BooleanQuery bQuery = new BooleanQuery();
214 for (int i = 0; i < fields.length; i++)
215 {
216 QueryParser qp = new QueryParser(fields[i], analyzer);
217 Query q = qp.parse(queries[i]);
218 if (q!=null && // q never null, just being defensive
219 (!(q instanceof BooleanQuery) || ((BooleanQuery)q).getClauses().length>0)) {
220 bQuery.add(q, BooleanClause.Occur.SHOULD);
221 }
222 }
223 return bQuery;
224 }
225
226 /**
227 * Parses a query, searching on the fields specified.
228 * Use this if you need to specify certain fields as required,
229 * and others as prohibited.
230 * <p><pre>
231 * Usage:
232 * <code>
233 * String[] fields = {"filename", "contents", "description"};
234 * BooleanClause.Occur[] flags = {BooleanClause.Occur.SHOULD,
235 * BooleanClause.Occur.MUST,
236 * BooleanClause.Occur.MUST_NOT};
237 * MultiFieldQueryParser.parse("query", fields, flags, analyzer);
238 * </code>
239 * </pre>
240 *<p>
241 * The code above would construct a query:
242 * <pre>
243 * <code>
244 * (filename:query) +(contents:query) -(description:query)
245 * </code>
246 * </pre>
247 *
248 * @param query Query string to parse
249 * @param fields Fields to search on
250 * @param flags Flags describing the fields
251 * @param analyzer Analyzer to use
252 * @throws ParseException if query parsing fails
253 * @throws IllegalArgumentException if the length of the fields array differs
254 * from the length of the flags array
255 */
256 public static Query parse(String query, String[] fields,
257 BooleanClause.Occur[] flags, Analyzer analyzer) throws ParseException {
258 if (fields.length != flags.length)
259 throw new IllegalArgumentException("fields.length != flags.length");
260 BooleanQuery bQuery = new BooleanQuery();
261 for (int i = 0; i < fields.length; i++) {
262 QueryParser qp = new QueryParser(fields[i], analyzer);
263 Query q = qp.parse(query);
264 if (q!=null && // q never null, just being defensive
265 (!(q instanceof BooleanQuery) || ((BooleanQuery)q).getClauses().length>0)) {
266 bQuery.add(q, flags[i]);
267 }
268 }
269 return bQuery;
270 }
271
272 /**
273 * Parses a query, searching on the fields specified.
274 * Use this if you need to specify certain fields as required,
275 * and others as prohibited.
276 * <p><pre>
277 * Usage:
278 * <code>
279 * String[] query = {"query1", "query2", "query3"};
280 * String[] fields = {"filename", "contents", "description"};
281 * BooleanClause.Occur[] flags = {BooleanClause.Occur.SHOULD,
282 * BooleanClause.Occur.MUST,
283 * BooleanClause.Occur.MUST_NOT};
284 * MultiFieldQueryParser.parse(query, fields, flags, analyzer);
285 * </code>
286 * </pre>
287 *<p>
288 * The code above would construct a query:
289 * <pre>
290 * <code>
291 * (filename:query1) +(contents:query2) -(description:query3)
292 * </code>
293 * </pre>
294 *
295 * @param queries Queries string to parse
296 * @param fields Fields to search on
297 * @param flags Flags describing the fields
298 * @param analyzer Analyzer to use
299 * @throws ParseException if query parsing fails
300 * @throws IllegalArgumentException if the length of the queries, fields,
301 * and flags array differ
302 */
303 public static Query parse(String[] queries, String[] fields, BooleanClause.Occur[] flags,
304 Analyzer analyzer) throws ParseException
305 {
306 if (!(queries.length == fields.length && queries.length == flags.length))
307 throw new IllegalArgumentException("queries, fields, and flags array have have different length");
308 BooleanQuery bQuery = new BooleanQuery();
309 for (int i = 0; i < fields.length; i++)
310 {
311 QueryParser qp = new QueryParser(fields[i], analyzer);
312 Query q = qp.parse(queries[i]);
313 if (q!=null && // q never null, just being defensive
314 (!(q instanceof BooleanQuery) || ((BooleanQuery)q).getClauses().length>0)) {
315 bQuery.add(q, flags[i]);
316 }
317 }
318 return bQuery;
319 }
320
321 }
322 | MultiFieldQueryParser.java |