001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.solr.search;
017:
018: import org.apache.lucene.queryParser.QueryParser;
019: import org.apache.lucene.queryParser.ParseException;
020: import org.apache.lucene.search.*;
021: import org.apache.lucene.index.Term;
022: import org.apache.solr.schema.IndexSchema;
023: import org.apache.solr.schema.FieldType;
024:
025: // TODO: implement the analysis of simple fields with
026: // FieldType.toInternal() instead of going through the
027: // analyzer. Should lead to faster query parsing.
028:
029: /**
030: * A variation on the Lucene QueryParser which knows about the field
031: * types and query time analyzers configured in Solr's schema.xml.
032: *
033: * <p>
034: * This class also deviates from the Lucene QueryParser by using
035: * ConstantScore versions of RangeQuery and PrefixQuery to prevent
036: * TooManyClauses exceptions.
037: * </p>
038: *
039: * <p>
040: * If the magic field name "<code>_val_</code>" is used in a term or
041: * phrase query, the value is parsed as a function.
042: * </p>
043: *
044: * @see QueryParsing#parseFunction
045: * @see ConstantScoreRangeQuery
046: * @see ConstantScorePrefixQuery
047: * @author yonik
048: */
049: public class SolrQueryParser extends QueryParser {
050: protected final IndexSchema schema;
051:
052: /**
053: * Constructs a SolrQueryParser using the schema to understand the
054: * formats and datatypes of each field. Only the defaultSearchField
055: * will be used from the IndexSchema (unless overridden),
056: * <solrQueryParser> will not be used.
057: *
058: * @param schema Used for default search field name if defaultField is null and field information is used for analysis
059: * @param defaultField default field used for unspecified search terms. if null, the schema default field is used
060: * @see IndexSchema#getSolrQueryParser(String defaultField)
061: */
062: public SolrQueryParser(IndexSchema schema, String defaultField) {
063: super (defaultField == null ? schema.getDefaultSearchFieldName()
064: : defaultField, schema.getQueryAnalyzer());
065: this .schema = schema;
066: setLowercaseExpandedTerms(false);
067: }
068:
069: protected Query getFieldQuery(String field, String queryText)
070: throws ParseException {
071: // intercept magic field name of "_" to use as a hook for our
072: // own functions.
073: if (field.equals("_val_")) {
074: return QueryParsing.parseFunction(queryText, schema);
075: }
076:
077: // default to a normal field query
078: return super .getFieldQuery(field, queryText);
079: }
080:
081: protected Query getRangeQuery(String field, String part1,
082: String part2, boolean inclusive) throws ParseException {
083: FieldType ft = schema.getFieldType(field);
084: return new ConstantScoreRangeQuery(field,
085: "*".equals(part1) ? null : ft.toInternal(part1), "*"
086: .equals(part2) ? null : ft.toInternal(part2),
087: inclusive, inclusive);
088: }
089:
090: protected Query getPrefixQuery(String field, String termStr)
091: throws ParseException {
092: if (getLowercaseExpandedTerms()) {
093: termStr = termStr.toLowerCase();
094: }
095:
096: // TODO: toInternal() won't necessarily work on partial
097: // values, so it looks like we need a getPrefix() function
098: // on fieldtype? Or at the minimum, a method on fieldType
099: // that can tell me if I should lowercase or not...
100: // Schema could tell if lowercase filter is in the chain,
101: // but a more sure way would be to run something through
102: // the first time and check if it got lowercased.
103:
104: // TODO: throw exception if field type doesn't support prefixes?
105: // (sortable numeric types don't do prefixes, but can do range queries)
106: Term t = new Term(field, termStr);
107: return new ConstantScorePrefixQuery(t);
108: }
109:
110: }
|