001: package org.apache.lucene.queryParser;
002:
003: /**
004: * Licensed to the Apache Software Foundation (ASF) under one or more
005: * contributor license agreements. See the NOTICE file distributed with
006: * this work for additional information regarding copyright ownership.
007: * The ASF licenses this file to You under the Apache License, Version 2.0
008: * (the "License"); you may not use this file except in compliance with
009: * the License. You may obtain a copy of the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS,
015: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: * See the License for the specific language governing permissions and
017: * limitations under the License.
018: */
019:
020: import org.apache.lucene.analysis.Analyzer;
021: import org.apache.lucene.search.BooleanClause;
022: import org.apache.lucene.search.BooleanQuery;
023: import org.apache.lucene.search.MultiPhraseQuery;
024: import org.apache.lucene.search.PhraseQuery;
025: import org.apache.lucene.search.Query;
026:
027: import java.util.Vector;
028: import java.util.Map;
029:
030: /**
031: * A QueryParser which constructs queries to search multiple fields.
032: *
033: *
034: * @version $Revision: 564236 $
035: */
036: public class MultiFieldQueryParser extends QueryParser {
037: protected String[] fields;
038: protected Map boosts;
039:
040: /**
041: * Creates a MultiFieldQueryParser.
042: * Allows passing of a map with term to Boost, and the boost to apply to each term.
043: *
044: * <p>It will, when parse(String query)
045: * is called, construct a query like this (assuming the query consists of
046: * two terms and you specify the two fields <code>title</code> and <code>body</code>):</p>
047: *
048: * <code>
049: * (title:term1 body:term1) (title:term2 body:term2)
050: * </code>
051: *
052: * <p>When setDefaultOperator(AND_OPERATOR) is set, the result will be:</p>
053: *
054: * <code>
055: * +(title:term1 body:term1) +(title:term2 body:term2)
056: * </code>
057: *
058: * <p>When you pass a boost (title=>5 body=>10) you can get </p>
059: *
060: * <code>
061: * +(title:term1^5.0 body:term1^10.0) +(title:term2^5.0 body:term2^10.0)
062: * </code>
063: *
064: * <p>In other words, all the query's terms must appear, but it doesn't matter in
065: * what fields they appear.</p>
066: */
067: public MultiFieldQueryParser(String[] fields, Analyzer analyzer,
068: Map boosts) {
069: this (fields, analyzer);
070: this .boosts = boosts;
071: }
072:
073: /**
074: * Creates a MultiFieldQueryParser.
075: *
076: * <p>It will, when parse(String query)
077: * is called, construct a query like this (assuming the query consists of
078: * two terms and you specify the two fields <code>title</code> and <code>body</code>):</p>
079: *
080: * <code>
081: * (title:term1 body:term1) (title:term2 body:term2)
082: * </code>
083: *
084: * <p>When setDefaultOperator(AND_OPERATOR) is set, the result will be:</p>
085: *
086: * <code>
087: * +(title:term1 body:term1) +(title:term2 body:term2)
088: * </code>
089: *
090: * <p>In other words, all the query's terms must appear, but it doesn't matter in
091: * what fields they appear.</p>
092: */
093: public MultiFieldQueryParser(String[] fields, Analyzer analyzer) {
094: super (null, analyzer);
095: this .fields = fields;
096: }
097:
098: protected Query getFieldQuery(String field, String queryText,
099: int slop) throws ParseException {
100: if (field == null) {
101: Vector clauses = new Vector();
102: for (int i = 0; i < fields.length; i++) {
103: Query q = getFieldQuery(fields[i], queryText);
104: if (q != null) {
105: //If the user passes a map of boosts
106: if (boosts != null) {
107: //Get the boost from the map and apply them
108: Float boost = (Float) boosts.get(fields[i]);
109: if (boost != null) {
110: q.setBoost(boost.floatValue());
111: }
112: }
113: if (q instanceof PhraseQuery) {
114: ((PhraseQuery) q).setSlop(slop);
115: }
116: if (q instanceof MultiPhraseQuery) {
117: ((MultiPhraseQuery) q).setSlop(slop);
118: }
119: clauses.add(new BooleanClause(q,
120: BooleanClause.Occur.SHOULD));
121: }
122: }
123: if (clauses.size() == 0) // happens for stopwords
124: return null;
125: return getBooleanQuery(clauses, true);
126: }
127: return super .getFieldQuery(field, queryText);
128: }
129:
130: protected Query getFieldQuery(String field, String queryText)
131: throws ParseException {
132: return getFieldQuery(field, queryText, 0);
133: }
134:
135: protected Query getFuzzyQuery(String field, String termStr,
136: float minSimilarity) throws ParseException {
137: if (field == null) {
138: Vector clauses = new Vector();
139: for (int i = 0; i < fields.length; i++) {
140: clauses.add(new BooleanClause(getFuzzyQuery(fields[i],
141: termStr, minSimilarity),
142: BooleanClause.Occur.SHOULD));
143: }
144: return getBooleanQuery(clauses, true);
145: }
146: return super .getFuzzyQuery(field, termStr, minSimilarity);
147: }
148:
149: protected Query getPrefixQuery(String field, String termStr)
150: throws ParseException {
151: if (field == null) {
152: Vector clauses = new Vector();
153: for (int i = 0; i < fields.length; i++) {
154: clauses.add(new BooleanClause(getPrefixQuery(fields[i],
155: termStr), BooleanClause.Occur.SHOULD));
156: }
157: return getBooleanQuery(clauses, true);
158: }
159: return super .getPrefixQuery(field, termStr);
160: }
161:
162: protected Query getWildcardQuery(String field, String termStr)
163: throws ParseException {
164: if (field == null) {
165: Vector clauses = new Vector();
166: for (int i = 0; i < fields.length; i++) {
167: clauses
168: .add(new BooleanClause(getWildcardQuery(
169: fields[i], termStr),
170: BooleanClause.Occur.SHOULD));
171: }
172: return getBooleanQuery(clauses, true);
173: }
174: return super .getWildcardQuery(field, termStr);
175: }
176:
177: protected Query getRangeQuery(String field, String part1,
178: String part2, boolean inclusive) throws ParseException {
179: if (field == null) {
180: Vector clauses = new Vector();
181: for (int i = 0; i < fields.length; i++) {
182: clauses.add(new BooleanClause(getRangeQuery(fields[i],
183: part1, part2, inclusive),
184: BooleanClause.Occur.SHOULD));
185: }
186: return getBooleanQuery(clauses, true);
187: }
188: return super .getRangeQuery(field, part1, part2, inclusive);
189: }
190:
191: /**
192: * Parses a query which searches on the fields specified.
193: * <p>
194: * If x fields are specified, this effectively constructs:
195: * <pre>
196: * <code>
197: * (field1:query1) (field2:query2) (field3:query3)...(fieldx:queryx)
198: * </code>
199: * </pre>
200: * @param queries Queries strings to parse
201: * @param fields Fields to search on
202: * @param analyzer Analyzer to use
203: * @throws ParseException if query parsing fails
204: * @throws IllegalArgumentException if the length of the queries array differs
205: * from the length of the fields array
206: */
207: public static Query parse(String[] queries, String[] fields,
208: Analyzer analyzer) throws ParseException {
209: if (queries.length != fields.length)
210: throw new IllegalArgumentException(
211: "queries.length != fields.length");
212: BooleanQuery bQuery = new BooleanQuery();
213: for (int i = 0; i < fields.length; i++) {
214: QueryParser qp = new QueryParser(fields[i], analyzer);
215: Query q = qp.parse(queries[i]);
216: if (q != null && // q never null, just being defensive
217: (!(q instanceof BooleanQuery) || ((BooleanQuery) q)
218: .getClauses().length > 0)) {
219: bQuery.add(q, BooleanClause.Occur.SHOULD);
220: }
221: }
222: return bQuery;
223: }
224:
225: /**
226: * Parses a query, searching on the fields specified.
227: * Use this if you need to specify certain fields as required,
228: * and others as prohibited.
229: * <p><pre>
230: * Usage:
231: * <code>
232: * String[] fields = {"filename", "contents", "description"};
233: * BooleanClause.Occur[] flags = {BooleanClause.Occur.SHOULD,
234: * BooleanClause.Occur.MUST,
235: * BooleanClause.Occur.MUST_NOT};
236: * MultiFieldQueryParser.parse("query", fields, flags, analyzer);
237: * </code>
238: * </pre>
239: *<p>
240: * The code above would construct a query:
241: * <pre>
242: * <code>
243: * (filename:query) +(contents:query) -(description:query)
244: * </code>
245: * </pre>
246: *
247: * @param query Query string to parse
248: * @param fields Fields to search on
249: * @param flags Flags describing the fields
250: * @param analyzer Analyzer to use
251: * @throws ParseException if query parsing fails
252: * @throws IllegalArgumentException if the length of the fields array differs
253: * from the length of the flags array
254: */
255: public static Query parse(String query, String[] fields,
256: BooleanClause.Occur[] flags, Analyzer analyzer)
257: throws ParseException {
258: if (fields.length != flags.length)
259: throw new IllegalArgumentException(
260: "fields.length != flags.length");
261: BooleanQuery bQuery = new BooleanQuery();
262: for (int i = 0; i < fields.length; i++) {
263: QueryParser qp = new QueryParser(fields[i], analyzer);
264: Query q = qp.parse(query);
265: if (q != null && // q never null, just being defensive
266: (!(q instanceof BooleanQuery) || ((BooleanQuery) q)
267: .getClauses().length > 0)) {
268: bQuery.add(q, flags[i]);
269: }
270: }
271: return bQuery;
272: }
273:
274: /**
275: * Parses a query, searching on the fields specified.
276: * Use this if you need to specify certain fields as required,
277: * and others as prohibited.
278: * <p><pre>
279: * Usage:
280: * <code>
281: * String[] query = {"query1", "query2", "query3"};
282: * String[] fields = {"filename", "contents", "description"};
283: * BooleanClause.Occur[] flags = {BooleanClause.Occur.SHOULD,
284: * BooleanClause.Occur.MUST,
285: * BooleanClause.Occur.MUST_NOT};
286: * MultiFieldQueryParser.parse(query, fields, flags, analyzer);
287: * </code>
288: * </pre>
289: *<p>
290: * The code above would construct a query:
291: * <pre>
292: * <code>
293: * (filename:query1) +(contents:query2) -(description:query3)
294: * </code>
295: * </pre>
296: *
297: * @param queries Queries string to parse
298: * @param fields Fields to search on
299: * @param flags Flags describing the fields
300: * @param analyzer Analyzer to use
301: * @throws ParseException if query parsing fails
302: * @throws IllegalArgumentException if the length of the queries, fields,
303: * and flags array differ
304: */
305: public static Query parse(String[] queries, String[] fields,
306: BooleanClause.Occur[] flags, Analyzer analyzer)
307: throws ParseException {
308: if (!(queries.length == fields.length && queries.length == flags.length))
309: throw new IllegalArgumentException(
310: "queries, fields, and flags array have have different length");
311: BooleanQuery bQuery = new BooleanQuery();
312: for (int i = 0; i < fields.length; i++) {
313: QueryParser qp = new QueryParser(fields[i], analyzer);
314: Query q = qp.parse(queries[i]);
315: if (q != null && // q never null, just being defensive
316: (!(q instanceof BooleanQuery) || ((BooleanQuery) q)
317: .getClauses().length > 0)) {
318: bQuery.add(q, flags[i]);
319: }
320: }
321: return bQuery;
322: }
323:
324: }
|