add --shared
[pylucene.git] / lucene-java-3.4.0 / lucene / src / java / org / apache / lucene / queryParser / 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
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;
31
32 /**
33  * A QueryParser which constructs queries to search multiple fields.
34  *
35  * @version $Revision: 965592 $
36  */
37 public class MultiFieldQueryParser extends QueryParser
38 {
39   protected String[] fields;
40   protected Map<String,Float> boosts;
41
42   /**
43    * Creates a MultiFieldQueryParser. 
44    * Allows passing of a map with term to Boost, and the boost to apply to each term.
45    *
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>
49    * 
50    * <code>
51    * (title:term1 body:term1) (title:term2 body:term2)
52    * </code>
53    *
54    * <p>When setDefaultOperator(AND_OPERATOR) is set, the result will be:</p>
55    *  
56    * <code>
57    * +(title:term1 body:term1) +(title:term2 body:term2)
58    * </code>
59    * 
60    * <p>When you pass a boost (title=>5 body=>10) you can get </p>
61    * 
62    * <code>
63    * +(title:term1^5.0 body:term1^10.0) +(title:term2^5.0 body:term2^10.0)
64    * </code>
65    *
66    * <p>In other words, all the query's terms must appear, but it doesn't matter in
67    * what fields they appear.</p>
68    */
69   public MultiFieldQueryParser(Version matchVersion, String[] fields, Analyzer analyzer, Map<String,Float> boosts) {
70     this(matchVersion, fields, analyzer);
71     this.boosts = boosts;
72   }
73   
74   /**
75    * Creates a MultiFieldQueryParser.
76    *
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>
80    * 
81    * <code>
82    * (title:term1 body:term1) (title:term2 body:term2)
83    * </code>
84    *
85    * <p>When setDefaultOperator(AND_OPERATOR) is set, the result will be:</p>
86    *  
87    * <code>
88    * +(title:term1 body:term1) +(title:term2 body:term2)
89    * </code>
90    * 
91    * <p>In other words, all the query's terms must appear, but it doesn't matter in
92    * what fields they appear.</p>
93    */
94   public MultiFieldQueryParser(Version matchVersion, String[] fields, Analyzer analyzer) {
95     super(matchVersion, null, analyzer);
96     this.fields = fields;
97   }
98   
99   @Override
100   protected Query getFieldQuery(String field, String queryText, int slop) throws ParseException {
101     if (field == null) {
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);
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 = 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, true);
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   @Override
137   protected Query getFieldQuery(String field, String queryText, boolean quoted) throws ParseException {
138     if (field == null) {
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);
142         if (q != null) {
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]);
147             if (boost != null) {
148               q.setBoost(boost.floatValue());
149             }
150           }
151           clauses.add(new BooleanClause(q, BooleanClause.Occur.SHOULD));
152         }
153       }
154       if (clauses.size() == 0)  // happens for stopwords
155         return null;
156       return getBooleanQuery(clauses, true);
157     }
158     Query q = super.getFieldQuery(field, queryText, quoted);
159     return q;
160   }
161
162
163   @Override
164   protected Query getFuzzyQuery(String field, String termStr, float minSimilarity) throws ParseException
165   {
166     if (field == null) {
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));
171       }
172       return getBooleanQuery(clauses, true);
173     }
174     return super.getFuzzyQuery(field, termStr, minSimilarity);
175   }
176
177   @Override
178   protected Query getPrefixQuery(String field, String termStr) throws ParseException
179   {
180     if (field == null) {
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));
185       }
186       return getBooleanQuery(clauses, true);
187     }
188     return super.getPrefixQuery(field, termStr);
189   }
190
191   @Override
192   protected Query getWildcardQuery(String field, String termStr) throws ParseException {
193     if (field == null) {
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));
198       }
199       return getBooleanQuery(clauses, true);
200     }
201     return super.getWildcardQuery(field, termStr);
202   }
203
204  
205   @Override
206   protected Query getRangeQuery(String field, String part1, String part2, boolean inclusive) throws ParseException {
207     if (field == null) {
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));
212       }
213       return getBooleanQuery(clauses, true);
214     }
215     return super.getRangeQuery(field, part1, part2, inclusive);
216   }
217
218   /**
219    * Parses a query which searches on the fields specified.
220    * <p>
221    * If x fields are specified, this effectively constructs:
222    * <pre>
223    * <code>
224    * (field1:query1) (field2:query2) (field3:query3)...(fieldx:queryx)
225    * </code>
226    * </pre>
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
234    */
235   public static Query parse(Version matchVersion, String[] queries, String[] fields,
236       Analyzer analyzer) throws ParseException
237   {
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++)
242     {
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);
248       }
249     }
250     return bQuery;
251   }
252
253   /**
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.
257    * <p><pre>
258    * Usage:
259    * <code>
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);
265    * </code>
266    * </pre>
267    *<p>
268    * The code above would construct a query:
269    * <pre>
270    * <code>
271    * (filename:query) +(contents:query) -(description:query)
272    * </code>
273    * </pre>
274    *
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
283    */
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]);
295       }
296     }
297     return bQuery;
298   }
299
300   /**
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.
304    * <p><pre>
305    * Usage:
306    * <code>
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);
313    * </code>
314    * </pre>
315    *<p>
316    * The code above would construct a query:
317    * <pre>
318    * <code>
319    * (filename:query1) +(contents:query2) -(description:query3)
320    * </code>
321    * </pre>
322    *
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
331    */
332   public static Query parse(Version matchVersion, String[] queries, String[] fields, BooleanClause.Occur[] flags,
333       Analyzer analyzer) throws ParseException
334   {
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++)
339     {
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]);
345       }
346     }
347     return bQuery;
348   }
349
350 }