📄 precedencequeryparser.jj
字号:
/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */options { STATIC=false; JAVA_UNICODE_ESCAPE=true; USER_CHAR_STREAM=true;}PARSER_BEGIN(PrecedenceQueryParser)package org.apache.lucene.queryParser.precedence;import java.util.Vector;import java.io.*;import java.text.*;import java.util.*;import org.apache.lucene.index.Term;import org.apache.lucene.analysis.*;import org.apache.lucene.document.*;import org.apache.lucene.search.*;import org.apache.lucene.util.Parameter;/** * Experimental query parser variant designed to handle operator precedence * in a more sensible fashion than QueryParser. There are still some * open issues with this parser. The following tests are currently failing * in TestPrecedenceQueryParser and are disabled to make this test pass: * <ul> * <li> testSimple * <li> testWildcard * <li> testPrecedence * </ul> * * This class is generated by JavaCC. The only method that clients should need * to call is {@link #parse(String)}. * * The syntax for query strings is as follows: * A Query is a series of clauses. * A clause may be prefixed by: * <ul> * <li> a plus (<code>+</code>) or a minus (<code>-</code>) sign, indicating * that the clause is required or prohibited respectively; or * <li> a term followed by a colon, indicating the field to be searched. * This enables one to construct queries which search multiple fields. * </ul> * * A clause may be either: * <ul> * <li> a term, indicating all the documents that contain this term; or * <li> a nested query, enclosed in parentheses. Note that this may be used * with a <code>+</code>/<code>-</code> prefix to require any of a set of * terms. * </ul> * * Thus, in BNF, the query grammar is: * <pre> * Query ::= ( Clause )* * Clause ::= ["+", "-"] [<TERM> ":"] ( <TERM> | "(" Query ")" ) * </pre> * * <p> * Examples of appropriately formatted queries can be found in the <a * href="http://jakarta.apache.org/lucene/docs/queryparsersyntax.html">query syntax * documentation</a>. * </p> * * @author Brian Goetz * @author Peter Halacsy * @author Tatu Saloranta */public class PrecedenceQueryParser { private static final int CONJ_NONE = 0; private static final int CONJ_AND = 1; private static final int CONJ_OR = 2; private static final int MOD_NONE = 0; private static final int MOD_NOT = 10; private static final int MOD_REQ = 11; // make it possible to call setDefaultOperator() without accessing // the nested class: public static final Operator AND_OPERATOR = Operator.AND; public static final Operator OR_OPERATOR = Operator.OR; /** The actual operator that parser uses to combine query terms */ private Operator operator = OR_OPERATOR; boolean lowercaseExpandedTerms = true; Analyzer analyzer; String field; int phraseSlop = 0; float fuzzyMinSim = FuzzyQuery.defaultMinSimilarity; int fuzzyPrefixLength = FuzzyQuery.defaultPrefixLength; Locale locale = Locale.getDefault(); static final class Operator extends Parameter { private Operator(String name) { super(name); } static final Operator OR = new Operator("OR"); static final Operator AND = new Operator("AND"); } /** Constructs a query parser. * @param f the default field for query terms. * @param a used to find terms in the query text. */ public PrecedenceQueryParser(String f, Analyzer a) { this(new FastCharStream(new StringReader(""))); analyzer = a; field = f; } /** Parses a query string, returning a {@link org.apache.lucene.search.Query}. * @param expression the query string to be parsed. * @throws ParseException if the parsing fails */ public Query parse(String expression) throws ParseException { // optimize empty query to be empty BooleanQuery if (expression == null || expression.trim().length() == 0) { return new BooleanQuery(); } ReInit(new FastCharStream(new StringReader(expression))); try { Query query = Query(field); return (query != null) ? query : new BooleanQuery(); } catch (TokenMgrError tme) { throw new ParseException(tme.getMessage()); } catch (BooleanQuery.TooManyClauses tmc) { throw new ParseException("Too many boolean clauses"); } } /** * @return Returns the analyzer. */ public Analyzer getAnalyzer() { return analyzer; } /** * @return Returns the field. */ public String getField() { return field; } /** * Get the minimal similarity for fuzzy queries. */ public float getFuzzyMinSim() { return fuzzyMinSim; } /** * Set the minimum similarity for fuzzy queries. * Default is 0.5f. */ public void setFuzzyMinSim(float fuzzyMinSim) { this.fuzzyMinSim = fuzzyMinSim; } /** * Get the prefix length for fuzzy queries. * @return Returns the fuzzyPrefixLength. */ public int getFuzzyPrefixLength() { return fuzzyPrefixLength; } /** * Set the prefix length for fuzzy queries. Default is 0. * @param fuzzyPrefixLength The fuzzyPrefixLength to set. */ public void setFuzzyPrefixLength(int fuzzyPrefixLength) { this.fuzzyPrefixLength = fuzzyPrefixLength; } /** * Sets the default slop for phrases. If zero, then exact phrase matches * are required. Default value is zero. */ public void setPhraseSlop(int phraseSlop) { this.phraseSlop = phraseSlop; } /** * Gets the default slop for phrases. */ public int getPhraseSlop() { return phraseSlop; } /** * Sets the boolean operator of the QueryParser. * In default mode (<code>OR_OPERATOR</code>) terms without any modifiers * are considered optional: for example <code>capital of Hungary</code> is equal to * <code>capital OR of OR Hungary</code>.<br/> * In <code>AND_OPERATOR</code> mode terms are considered to be in conjuction: the * above mentioned query is parsed as <code>capital AND of AND Hungary</code> */ public void setDefaultOperator(Operator op) { this.operator = op; } /** * Gets implicit operator setting, which will be either AND_OPERATOR * or OR_OPERATOR. */ public Operator getDefaultOperator() { return operator; } /** * Whether terms of wildcard, prefix, fuzzy and range queries are to be automatically * lower-cased or not. Default is <code>true</code>. */ public void setLowercaseExpandedTerms(boolean lowercaseExpandedTerms) { this.lowercaseExpandedTerms = lowercaseExpandedTerms; } /** * @see #setLowercaseExpandedTerms(boolean) */ public boolean getLowercaseExpandedTerms() { return lowercaseExpandedTerms; } /** * Set locale used by date range parsing. */ public void setLocale(Locale locale) { this.locale = locale; } /** * Returns current locale, allowing access by subclasses. */ public Locale getLocale() { return locale; } protected void addClause(Vector clauses, int conj, int modifier, Query q) { boolean required, prohibited; // If this term is introduced by AND, make the preceding term required, // unless it's already prohibited if (clauses.size() > 0 && conj == CONJ_AND) { BooleanClause c = (BooleanClause) clauses.elementAt(clauses.size()-1); if (!c.isProhibited()) c.setOccur(BooleanClause.Occur.MUST); } if (clauses.size() > 0 && operator == AND_OPERATOR && conj == CONJ_OR) { // If this term is introduced by OR, make the preceding term optional, // unless it's prohibited (that means we leave -a OR b but +a OR b-->a OR b) // notice if the input is a OR b, first term is parsed as required; without // this modification a OR b would parsed as +a OR b BooleanClause c = (BooleanClause) clauses.elementAt(clauses.size()-1); if (!c.isProhibited()) c.setOccur(BooleanClause.Occur.SHOULD); } // We might have been passed a null query; the term might have been // filtered away by the analyzer. if (q == null) return; if (operator == OR_OPERATOR) { // We set REQUIRED if we're introduced by AND or +; PROHIBITED if // introduced by NOT or -; make sure not to set both. prohibited = (modifier == MOD_NOT); required = (modifier == MOD_REQ); if (conj == CONJ_AND && !prohibited) { required = true; } } else { // We set PROHIBITED if we're introduced by NOT or -; We set REQUIRED // if not PROHIBITED and not introduced by OR prohibited = (modifier == MOD_NOT); required = (!prohibited && conj != CONJ_OR); } if (required && !prohibited) clauses.addElement(new BooleanClause(q, BooleanClause.Occur.MUST)); else if (!required && !prohibited) clauses.addElement(new BooleanClause(q, BooleanClause.Occur.SHOULD)); else if (!required && prohibited) clauses.addElement(new BooleanClause(q, BooleanClause.Occur.MUST_NOT)); else throw new RuntimeException("Clause cannot be both required and prohibited"); } /** * @exception ParseException throw in overridden method to disallow */ protected Query getFieldQuery(String field, String queryText) throws ParseException { // Use the analyzer to get all the tokens, and then build a TermQuery, // PhraseQuery, or nothing based on the term count TokenStream source = analyzer.tokenStream(field, new StringReader(queryText)); Vector v = new Vector(); org.apache.lucene.analysis.Token t; int positionCount = 0; boolean severalTokensAtSamePosition = false; while (true) { try { t = source.next(); } catch (IOException e) { t = null; } if (t == null) break; v.addElement(t); if (t.getPositionIncrement() == 1) positionCount++; else severalTokensAtSamePosition = true; } try { source.close(); } catch (IOException e) { // ignore } if (v.size() == 0) return null; else if (v.size() == 1) { t = (org.apache.lucene.analysis.Token) v.elementAt(0); return new TermQuery(new Term(field, t.termText())); } else { if (severalTokensAtSamePosition) { if (positionCount == 1) { // no phrase query: BooleanQuery q = new BooleanQuery(); for (int i = 0; i < v.size(); i++) { t = (org.apache.lucene.analysis.Token) v.elementAt(i); TermQuery currentQuery = new TermQuery( new Term(field, t.termText())); q.add(currentQuery, BooleanClause.Occur.SHOULD); } return q; } else { // phrase query: MultiPhraseQuery mpq = new MultiPhraseQuery(); List multiTerms = new ArrayList(); for (int i = 0; i < v.size(); i++) { t = (org.apache.lucene.analysis.Token) v.elementAt(i); if (t.getPositionIncrement() == 1 && multiTerms.size() > 0) { mpq.add((Term[])multiTerms.toArray(new Term[0])); multiTerms.clear(); } multiTerms.add(new Term(field, t.termText())); } mpq.add((Term[])multiTerms.toArray(new Term[0])); return mpq; } } else { PhraseQuery q = new PhraseQuery(); q.setSlop(phraseSlop); for (int i = 0; i < v.size(); i++) { q.add(new Term(field, ((org.apache.lucene.analysis.Token) v.elementAt(i)).termText())); } return q; } } } /** * Base implementation delegates to {@link #getFieldQuery(String,String)}. * This method may be overridden, for example, to return * a SpanNearQuery instead of a PhraseQuery. * * @exception ParseException throw in overridden method to disallow */ protected Query getFieldQuery(String field, String queryText, int slop) throws ParseException { Query query = getFieldQuery(field, queryText); if (query instanceof PhraseQuery) { ((PhraseQuery) query).setSlop(slop); } if (query instanceof MultiPhraseQuery) { ((MultiPhraseQuery) query).setSlop(slop); } return query; } /** * @exception ParseException throw in overridden method to disallow */ protected Query getRangeQuery(String field, String part1, String part2, boolean inclusive) throws ParseException { if (lowercaseExpandedTerms) { part1 = part1.toLowerCase(); part2 = part2.toLowerCase(); } try { DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale); df.setLenient(true); Date d1 = df.parse(part1); Date d2 = df.parse(part2); part1 = DateTools.dateToString(d1, DateTools.Resolution.DAY); part2 = DateTools.dateToString(d2, DateTools.Resolution.DAY); } catch (Exception e) { } return new RangeQuery(new Term(field, part1), new Term(field, part2), inclusive); } /** * Factory method for generating query, given a set of clauses. * By default creates a boolean query composed of clauses passed in. * * Can be overridden by extending classes, to modify query being * returned. * * @param clauses Vector that contains {@link BooleanClause} instances * to join. * * @return Resulting {@link Query} object.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -