1 package org.apache.lucene.queryParser;
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 import java.util.ArrayList;
21 import java.util.List;
24 import org.apache.lucene.analysis.Analyzer;
25 import org.apache.lucene.search.BooleanClause;
26 import org.apache.lucene.search.BooleanQuery;
27 import org.apache.lucene.search.MultiPhraseQuery;
28 import org.apache.lucene.search.PhraseQuery;
29 import org.apache.lucene.search.Query;
30 import org.apache.lucene.util.Version;
33 * A QueryParser which constructs queries to search multiple fields.
35 * @version $Revision: 965592 $
37 public class MultiFieldQueryParser extends QueryParser
39 protected String[] fields;
40 protected Map<String,Float> boosts;
43 * Creates a MultiFieldQueryParser.
44 * Allows passing of a map with term to Boost, and the boost to apply to each term.
46 * <p>It will, when parse(String query)
47 * is called, construct a query like this (assuming the query consists of
48 * two terms and you specify the two fields <code>title</code> and <code>body</code>):</p>
51 * (title:term1 body:term1) (title:term2 body:term2)
54 * <p>When setDefaultOperator(AND_OPERATOR) is set, the result will be:</p>
57 * +(title:term1 body:term1) +(title:term2 body:term2)
60 * <p>When you pass a boost (title=>5 body=>10) you can get </p>
63 * +(title:term1^5.0 body:term1^10.0) +(title:term2^5.0 body:term2^10.0)
66 * <p>In other words, all the query's terms must appear, but it doesn't matter in
67 * what fields they appear.</p>
69 public MultiFieldQueryParser(Version matchVersion, String[] fields, Analyzer analyzer, Map<String,Float> boosts) {
70 this(matchVersion, fields, analyzer);
75 * Creates a MultiFieldQueryParser.
77 * <p>It will, when parse(String query)
78 * is called, construct a query like this (assuming the query consists of
79 * two terms and you specify the two fields <code>title</code> and <code>body</code>):</p>
82 * (title:term1 body:term1) (title:term2 body:term2)
85 * <p>When setDefaultOperator(AND_OPERATOR) is set, the result will be:</p>
88 * +(title:term1 body:term1) +(title:term2 body:term2)
91 * <p>In other words, all the query's terms must appear, but it doesn't matter in
92 * what fields they appear.</p>
94 public MultiFieldQueryParser(Version matchVersion, String[] fields, Analyzer analyzer) {
95 super(matchVersion, null, analyzer);
100 protected Query getFieldQuery(String field, String queryText, int slop) throws ParseException {
102 List<BooleanClause> clauses = new ArrayList<BooleanClause>();
103 for (int i = 0; i < fields.length; i++) {
104 Query q = super.getFieldQuery(fields[i], queryText, true);
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 = boosts.get(fields[i]);
111 q.setBoost(boost.floatValue());
115 clauses.add(new BooleanClause(q, BooleanClause.Occur.SHOULD));
118 if (clauses.size() == 0) // happens for stopwords
120 return getBooleanQuery(clauses, true);
122 Query q = super.getFieldQuery(field, queryText, true);
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);
137 protected Query getFieldQuery(String field, String queryText, boolean quoted) throws ParseException {
139 List<BooleanClause> clauses = new ArrayList<BooleanClause>();
140 for (int i = 0; i < fields.length; i++) {
141 Query q = super.getFieldQuery(fields[i], queryText, quoted);
143 //If the user passes a map of boosts
144 if (boosts != null) {
145 //Get the boost from the map and apply them
146 Float boost = boosts.get(fields[i]);
148 q.setBoost(boost.floatValue());
151 clauses.add(new BooleanClause(q, BooleanClause.Occur.SHOULD));
154 if (clauses.size() == 0) // happens for stopwords
156 return getBooleanQuery(clauses, true);
158 Query q = super.getFieldQuery(field, queryText, quoted);
164 protected Query getFuzzyQuery(String field, String termStr, float minSimilarity) throws ParseException
167 List<BooleanClause> clauses = new ArrayList<BooleanClause>();
168 for (int i = 0; i < fields.length; i++) {
169 clauses.add(new BooleanClause(getFuzzyQuery(fields[i], termStr, minSimilarity),
170 BooleanClause.Occur.SHOULD));
172 return getBooleanQuery(clauses, true);
174 return super.getFuzzyQuery(field, termStr, minSimilarity);
178 protected Query getPrefixQuery(String field, String termStr) throws ParseException
181 List<BooleanClause> clauses = new ArrayList<BooleanClause>();
182 for (int i = 0; i < fields.length; i++) {
183 clauses.add(new BooleanClause(getPrefixQuery(fields[i], termStr),
184 BooleanClause.Occur.SHOULD));
186 return getBooleanQuery(clauses, true);
188 return super.getPrefixQuery(field, termStr);
192 protected Query getWildcardQuery(String field, String termStr) throws ParseException {
194 List<BooleanClause> clauses = new ArrayList<BooleanClause>();
195 for (int i = 0; i < fields.length; i++) {
196 clauses.add(new BooleanClause(getWildcardQuery(fields[i], termStr),
197 BooleanClause.Occur.SHOULD));
199 return getBooleanQuery(clauses, true);
201 return super.getWildcardQuery(field, termStr);
206 protected Query getRangeQuery(String field, String part1, String part2, boolean inclusive) throws ParseException {
208 List<BooleanClause> clauses = new ArrayList<BooleanClause>();
209 for (int i = 0; i < fields.length; i++) {
210 clauses.add(new BooleanClause(getRangeQuery(fields[i], part1, part2, inclusive),
211 BooleanClause.Occur.SHOULD));
213 return getBooleanQuery(clauses, true);
215 return super.getRangeQuery(field, part1, part2, inclusive);
219 * Parses a query which searches on the fields specified.
221 * If x fields are specified, this effectively constructs:
224 * (field1:query1) (field2:query2) (field3:query3)...(fieldx:queryx)
227 * @param matchVersion Lucene version to match; this is passed through to QueryParser.
228 * @param queries Queries strings to parse
229 * @param fields Fields to search on
230 * @param analyzer Analyzer to use
231 * @throws ParseException if query parsing fails
232 * @throws IllegalArgumentException if the length of the queries array differs
233 * from the length of the fields array
235 public static Query parse(Version matchVersion, String[] queries, String[] fields,
236 Analyzer analyzer) throws ParseException
238 if (queries.length != fields.length)
239 throw new IllegalArgumentException("queries.length != fields.length");
240 BooleanQuery bQuery = new BooleanQuery();
241 for (int i = 0; i < fields.length; i++)
243 QueryParser qp = new QueryParser(matchVersion, fields[i], analyzer);
244 Query q = qp.parse(queries[i]);
245 if (q!=null && // q never null, just being defensive
246 (!(q instanceof BooleanQuery) || ((BooleanQuery)q).getClauses().length>0)) {
247 bQuery.add(q, BooleanClause.Occur.SHOULD);
254 * Parses a query, searching on the fields specified.
255 * Use this if you need to specify certain fields as required,
256 * and others as prohibited.
260 * String[] fields = {"filename", "contents", "description"};
261 * BooleanClause.Occur[] flags = {BooleanClause.Occur.SHOULD,
262 * BooleanClause.Occur.MUST,
263 * BooleanClause.Occur.MUST_NOT};
264 * MultiFieldQueryParser.parse("query", fields, flags, analyzer);
268 * The code above would construct a query:
271 * (filename:query) +(contents:query) -(description:query)
275 * @param matchVersion Lucene version to match; this is passed through to QueryParser.
276 * @param query Query string to parse
277 * @param fields Fields to search on
278 * @param flags Flags describing the fields
279 * @param analyzer Analyzer to use
280 * @throws ParseException if query parsing fails
281 * @throws IllegalArgumentException if the length of the fields array differs
282 * from the length of the flags array
284 public static Query parse(Version matchVersion, String query, String[] fields,
285 BooleanClause.Occur[] flags, Analyzer analyzer) throws ParseException {
286 if (fields.length != flags.length)
287 throw new IllegalArgumentException("fields.length != flags.length");
288 BooleanQuery bQuery = new BooleanQuery();
289 for (int i = 0; i < fields.length; i++) {
290 QueryParser qp = new QueryParser(matchVersion, fields[i], analyzer);
291 Query q = qp.parse(query);
292 if (q!=null && // q never null, just being defensive
293 (!(q instanceof BooleanQuery) || ((BooleanQuery)q).getClauses().length>0)) {
294 bQuery.add(q, flags[i]);
301 * Parses a query, searching on the fields specified.
302 * Use this if you need to specify certain fields as required,
303 * and others as prohibited.
307 * String[] query = {"query1", "query2", "query3"};
308 * String[] fields = {"filename", "contents", "description"};
309 * BooleanClause.Occur[] flags = {BooleanClause.Occur.SHOULD,
310 * BooleanClause.Occur.MUST,
311 * BooleanClause.Occur.MUST_NOT};
312 * MultiFieldQueryParser.parse(query, fields, flags, analyzer);
316 * The code above would construct a query:
319 * (filename:query1) +(contents:query2) -(description:query3)
323 * @param matchVersion Lucene version to match; this is passed through to QueryParser.
324 * @param queries Queries string to parse
325 * @param fields Fields to search on
326 * @param flags Flags describing the fields
327 * @param analyzer Analyzer to use
328 * @throws ParseException if query parsing fails
329 * @throws IllegalArgumentException if the length of the queries, fields,
330 * and flags array differ
332 public static Query parse(Version matchVersion, String[] queries, String[] fields, BooleanClause.Occur[] flags,
333 Analyzer analyzer) throws ParseException
335 if (!(queries.length == fields.length && queries.length == flags.length))
336 throw new IllegalArgumentException("queries, fields, and flags array have have different length");
337 BooleanQuery bQuery = new BooleanQuery();
338 for (int i = 0; i < fields.length; i++)
340 QueryParser qp = new QueryParser(matchVersion, fields[i], analyzer);
341 Query q = qp.parse(queries[i]);
342 if (q!=null && // q never null, just being defensive
343 (!(q instanceof BooleanQuery) || ((BooleanQuery)q).getClauses().length>0)) {
344 bQuery.add(q, flags[i]);