mirror of https://github.com/apache/lucene.git
LUCENE-2039: Added extensible query parser which enables arbitrary parser extensions based on field naming scheme
git-svn-id: https://svn.apache.org/repos/asf/lucene/java/trunk@887533 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
ab447b6af0
commit
5556599fad
|
@ -17,6 +17,11 @@ API Changes
|
|||
|
||||
New features
|
||||
|
||||
* LUCENE-2039: Add a extensible query parser to contrib/misc.
|
||||
ExtendableQueryParser enables arbitrary parser extensions based on a
|
||||
customizable field naming scheme.
|
||||
(Simon Willnauer)
|
||||
|
||||
* LUCENE-2108: Spellchecker now safely supports concurrent modifications to
|
||||
the spell-index. Threads can safely obtain term suggestions while the spell-
|
||||
index is rebuild, cleared or reset. Internal IndexSearcher instances remain
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
package org.apache.lucene.queryParser.ext;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.queryParser.ParseException;
|
||||
import org.apache.lucene.queryParser.QueryParser;
|
||||
import org.apache.lucene.queryParser.ext.Extensions.Pair;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.util.Version;
|
||||
|
||||
/**
|
||||
* The {@link ExtendableQueryParser} enables arbitrary query parser extension
|
||||
* based on a customizable field naming scheme. The lucene query syntax allows
|
||||
* implicit and explicit field definitions as query prefix followed by a colon
|
||||
* (':') character. The {@link ExtendableQueryParser} allows to encode extension
|
||||
* keys into the field symbol associated with a registered instance of
|
||||
* {@link ParserExtension}. A customizable separation character separates the
|
||||
* extension key from the actual field symbol. The {@link ExtendableQueryParser}
|
||||
* splits (@see {@link Extensions#splitExtensionField(String, String)}) the
|
||||
* extension key from the field symbol and tries to resolve the associated
|
||||
* {@link ParserExtension}. If the parser can't resolve the key or the field
|
||||
* token does not contain a separation character, {@link ExtendableQueryParser}
|
||||
* yields the same behavior as its super class {@link QueryParser}. Otherwise,
|
||||
* if the key is associated with a {@link ParserExtension} instance, the parser
|
||||
* builds an instance of {@link ExtensionQuery} to be processed by
|
||||
* {@link ParserExtension#parse(ExtensionQuery)}.If a extension field does not
|
||||
* contain a field part the default field for the query will be used.
|
||||
* <p>
|
||||
* To guarantee that an extension field is processed with its associated
|
||||
* extension, the extension query part must escape any special characters like
|
||||
* '*' or '['. If the extension query contains any whitespace characters, the
|
||||
* extension query part must be enclosed in quotes.
|
||||
* Example ('_' used as separation character):
|
||||
* <pre>
|
||||
* title_customExt:"Apache Lucene\?" OR content_customExt:prefix\*
|
||||
* </pre>
|
||||
*
|
||||
* Search on the default field:
|
||||
* <pre>
|
||||
* _customExt:"Apache Lucene\?" OR _customExt:prefix\*
|
||||
* </pre>
|
||||
* </p>
|
||||
* <p>
|
||||
* The {@link ExtendableQueryParser} itself does not implement the logic how
|
||||
* field and extension key are separated or ordered. All logic regarding the
|
||||
* extension key and field symbol parsing is located in {@link Extensions}.
|
||||
* Customized extension schemes should be implemented by sub-classing
|
||||
* {@link Extensions}.
|
||||
* </p>
|
||||
* <p>
|
||||
* For details about the default encoding scheme see {@link Extensions}.
|
||||
* </p>
|
||||
*
|
||||
* @see Extensions
|
||||
* @see ParserExtension
|
||||
* @see ExtensionQuery
|
||||
*/
|
||||
public class ExtendableQueryParser extends QueryParser {
|
||||
|
||||
private final String defaultField;
|
||||
private final Extensions extensions;
|
||||
|
||||
/**
|
||||
* Default empty extensions instance
|
||||
*/
|
||||
private static final Extensions DEFAULT_EXTENSION = new Extensions();
|
||||
|
||||
/**
|
||||
* Creates a new {@link ExtendableQueryParser} instance
|
||||
*
|
||||
* @param matchVersion
|
||||
* the lucene version to use.
|
||||
* @param f
|
||||
* the default query field
|
||||
* @param a
|
||||
* the analyzer used to find terms in a query string
|
||||
*/
|
||||
public ExtendableQueryParser(final Version matchVersion, final String f,
|
||||
final Analyzer a) {
|
||||
this(matchVersion, f, a, DEFAULT_EXTENSION);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ExtendableQueryParser} instance
|
||||
*
|
||||
* @param matchVersion
|
||||
* the lucene version to use.
|
||||
* @param f
|
||||
* the default query field
|
||||
* @param a
|
||||
* the analyzer used to find terms in a query string
|
||||
* @param ext
|
||||
* the query parser extensions
|
||||
*/
|
||||
public ExtendableQueryParser(final Version matchVersion, final String f,
|
||||
final Analyzer a, final Extensions ext) {
|
||||
super(matchVersion, f, a);
|
||||
this.defaultField = f;
|
||||
this.extensions = ext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the extension field delimiter character.
|
||||
*
|
||||
* @return the extension field delimiter character.
|
||||
*/
|
||||
public char getExtensionFieldDelimiter() {
|
||||
return extensions.getExtensionFieldDelimiter();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query getFieldQuery(final String field, final String queryText)
|
||||
throws ParseException {
|
||||
final Pair<String,String> splitExtensionField = this.extensions
|
||||
.splitExtensionField(defaultField, field);
|
||||
final ParserExtension extension = this.extensions
|
||||
.getExtension(splitExtensionField.cud);
|
||||
if (extension != null) {
|
||||
return extension.parse(new ExtensionQuery(splitExtensionField.cur,
|
||||
queryText));
|
||||
}
|
||||
return super.getFieldQuery(field, queryText);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package org.apache.lucene.queryParser.ext;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* {@link ExtensionQuery} holds all query components extracted from the original
|
||||
* query string like the query field and the extension query string.
|
||||
*
|
||||
* @see Extensions
|
||||
* @see ExtendableQueryParser
|
||||
* @see ParserExtension
|
||||
*/
|
||||
public class ExtensionQuery {
|
||||
|
||||
private final String field;
|
||||
private final String rawQueryString;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ExtensionQuery}
|
||||
*
|
||||
* @param field
|
||||
* the query field
|
||||
* @param rawQueryString
|
||||
* the raw extension query string
|
||||
*/
|
||||
public ExtensionQuery(String field, String rawQueryString) {
|
||||
this.field = field;
|
||||
this.rawQueryString = rawQueryString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the query field
|
||||
*
|
||||
* @return the query field
|
||||
*/
|
||||
public String getField() {
|
||||
return field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw extension query string
|
||||
*
|
||||
* @return the raw extension query string
|
||||
*/
|
||||
public String getRawQueryString() {
|
||||
return rawQueryString;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,217 @@
|
|||
package org.apache.lucene.queryParser.ext;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.lucene.queryParser.QueryParser;
|
||||
|
||||
/**
|
||||
* The {@link Extensions} class represents an extension mapping to associate
|
||||
* {@link ParserExtension} instances with extension keys. An extension key is a
|
||||
* string encoded into a Lucene standard query parser field symbol recognized by
|
||||
* {@link ExtendableQueryParser}. The query parser passes each extension field
|
||||
* token to {@link #splitExtensionField(String, String)} to separate the
|
||||
* extension key from the field identifier.
|
||||
* <p>
|
||||
* In addition to the key to extension mapping this class also defines the field
|
||||
* name overloading scheme. {@link ExtendableQueryParser} uses the given
|
||||
* extension to split the actual field name and extension key by calling
|
||||
* {@link #splitExtensionField(String, String)}. To change the order or the key
|
||||
* / field name encoding scheme users can subclass {@link Extensions} to
|
||||
* implement their own.
|
||||
*
|
||||
* @see ExtendableQueryParser
|
||||
* @see ParserExtension
|
||||
*/
|
||||
public class Extensions {
|
||||
private final Map<String,ParserExtension> extensions = new HashMap<String,ParserExtension>();
|
||||
private final char extensionFieldDelimiter;
|
||||
/**
|
||||
* The default extension field delimiter character. This constant is set to
|
||||
* ':'
|
||||
*/
|
||||
public static final char DEFAULT_EXTENSION_FIELD_DELIMITER = ':';
|
||||
|
||||
/**
|
||||
* Creates a new {@link Extensions} instance with the
|
||||
* {@link #DEFAULT_EXTENSION_FIELD_DELIMITER} as a delimiter character.
|
||||
*/
|
||||
public Extensions() {
|
||||
this(DEFAULT_EXTENSION_FIELD_DELIMITER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Extensions} instance
|
||||
*
|
||||
* @param extensionFieldDelimiter
|
||||
* the extensions field delimiter character
|
||||
*/
|
||||
public Extensions(char extensionFieldDelimiter) {
|
||||
this.extensionFieldDelimiter = extensionFieldDelimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new {@link ParserExtension} instance associated with the given key.
|
||||
*
|
||||
* @param key
|
||||
* the parser extension key
|
||||
* @param extension
|
||||
* the parser extension
|
||||
*/
|
||||
public void add(String key, ParserExtension extension) {
|
||||
this.extensions.put(key, extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ParserExtension} instance for the given key or
|
||||
* <code>null</code> if no extension can be found for the key.
|
||||
*
|
||||
* @param key
|
||||
* the extension key
|
||||
* @return the {@link ParserExtension} instance for the given key or
|
||||
* <code>null</code> if no extension can be found for the key.
|
||||
*/
|
||||
public final ParserExtension getExtension(String key) {
|
||||
return this.extensions.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the extension field delimiter
|
||||
*
|
||||
* @return the extension field delimiter
|
||||
*/
|
||||
public char getExtensionFieldDelimiter() {
|
||||
return extensionFieldDelimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a extension field and returns the field / extension part as a
|
||||
* {@link Pair}. This method tries to split on the first occurrence of the
|
||||
* extension field delimiter, if the delimiter is not present in the string
|
||||
* the result will contain a <code>null</code> value for the extension key and
|
||||
* the given field string as the field value. If the given extension field
|
||||
* string contains no field identifier the result pair will carry the given
|
||||
* default field as the field value.
|
||||
*
|
||||
* @param defaultField
|
||||
* the default query field
|
||||
* @param field
|
||||
* the extension field string
|
||||
* @return a {@link Pair} with the field name as the {@link Pair#cur} and the
|
||||
* extension key as the {@link Pair#cud}
|
||||
*/
|
||||
public Pair<String,String> splitExtensionField(String defaultField,
|
||||
String field) {
|
||||
int indexOf = field.indexOf(this.extensionFieldDelimiter);
|
||||
if (indexOf < 0)
|
||||
return new Pair<String,String>(field, null);
|
||||
final String indexField = indexOf == 0 ? defaultField : field.substring(0,
|
||||
indexOf);
|
||||
final String extensionKey = field.substring(indexOf + 1);
|
||||
return new Pair<String,String>(indexField, extensionKey);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes an extension field. The default implementation is equivalent to
|
||||
* {@link QueryParser#escape(String)}.
|
||||
*
|
||||
* @param extfield
|
||||
* the extension field identifier
|
||||
* @return the extension field identifier with all special chars escaped with
|
||||
* a backslash character.
|
||||
*/
|
||||
public String escapeExtensionField(String extfield) {
|
||||
return QueryParser.escape(extfield);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an extension field string from a given extension key and the default
|
||||
* query field. The default field and the key are delimited with the extension
|
||||
* field delimiter character. This method makes no assumption about the order
|
||||
* of the extension key and the field. By default the extension key is
|
||||
* appended to the end of the returned string while the field is added to the
|
||||
* beginning. Special Query characters are escaped in the result.
|
||||
* <p>
|
||||
* Note: {@link Extensions} subclasses must maintain the contract between
|
||||
* {@link #buildExtensionField(String)} and
|
||||
* {@link #splitExtensionField(String, String)} where the latter inverts the
|
||||
* former.
|
||||
* </p>
|
||||
*/
|
||||
public String buildExtensionField(String extensionKey) {
|
||||
return buildExtensionField(extensionKey, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an extension field string from a given extension key and the
|
||||
* extensions field. The field and the key are delimited with the extension
|
||||
* field delimiter character. This method makes no assumption about the order
|
||||
* of the extension key and the field. By default the extension key is
|
||||
* appended to the end of the returned string while the field is added to the
|
||||
* beginning. Special Query characters are escaped in the result.
|
||||
* <p>
|
||||
* Note: {@link Extensions} subclasses must maintain the contract between
|
||||
* {@link #buildExtensionField(String, String)} and
|
||||
* {@link #splitExtensionField(String, String)} where the latter inverts the
|
||||
* former.
|
||||
* </p>
|
||||
*
|
||||
* @param extensionKey
|
||||
* the extension key
|
||||
* @param field
|
||||
* the field to apply the extension on.
|
||||
* @return escaped extension field identifier
|
||||
* @see #buildExtensionField(String) to use the default query field
|
||||
*/
|
||||
public String buildExtensionField(String extensionKey, String field) {
|
||||
StringBuilder builder = new StringBuilder(field);
|
||||
builder.append(this.extensionFieldDelimiter);
|
||||
builder.append(extensionKey);
|
||||
return escapeExtensionField(builder.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* This class represents a generic pair.
|
||||
*
|
||||
* @param <Cur>
|
||||
* the pairs first element
|
||||
* @param <Cud>
|
||||
* the pairs last element of the pair.
|
||||
*/
|
||||
public static class Pair<Cur,Cud> {
|
||||
|
||||
public final Cur cur;
|
||||
public final Cud cud;
|
||||
|
||||
/**
|
||||
* Creates a new Pair
|
||||
*
|
||||
* @param cur
|
||||
* the pairs first element
|
||||
* @param cud
|
||||
* the pairs last element
|
||||
*/
|
||||
public Pair(Cur cur, Cud cud) {
|
||||
this.cur = cur;
|
||||
this.cud = cud;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package org.apache.lucene.queryParser.ext;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import org.apache.lucene.queryParser.ParseException;
|
||||
import org.apache.lucene.queryParser.QueryParser;
|
||||
import org.apache.lucene.search.Query;
|
||||
|
||||
/**
|
||||
* This class represents an extension base class to the Lucene standard
|
||||
* {@link QueryParser}. The {@link QueryParser} is generated by the JavaCC
|
||||
* parser generator. Changing or adding functionality or syntax in the standard
|
||||
* query parser requires changes to the JavaCC source file. To enable extending
|
||||
* the standard query parser without changing the JavaCC sources and re-generate
|
||||
* the parser the {@link ParserExtension} can be customized and plugged into an
|
||||
* instance of {@link ExtendableQueryParser}, a direct subclass of
|
||||
* {@link QueryParser}.
|
||||
*
|
||||
* @see Extensions
|
||||
* @see ExtendableQueryParser
|
||||
*/
|
||||
public abstract class ParserExtension {
|
||||
|
||||
/**
|
||||
* Processes the given {@link ExtensionQuery} and returns a corresponding
|
||||
* {@link Query} instance. Subclasses must either return a {@link Query}
|
||||
* instance or raise a {@link ParseException}. This method must not return
|
||||
* <code>null</code>.
|
||||
*
|
||||
* @param query
|
||||
* the extension query
|
||||
* @return a new query instance
|
||||
* @throws ParseException
|
||||
* if the query can not be parsed.
|
||||
*/
|
||||
public abstract Query parse(final ExtensionQuery query) throws ParseException;
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<html><head></head>
|
||||
<body>
|
||||
Extendable QueryParser provides a simple and flexible extension mechanism by overloading query field names.
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,33 @@
|
|||
package org.apache.lucene.queryParser.ext;
|
||||
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.queryParser.ParseException;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.TermQuery;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
class ExtensionStub extends ParserExtension {
|
||||
|
||||
@Override
|
||||
public Query parse(ExtensionQuery components) throws ParseException {
|
||||
return new TermQuery(new Term(components.getField(), components
|
||||
.getRawQueryString()));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
package org.apache.lucene.queryParser.ext;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.analysis.SimpleAnalyzer;
|
||||
import org.apache.lucene.queryParser.ParseException;
|
||||
import org.apache.lucene.queryParser.QueryParser;
|
||||
import org.apache.lucene.queryParser.TestQueryParser;
|
||||
import org.apache.lucene.search.BooleanClause;
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.TermQuery;
|
||||
import org.apache.lucene.util.Version;
|
||||
|
||||
/**
|
||||
* Testcase for the class {@link ExtendableQueryParser}
|
||||
*/
|
||||
public class TestExtendableQueryParser extends TestQueryParser {
|
||||
private static char[] DELIMITERS = new char[] {
|
||||
Extensions.DEFAULT_EXTENSION_FIELD_DELIMITER, '-', '|' };
|
||||
|
||||
public TestExtendableQueryParser(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryParser getParser(Analyzer a) throws Exception {
|
||||
return getParser(a, null);
|
||||
}
|
||||
|
||||
public QueryParser getParser(Analyzer a, Extensions extensions)
|
||||
throws Exception {
|
||||
if (a == null)
|
||||
a = new SimpleAnalyzer();
|
||||
QueryParser qp = extensions == null ? new ExtendableQueryParser(
|
||||
Version.LUCENE_CURRENT, "field", a) : new ExtendableQueryParser(
|
||||
Version.LUCENE_CURRENT, "field", a, extensions);
|
||||
qp.setDefaultOperator(QueryParser.OR_OPERATOR);
|
||||
return qp;
|
||||
}
|
||||
|
||||
public void testUnescapedExtDelimiter() throws Exception {
|
||||
Extensions ext = newExtensions(':');
|
||||
ext.add("testExt", new ExtensionStub());
|
||||
ExtendableQueryParser parser = (ExtendableQueryParser) getParser(null, ext);
|
||||
try {
|
||||
parser.parse("aField:testExt:\"foo \\& bar\"");
|
||||
fail("extension field delimiter is not escaped");
|
||||
} catch (ParseException e) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testExtFieldUnqoted() throws Exception {
|
||||
for (int i = 0; i < DELIMITERS.length; i++) {
|
||||
Extensions ext = newExtensions(DELIMITERS[i]);
|
||||
ext.add("testExt", new ExtensionStub());
|
||||
ExtendableQueryParser parser = (ExtendableQueryParser) getParser(null,
|
||||
ext);
|
||||
String field = ext.buildExtensionField("testExt", "aField");
|
||||
Query query = parser.parse(String.format("%s:foo bar", field));
|
||||
assertTrue("expected instance of BooleanQuery but was "
|
||||
+ query.getClass(), query instanceof BooleanQuery);
|
||||
BooleanQuery bquery = (BooleanQuery) query;
|
||||
BooleanClause[] clauses = bquery.getClauses();
|
||||
assertEquals(2, clauses.length);
|
||||
BooleanClause booleanClause = clauses[0];
|
||||
query = booleanClause.getQuery();
|
||||
assertTrue("expected instance of TermQuery but was " + query.getClass(),
|
||||
query instanceof TermQuery);
|
||||
TermQuery tquery = (TermQuery) query;
|
||||
assertEquals("aField", tquery.getTerm()
|
||||
.field());
|
||||
assertEquals("foo", tquery.getTerm().text());
|
||||
|
||||
booleanClause = clauses[1];
|
||||
query = booleanClause.getQuery();
|
||||
assertTrue("expected instance of TermQuery but was " + query.getClass(),
|
||||
query instanceof TermQuery);
|
||||
tquery = (TermQuery) query;
|
||||
assertEquals("field", tquery.getTerm().field());
|
||||
assertEquals("bar", tquery.getTerm().text());
|
||||
}
|
||||
}
|
||||
|
||||
public void testExtDefaultField() throws Exception {
|
||||
for (int i = 0; i < DELIMITERS.length; i++) {
|
||||
Extensions ext = newExtensions(DELIMITERS[i]);
|
||||
ext.add("testExt", new ExtensionStub());
|
||||
ExtendableQueryParser parser = (ExtendableQueryParser) getParser(null,
|
||||
ext);
|
||||
String field = ext.buildExtensionField("testExt");
|
||||
Query parse = parser.parse(String.format("%s:\"foo \\& bar\"", field));
|
||||
assertTrue("expected instance of TermQuery but was " + parse.getClass(),
|
||||
parse instanceof TermQuery);
|
||||
TermQuery tquery = (TermQuery) parse;
|
||||
assertEquals("field", tquery.getTerm().field());
|
||||
assertEquals("foo & bar", tquery.getTerm().text());
|
||||
}
|
||||
}
|
||||
|
||||
public Extensions newExtensions(char delimiter) {
|
||||
return new Extensions(delimiter);
|
||||
}
|
||||
|
||||
public void testExtField() throws Exception {
|
||||
for (int i = 0; i < DELIMITERS.length; i++) {
|
||||
Extensions ext = newExtensions(DELIMITERS[i]);
|
||||
ext.add("testExt", new ExtensionStub());
|
||||
ExtendableQueryParser parser = (ExtendableQueryParser) getParser(null,
|
||||
ext);
|
||||
String field = ext.buildExtensionField("testExt", "afield");
|
||||
Query parse = parser.parse(String.format("%s:\"foo \\& bar\"", field));
|
||||
assertTrue("expected instance of TermQuery but was " + parse.getClass(),
|
||||
parse instanceof TermQuery);
|
||||
TermQuery tquery = (TermQuery) parse;
|
||||
assertEquals("afield", tquery.getTerm().field());
|
||||
assertEquals("foo & bar", tquery.getTerm().text());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package org.apache.lucene.queryParser.ext;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
|
||||
/**
|
||||
* Testcase for the {@link Extensions} class
|
||||
*/
|
||||
public class TestExtensions extends LuceneTestCase {
|
||||
|
||||
private Extensions ext;
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
this.ext = new Extensions();
|
||||
}
|
||||
|
||||
public void testBuildExtensionField() {
|
||||
assertEquals("field\\:key", ext.buildExtensionField("key", "field"));
|
||||
assertEquals("\\:key", ext.buildExtensionField("key"));
|
||||
|
||||
ext = new Extensions('.');
|
||||
assertEquals("field.key", ext.buildExtensionField("key", "field"));
|
||||
assertEquals(".key", ext.buildExtensionField("key"));
|
||||
}
|
||||
|
||||
public void testSplitExtensionField() {
|
||||
assertEquals("field\\:key", ext.buildExtensionField("key", "field"));
|
||||
assertEquals("\\:key", ext.buildExtensionField("key"));
|
||||
|
||||
ext = new Extensions('.');
|
||||
assertEquals("field.key", ext.buildExtensionField("key", "field"));
|
||||
assertEquals(".key", ext.buildExtensionField("key"));
|
||||
}
|
||||
|
||||
public void testAddGetExtension() {
|
||||
ParserExtension extension = new ExtensionStub();
|
||||
assertNull(ext.getExtension("foo"));
|
||||
ext.add("foo", extension);
|
||||
assertSame(extension, ext.getExtension("foo"));
|
||||
ext.add("foo", null);
|
||||
assertNull(ext.getExtension("foo"));
|
||||
}
|
||||
|
||||
public void testGetExtDelimiter() {
|
||||
assertEquals(Extensions.DEFAULT_EXTENSION_FIELD_DELIMITER, this.ext
|
||||
.getExtensionFieldDelimiter());
|
||||
ext = new Extensions('?');
|
||||
assertEquals('?', this.ext.getExtensionFieldDelimiter());
|
||||
}
|
||||
|
||||
public void testEscapeExtension() {
|
||||
assertEquals("abc\\:\\?\\{\\}\\[\\]\\\\\\(\\)\\+\\-\\!\\~", ext
|
||||
.escapeExtensionField("abc:?{}[]\\()+-!~"));
|
||||
try {
|
||||
ext.escapeExtensionField(null);
|
||||
fail("should throw NPE - escape string is null");
|
||||
} catch (NullPointerException e) {
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue