1 package org.apache.lucene.queryParser.spans;
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 import org.apache.lucene.queryParser.core.QueryNodeException;
21 import org.apache.lucene.queryParser.core.nodes.OrQueryNode;
22 import org.apache.lucene.queryParser.core.nodes.QueryNode;
23 import org.apache.lucene.queryParser.core.parser.SyntaxParser;
24 import org.apache.lucene.queryParser.core.processors.QueryNodeProcessorPipeline;
25 import org.apache.lucene.queryParser.standard.parser.StandardSyntaxParser;
26 import org.apache.lucene.queryParser.standard.processors.WildcardQueryNodeProcessor;
27 import org.apache.lucene.search.spans.SpanOrQuery;
28 import org.apache.lucene.search.spans.SpanQuery;
29 import org.apache.lucene.search.spans.SpanTermQuery;
30 import org.apache.lucene.search.Query;
31 import org.apache.lucene.util.LuceneTestCase;
34 * This test case demonstrates how the new query parser can be used.<br/>
37 * It tests queries likes "term", "field:term" "term1 term2" "term1 OR term2",
38 * which are all already supported by the current syntax parser (
39 * {@link StandardSyntaxParser}).<br/>
42 * The goals is to create a new query parser that supports only the pair
43 * "field:term" or a list of pairs separated or not by an OR operator, and from
44 * this query generate {@link SpanQuery} objects instead of the regular
45 * {@link Query} objects. Basically, every pair will be converted to a
46 * {@link SpanTermQuery} object and if there are more than one pair they will be
47 * grouped by an {@link OrQueryNode}.<br/>
50 * Another functionality that will be added is the ability to convert every
51 * field defined in the query to an unique specific field.<br/>
54 * The query generation is divided in three different steps: parsing (syntax),
55 * processing (semantic) and building.<br/>
58 * The parsing phase, as already mentioned will be performed by the current
59 * query parser: {@link StandardSyntaxParser}.<br/>
62 * The processing phase will be performed by a processor pipeline which is
63 * compound by 2 processors: {@link SpansValidatorQueryNodeProcessor} and
64 * {@link UniqueFieldQueryNodeProcessor}.
68 * {@link SpansValidatorQueryNodeProcessor}: as it's going to use the current
69 * query parser to parse the syntax, it will support more features than we want,
70 * this processor basically validates the query node tree generated by the parser
71 * and just let got through the elements we want, all the other elements as
72 * wildcards, range queries, etc...if found, an exception is thrown.
74 * {@link UniqueFieldQueryNodeProcessor}: this processor will take care of reading
75 * what is the "unique field" from the configuration and convert every field defined
76 * in every pair to this "unique field". For that, a {@link SpansQueryConfigHandler} is
77 * used, which has the {@link UniqueFieldAttribute} defined in it.
80 * The building phase is performed by the {@link SpansQueryTreeBuilder}, which
81 * basically contains a map that defines which builder will be used to generate
82 * {@link SpanQuery} objects from {@link QueryNode} objects.<br/>
85 * @see SpansQueryConfigHandler
86 * @see SpansQueryTreeBuilder
87 * @see SpansValidatorQueryNodeProcessor
88 * @see SpanOrQueryNodeBuilder
89 * @see SpanTermQueryNodeBuilder
90 * @see StandardSyntaxParser
91 * @see UniqueFieldQueryNodeProcessor
92 * @see UniqueFieldAttribute
94 public class TestSpanQueryParser extends LuceneTestCase {
96 private QueryNodeProcessorPipeline spanProcessorPipeline;
98 private SpansQueryConfigHandler spanQueryConfigHandler;
100 private SpansQueryTreeBuilder spansQueryTreeBuilder;
102 private SyntaxParser queryParser = new StandardSyntaxParser();
104 public TestSpanQueryParser() {
109 public void setUp() throws Exception {
112 this.spanProcessorPipeline = new QueryNodeProcessorPipeline();
113 this.spanQueryConfigHandler = new SpansQueryConfigHandler();
114 this.spansQueryTreeBuilder = new SpansQueryTreeBuilder();
116 // set up the processor pipeline
117 this.spanProcessorPipeline
118 .setQueryConfigHandler(this.spanQueryConfigHandler);
120 this.spanProcessorPipeline.add(new WildcardQueryNodeProcessor());
121 this.spanProcessorPipeline.add(new SpansValidatorQueryNodeProcessor());
122 this.spanProcessorPipeline.add(new UniqueFieldQueryNodeProcessor());
126 public SpanQuery getSpanQuery(CharSequence query) throws QueryNodeException {
127 return getSpanQuery("", query);
130 public SpanQuery getSpanQuery(String uniqueField, CharSequence query)
131 throws QueryNodeException {
133 this.spanQueryConfigHandler.set(SpansQueryConfigHandler.UNIQUE_FIELD, uniqueField);
135 QueryNode queryTree = this.queryParser.parse(query, "defaultField");
136 queryTree = this.spanProcessorPipeline.process(queryTree);
138 return this.spansQueryTreeBuilder.build(queryTree);
142 public void testTermSpans() throws Exception {
143 assertEquals(getSpanQuery("field:term").toString(), "term");
144 assertEquals(getSpanQuery("term").toString(), "term");
146 assertTrue(getSpanQuery("field:term") instanceof SpanTermQuery);
147 assertTrue(getSpanQuery("term") instanceof SpanTermQuery);
151 public void testUniqueField() throws Exception {
152 assertEquals(getSpanQuery("field", "term").toString(), "field:term");
153 assertEquals(getSpanQuery("field", "field:term").toString(), "field:term");
154 assertEquals(getSpanQuery("field", "anotherField:term").toString(),
159 public void testOrSpans() throws Exception {
160 assertEquals(getSpanQuery("term1 term2").toString(),
161 "spanOr([term1, term2])");
162 assertEquals(getSpanQuery("term1 OR term2").toString(),
163 "spanOr([term1, term2])");
165 assertTrue(getSpanQuery("term1 term2") instanceof SpanOrQuery);
166 assertTrue(getSpanQuery("term1 term2") instanceof SpanOrQuery);
170 public void testQueryValidator() throws QueryNodeException {
173 getSpanQuery("term*");
174 fail("QueryNodeException was expected, wildcard queries should not be supported");
176 } catch (QueryNodeException ex) {
177 // expected exception
181 getSpanQuery("[a TO z]");
182 fail("QueryNodeException was expected, range queries should not be supported");
184 } catch (QueryNodeException ex) {
185 // expected exception
189 getSpanQuery("a~0.5");
190 fail("QueryNodeException was expected, boost queries should not be supported");
192 } catch (QueryNodeException ex) {
193 // expected exception
197 getSpanQuery("a^0.5");
198 fail("QueryNodeException was expected, fuzzy queries should not be supported");
200 } catch (QueryNodeException ex) {
201 // expected exception
205 getSpanQuery("\"a b\"");
206 fail("QueryNodeException was expected, quoted queries should not be supported");
208 } catch (QueryNodeException ex) {
209 // expected exception
213 getSpanQuery("(a b)");
214 fail("QueryNodeException was expected, parenthesized queries should not be supported");
216 } catch (QueryNodeException ex) {
217 // expected exception
221 getSpanQuery("a AND b");
222 fail("QueryNodeException was expected, and queries should not be supported");
224 } catch (QueryNodeException ex) {
225 // expected exception