diff --git a/gradle/documentation/render-javadoc.gradle b/gradle/documentation/render-javadoc.gradle index d579840ef46..4501e8184f8 100644 --- a/gradle/documentation/render-javadoc.gradle +++ b/gradle/documentation/render-javadoc.gradle @@ -481,8 +481,11 @@ class RenderJavadocTask extends DefaultTask { // append some special table css, prettify css ant.concat(destfile: "${outputDir}/stylesheet.css", append: "true", fixlastline: "true", encoding: "UTF-8") { - filelist(dir: taskResources, files: "table_padding.css") - filelist(dir: project.file("${taskResources}/prettify"), files: "prettify.css") + filelist(dir: taskResources, files: + ["table_padding.css", + "custom_styles.css", + "prettify/prettify.css"].join(" ") + ) } // append prettify to scripts diff --git a/gradle/documentation/render-javadoc/custom_styles.css b/gradle/documentation/render-javadoc/custom_styles.css new file mode 100644 index 00000000000..6e5af801b60 --- /dev/null +++ b/gradle/documentation/render-javadoc/custom_styles.css @@ -0,0 +1,41 @@ +/* + * 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. + */ + +/* + * Used by interval function documentation. + */ + +.example.sentence { + text-align: center; + padding: 1rem 0 2rem; + font-style: italic; +} +.example.sentence.with-highlights { + line-height: 150%; +} +.example.sentence.with-positions sub { + color: red; + font-weight: bold; +} +.example.sentence.left-aligned { + text-align: left; +} +span.highlight { + padding-bottom: 2px; + border-bottom: 2px solid red; +} + diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt index d08374d01af..931f7962741 100644 --- a/lucene/CHANGES.txt +++ b/lucene/CHANGES.txt @@ -38,6 +38,10 @@ API Changes New Features --------------------- +* LUCENE-10223: Add interval function support to StandardQueryParser. Add min-should-match operator + support to StandardQueryParser. Update and clean up package documentation in flexible query parser + module. (Dawid Weiss, Alan Woodward) + * LUCENE-10220: Add an utility method to get IntervalSource from analyzed text (or token stream). (Uwe Schindler, Dawid Weiss, Alan Woodward) diff --git a/lucene/MIGRATE.md b/lucene/MIGRATE.md index 6a7087adc33..eb5436d5333 100644 --- a/lucene/MIGRATE.md +++ b/lucene/MIGRATE.md @@ -17,6 +17,13 @@ # Apache Lucene Migration Guide +## Minor syntactical changes in StandardQueryParser (Lucene 9.1) + +LUCENE-10223 adds interval functions and min-should-match support to StandardQueryParser. This +means that interval function prefixes ("fn:") and the '@' character after parentheses will +parse differently than before. If you need the exact previous behavior, clone the StandardSyntaxParser from the previous version of Lucene and create a custom query parser +with that parser. + ## Directory API is now little endian (LUCENE-9047) DataOutput's writeShort, writeInt, and writeLong methods now encode with diff --git a/lucene/highlighter/src/test/org/apache/lucene/search/matchhighlight/IndexBuilder.java b/lucene/highlighter/src/test/org/apache/lucene/search/matchhighlight/IndexBuilder.java index c409c750ccb..55d63cf17f2 100644 --- a/lucene/highlighter/src/test/org/apache/lucene/search/matchhighlight/IndexBuilder.java +++ b/lucene/highlighter/src/test/org/apache/lucene/search/matchhighlight/IndexBuilder.java @@ -17,7 +17,6 @@ package org.apache.lucene.search.matchhighlight; import com.carrotsearch.randomizedtesting.RandomizedTest; -import java.io.IOException; import java.util.ArrayList; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -34,7 +33,7 @@ import org.apache.lucene.search.Sort; import org.apache.lucene.search.SortField; import org.apache.lucene.store.ByteBuffersDirectory; import org.apache.lucene.store.Directory; -import org.apache.lucene.util.IOUtils; +import org.apache.lucene.util.LuceneTestCase; /** * Utility class for building an ephemeral document index and running a block of code on its reader. @@ -82,8 +81,8 @@ class IndexBuilder { return this; } - public IndexBuilder build(Analyzer analyzer, IOUtils.IOConsumer block) - throws IOException { + public IndexBuilder build( + Analyzer analyzer, LuceneTestCase.ThrowingConsumer block) throws Exception { IndexWriterConfig config = new IndexWriterConfig(analyzer); config.setIndexSort(new Sort(new SortField(FLD_SORT_ORDER, SortField.Type.LONG))); try (Directory directory = new ByteBuffersDirectory()) { diff --git a/lucene/highlighter/src/test/org/apache/lucene/search/matchhighlight/TestMatchHighlighter.java b/lucene/highlighter/src/test/org/apache/lucene/search/matchhighlight/TestMatchHighlighter.java index a441b903f54..fa6eb1dc504 100644 --- a/lucene/highlighter/src/test/org/apache/lucene/search/matchhighlight/TestMatchHighlighter.java +++ b/lucene/highlighter/src/test/org/apache/lucene/search/matchhighlight/TestMatchHighlighter.java @@ -49,6 +49,7 @@ import org.apache.lucene.index.IndexableField; import org.apache.lucene.index.Term; import org.apache.lucene.queries.intervals.IntervalQuery; import org.apache.lucene.queries.intervals.Intervals; +import org.apache.lucene.queryparser.flexible.standard.StandardQueryParser; import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.IndexSearcher; @@ -127,7 +128,7 @@ public class TestMatchHighlighter extends LuceneTestCase { } @Test - public void testBasicUsage() throws IOException { + public void testBasicUsage() throws Exception { new IndexBuilder(this::toField) .doc(FLD_TEXT1, "foo bar baz") .doc(FLD_TEXT1, "bar foo baz") @@ -237,7 +238,7 @@ public class TestMatchHighlighter extends LuceneTestCase { } @Test - public void testSynonymHighlight() throws IOException { + public void testSynonymHighlight() throws Exception { // There is nothing special needed to highlight or process complex queries, synonyms, etc. // Synonyms defined in the constructor of this class. new IndexBuilder(this::toField) @@ -268,7 +269,7 @@ public class TestMatchHighlighter extends LuceneTestCase { } @Test - public void testAnalyzedTextIntervals() throws IOException { + public void testAnalyzedTextIntervals() throws Exception { SynonymMap synonymMap = buildSynonymMap( new String[][] { @@ -319,7 +320,229 @@ public class TestMatchHighlighter extends LuceneTestCase { } @Test - public void testCustomFieldHighlightHandling() throws IOException { + public void testStandardQueryParserIntervalFunctions() throws Exception { + Analyzer analyzer = + new Analyzer() { + @Override + protected TokenStreamComponents createComponents(String fieldName) { + Tokenizer tokenizer = new StandardTokenizer(); + TokenStream ts = tokenizer; + ts = new LowerCaseFilter(ts); + return new TokenStreamComponents(tokenizer, ts); + } + }; + + // TODO: the highlights are different when the field is indexed with offsets. Weird. + // String field = FLD_TEXT1; + String field = FLD_TEXT2; + new IndexBuilder(this::toField) + // Just one document and multiple interval queries. + .doc(field, "The quick brown fox jumps over the lazy dog") + .build( + analyzer, + reader -> { + IndexSearcher searcher = new IndexSearcher(reader); + Sort sortOrder = Sort.INDEXORDER; // So that results are consistently ordered. + + MatchHighlighter highlighter = + new MatchHighlighter(searcher, analyzer) + .appendFieldHighlighter( + FieldValueHighlighters.highlighted( + 80 * 3, 1, new PassageFormatter("...", ">", "<"), fld -> true)) + .appendFieldHighlighter(FieldValueHighlighters.skipRemaining()); + + StandardQueryParser qp = new StandardQueryParser(analyzer); + + // Run all pairs of query-expected highlight. + List errors = new ArrayList<>(); + for (var queryHighlightPair : + new String[][] { + { + "fn:ordered(brown dog)", + "0. %s: The quick >brown fox jumps over the lazy dog<" + }, + { + "fn:within(fn:or(lazy quick) 1 fn:or(dog fox))", + "0. %s: The quick brown fox jumps over the >lazy< dog" + }, + { + "fn:containedBy(fox fn:ordered(brown fox dog))", + "0. %s: The quick brown >fox< jumps over the lazy dog" + }, + { + "fn:atLeast(2 fn:unordered(furry dog) fn:unordered(brown dog) lazy quick)", + "0. %s: The >quick >brown fox jumps over the lazy<<> dog<" + }, + { + "fn:atLeast(2 quick fox \"furry dog\")", + "0. %s: The >quick brown fox< jumps over the lazy dog" + }, + { + "fn:maxgaps(0 fn:ordered(fn:or(quick lazy) fn:or(fox dog)))", + "0. %s: The quick brown fox jumps over the >lazy dog<" + }, + { + "fn:maxgaps(1 fn:ordered(fn:or(quick lazy) fn:or(fox dog)))", + "0. %s: The >quick brown fox< jumps over the >lazy dog<" + }, + { + "fn:maxwidth(2 fn:ordered(fn:or(quick lazy) fn:or(fox dog)))", + "0. %s: The quick brown fox jumps over the >lazy dog<" + }, + { + "fn:maxwidth(3 fn:ordered(fn:or(quick lazy) fn:or(fox dog)))", + "0. %s: The >quick brown fox< jumps over the >lazy dog<" + }, + { + "fn:or(quick \"fox\")", + "0. %s: The >quick< brown >fox< jumps over the lazy dog" + }, + {"fn:or(\"quick fox\")"}, + { + "fn:phrase(quick brown fox)", + "0. %s: The >quick brown fox< jumps over the lazy dog" + }, + {"fn:wildcard(jump*)", "0. %s: The quick brown fox >jumps< over the lazy dog"}, + {"fn:wildcard(br*n)", "0. %s: The quick >brown< fox jumps over the lazy dog"}, + {"fn:or(dog fox)", "0. %s: The quick brown >fox< jumps over the lazy >dog<"}, + { + "fn:phrase(fn:ordered(quick fox) jumps)", + "0. %s: The >quick brown fox jumps< over the lazy dog" + }, + { + "fn:ordered(quick jumps dog)", + "0. %s: The >quick brown fox jumps over the lazy dog<" + }, + { + "fn:ordered(quick fn:or(fox dog))", + "0. %s: The >quick brown fox< jumps over the lazy dog" + }, + { + "fn:ordered(quick jumps fn:or(fox dog))", + "0. %s: The >quick brown fox jumps over the lazy dog<" + }, + { + "fn:unordered(dog jumps quick)", + "0. %s: The >quick brown fox jumps over the lazy dog<" + }, + { + "fn:unordered(fn:or(fox dog) quick)", + "0. %s: The >quick brown fox< jumps over the lazy dog" + }, + { + "fn:unordered(fn:phrase(brown fox) fn:phrase(fox jumps))", + "0. %s: The quick >brown fox jumps< over the lazy dog" + }, + {"fn:ordered(fn:phrase(brown fox) fn:phrase(fox jumps))"}, + {"fn:unorderedNoOverlaps(fn:phrase(brown fox) fn:phrase(fox jumps))"}, + { + "fn:before(fn:or(brown lazy) fox)", + "0. %s: The quick >brown< fox jumps over the lazy dog" + }, + { + "fn:before(fn:or(brown lazy) fn:or(dog fox))", + "0. %s: The quick >brown< fox jumps over the >lazy< dog" + }, + { + "fn:after(fn:or(brown lazy) fox)", + "0. %s: The quick brown fox jumps over the >lazy< dog" + }, + { + "fn:after(fn:or(brown lazy) fn:or(dog fox))", + "0. %s: The quick brown fox jumps over the >lazy< dog" + }, + {"fn:extend(fox 1 2)", "0. %s: The quick >brown fox jumps over< the lazy dog"}, + { + "fn:extend(fn:or(dog fox) 2 0)", + "0. %s: The >quick brown fox< jumps over >the lazy dog<" + }, + { + "fn:within(fn:or(fox dog) 1 fn:or(quick lazy))", + "0. %s: The quick brown fox jumps over the lazy >dog<" + }, + { + "fn:within(fn:or(fox dog) 2 fn:or(quick lazy))", + "0. %s: The quick brown >fox< jumps over the lazy >dog<" + }, + { + "fn:notWithin(fn:or(fox dog) 1 fn:or(quick lazy))", + "0. %s: The quick brown >fox< jumps over the lazy dog" + }, + { + "fn:containedBy(fn:or(fox dog) fn:extend(lazy 3 3))", + "0. %s: The quick brown fox jumps over the lazy >dog<" + }, + { + "fn:containedBy(fn:or(fox dog) fn:ordered(quick lazy))", + "0. %s: The quick brown >fox< jumps over the lazy dog" + }, + { + "fn:notContainedBy(fn:or(fox dog) fn:extend(lazy 3 3))", + "0. %s: The quick brown >fox< jumps over the lazy dog" + }, + { + "fn:notContainedBy(fn:or(fox dog) fn:ordered(quick lazy))", + "0. %s: The quick brown fox jumps over the lazy >dog<" + }, + { + "fn:containing(fn:extend(fn:or(lazy brown) 1 1) fn:or(fox dog))", + "0. %s: The >quick brown fox< jumps over >the lazy dog<" + }, + { + "fn:containing(fn:atLeast(2 quick fox dog) jumps)", + "0. %s: The quick brown >fox jumps over the lazy dog<" + }, + { + "fn:notContaining(fn:ordered(fn:or(the The) fn:or(fox dog)) brown)", + "0. %s: The quick brown fox jumps over >the lazy dog<" + }, + { + "fn:notContaining(fn:extend(fn:or(fox dog) 1 0) fn:or(brown yellow))", + "0. %s: The quick brown fox jumps over the >lazy dog<" + }, + { + "fn:overlapping(fn:phrase(brown fox) fn:phrase(fox jumps))", + "0. %s: The quick >brown fox< jumps over the lazy dog" + }, + { + "fn:overlapping(fn:or(fox dog) fn:extend(lazy 2 2))", + "0. %s: The quick brown fox jumps over the lazy >dog<" + }, + { + "fn:nonOverlapping(fn:phrase(brown fox) fn:phrase(lazy dog))", + "0. %s: The quick >brown fox< jumps over the lazy dog" + }, + { + "fn:nonOverlapping(fn:or(fox dog) fn:extend(lazy 2 2))", + "0. %s: The quick brown >fox< jumps over the lazy dog" + }, + }) { + assert queryHighlightPair.length >= 1; + String queryString = queryHighlightPair[0]; + var query = qp.parse(queryString, field); + var expected = + Arrays.stream(queryHighlightPair) + .skip(1) + .map(v -> String.format(Locale.ROOT, v, field)) + .toArray(String[]::new); + + try { + assertHighlights( + toDocList( + highlighter.highlight(searcher.search(query, 10, sortOrder), query)), + expected); + } catch (AssertionError e) { + errors.add("MISMATCH: query: " + queryString + "\n" + e.getMessage()); + } + } + if (errors.size() > 0) { + throw new AssertionError(String.join("\n\n", errors)); + } + }); + } + + @Test + public void testCustomFieldHighlightHandling() throws Exception { // Match highlighter is a showcase of individual components in this package, suitable // to create any kind of field-display designs. // @@ -427,7 +650,7 @@ public class TestMatchHighlighter extends LuceneTestCase { } @Test - public void testHighlightMoreQueriesAtOnceShowoff() throws IOException { + public void testHighlightMoreQueriesAtOnceShowoff() throws Exception { // Match highlighter underlying components are powerful enough to build interesting, // if not always super-practical, things. In this case, we would like to highlight // a set of matches of *more than one* query over the same set of input documents. This includes @@ -566,14 +789,15 @@ public class TestMatchHighlighter extends LuceneTestCase { } } - if (!Arrays.equals( - Stream.of(expectedFormattedLines).map(String::trim).toArray(), - actualLines.stream().map(String::trim).toArray())) { + var expectedTrimmed = + Stream.of(expectedFormattedLines).map(String::trim).collect(Collectors.toList()); + var actualTrimmed = actualLines.stream().map(String::trim).collect(Collectors.toList()); + if (!Objects.equals(expectedTrimmed, actualTrimmed)) { throw new AssertionError( "Actual hits were:\n" - + String.join("\n", actualLines) - + "\n\n but expected them to be:\n" - + String.join("\n", expectedFormattedLines)); + + String.join("\n", actualTrimmed) + + "\n\nbut expected them to be:\n" + + String.join("\n", expectedTrimmed)); } } diff --git a/lucene/highlighter/src/test/org/apache/lucene/search/matchhighlight/TestMatchRegionRetriever.java b/lucene/highlighter/src/test/org/apache/lucene/search/matchhighlight/TestMatchRegionRetriever.java index b3dbb3962d8..4d8d4f29d53 100644 --- a/lucene/highlighter/src/test/org/apache/lucene/search/matchhighlight/TestMatchRegionRetriever.java +++ b/lucene/highlighter/src/test/org/apache/lucene/search/matchhighlight/TestMatchRegionRetriever.java @@ -64,6 +64,7 @@ import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.TopDocs; import org.apache.lucene.util.LuceneTestCase; +import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; @@ -158,16 +159,16 @@ public class TestMatchRegionRetriever extends LuceneTestCase { }; @Test - public void testTermQueryWithOffsets() throws IOException { + public void testTermQueryWithOffsets() throws Exception { checkTermQuery(FLD_TEXT_POS_OFFS); } @Test - public void testTermQueryWithPositions() throws IOException { + public void testTermQueryWithPositions() throws Exception { checkTermQuery(FLD_TEXT_POS); } - private void checkTermQuery(String field) throws IOException { + private void checkTermQuery(String field) throws Exception { new IndexBuilder(this::toField) .doc(field, "foo bar baz") .doc(field, "bar foo baz") @@ -176,7 +177,7 @@ public class TestMatchRegionRetriever extends LuceneTestCase { .build( analyzer, reader -> { - assertThat( + MatcherAssert.assertThat( highlights(reader, new TermQuery(new Term(field, "foo"))), containsInAnyOrder( fmt("0: (%s: '>foo< bar baz')", field), @@ -186,16 +187,16 @@ public class TestMatchRegionRetriever extends LuceneTestCase { } @Test - public void testBooleanMultifieldQueryWithOffsets() throws IOException { + public void testBooleanMultifieldQueryWithOffsets() throws Exception { checkBooleanMultifieldQuery(FLD_TEXT_POS_OFFS); } @Test - public void testBooleanMultifieldQueryWithPositions() throws IOException { + public void testBooleanMultifieldQueryWithPositions() throws Exception { checkBooleanMultifieldQuery(FLD_TEXT_POS); } - private void checkBooleanMultifieldQuery(String field) throws IOException { + private void checkBooleanMultifieldQuery(String field) throws Exception { Query query = new BooleanQuery.Builder() .add(new PhraseQuery(1, field, "foo", "baz"), BooleanClause.Occur.SHOULD) @@ -210,7 +211,7 @@ public class TestMatchRegionRetriever extends LuceneTestCase { .build( analyzer, reader -> { - assertThat( + MatcherAssert.assertThat( highlights(reader, query), containsInAnyOrder( fmt("0: (%s: '>foo bar baz< abc')", field), @@ -219,16 +220,16 @@ public class TestMatchRegionRetriever extends LuceneTestCase { } @Test - public void testVariousQueryTypesWithOffsets() throws IOException { + public void testVariousQueryTypesWithOffsets() throws Exception { checkVariousQueryTypes(FLD_TEXT_POS_OFFS); } @Test - public void testVariousQueryTypesWithPositions() throws IOException { + public void testVariousQueryTypesWithPositions() throws Exception { checkVariousQueryTypes(FLD_TEXT_POS); } - private void checkVariousQueryTypes(String field) throws IOException { + private void checkVariousQueryTypes(String field) throws Exception { new IndexBuilder(this::toField) .doc(field, "foo bar baz abc") .doc(field, "bar foo baz def") @@ -236,46 +237,46 @@ public class TestMatchRegionRetriever extends LuceneTestCase { .build( analyzer, reader -> { - assertThat( + MatcherAssert.assertThat( highlights(reader, stdQueryParser.apply("foo baz", field)), containsInAnyOrder( fmt("0: (%s: '>foo< bar >baz< abc')", field), fmt("1: (%s: 'bar >foo< >baz< def')", field), fmt("2: (%s: 'bar >baz< >foo< xyz')", field))); - assertThat( + MatcherAssert.assertThat( highlights(reader, stdQueryParser.apply("foo OR xyz", field)), containsInAnyOrder( fmt("0: (%s: '>foo< bar baz abc')", field), fmt("1: (%s: 'bar >foo< baz def')", field), fmt("2: (%s: 'bar baz >foo< >xyz<')", field))); - assertThat( + MatcherAssert.assertThat( highlights(reader, stdQueryParser.apply("bas~2", field)), containsInAnyOrder( fmt("0: (%s: 'foo >bar< >baz< >abc<')", field), fmt("1: (%s: '>bar< foo >baz< def')", field), fmt("2: (%s: '>bar< >baz< foo xyz')", field))); - assertThat( + MatcherAssert.assertThat( highlights(reader, stdQueryParser.apply("\"foo bar\"", field)), containsInAnyOrder((fmt("0: (%s: '>foo bar< baz abc')", field)))); - assertThat( + MatcherAssert.assertThat( highlights(reader, stdQueryParser.apply("\"foo bar\"~3", field)), containsInAnyOrder( fmt("0: (%s: '>foo bar< baz abc')", field), fmt("1: (%s: '>bar foo< baz def')", field), fmt("2: (%s: '>bar baz foo< xyz')", field))); - assertThat( + MatcherAssert.assertThat( highlights(reader, stdQueryParser.apply("ba*", field)), containsInAnyOrder( fmt("0: (%s: 'foo >bar< >baz< abc')", field), fmt("1: (%s: '>bar< foo >baz< def')", field), fmt("2: (%s: '>bar< >baz< foo xyz')", field))); - assertThat( + MatcherAssert.assertThat( highlights(reader, stdQueryParser.apply("[bar TO bas]", field)), containsInAnyOrder( fmt("0: (%s: 'foo >bar< baz abc')", field), @@ -284,14 +285,15 @@ public class TestMatchRegionRetriever extends LuceneTestCase { // Note how document '2' has 'bar' that isn't highlighted (because this // document is excluded in the first clause). - assertThat( + MatcherAssert.assertThat( highlights(reader, stdQueryParser.apply("([bar TO baz] -xyz) OR baz", field)), containsInAnyOrder( fmt("0: (%s: 'foo >bar< >>baz<< abc')", field), fmt("1: (%s: '>bar< foo >>baz<< def')", field), fmt("2: (%s: 'bar >baz< foo xyz')", field))); - assertThat(highlights(reader, new MatchAllDocsQuery()), Matchers.hasSize(0)); + MatcherAssert.assertThat( + highlights(reader, new MatchAllDocsQuery()), Matchers.hasSize(0)); }); new IndexBuilder(this::toField) @@ -301,7 +303,7 @@ public class TestMatchRegionRetriever extends LuceneTestCase { .build( analyzer, reader -> { - assertThat( + MatcherAssert.assertThat( highlights(reader, stdQueryParser.apply("[bar TO baz] -bar", field)), containsInAnyOrder( fmt("0: (%s: 'foo >baz< foo')", field), @@ -310,7 +312,7 @@ public class TestMatchRegionRetriever extends LuceneTestCase { } @Test - public void testIntervalQueryHighlightCrossingMultivalueBoundary() throws IOException { + public void testIntervalQueryHighlightCrossingMultivalueBoundary() throws Exception { String field = FLD_TEXT_POS; new IndexBuilder(this::toField) .doc(field, "foo", "bar") @@ -328,7 +330,7 @@ public class TestMatchRegionRetriever extends LuceneTestCase { } @Test - public void testIntervalQueries() throws IOException { + public void testIntervalQueries() throws Exception { String field = FLD_TEXT_POS_OFFS; new IndexBuilder(this::toField) @@ -338,7 +340,7 @@ public class TestMatchRegionRetriever extends LuceneTestCase { .build( analyzer, reader -> { - assertThat( + MatcherAssert.assertThat( highlights( reader, new IntervalQuery( @@ -349,7 +351,7 @@ public class TestMatchRegionRetriever extends LuceneTestCase { Intervals.term("baz")))), containsInAnyOrder(fmt("1: (field_text_offs: '>bas baz foo<')", field))); - assertThat( + MatcherAssert.assertThat( highlights( reader, new IntervalQuery( @@ -359,7 +361,7 @@ public class TestMatchRegionRetriever extends LuceneTestCase { Intervals.unordered(Intervals.term("foo"), Intervals.term("bar"))))), containsInAnyOrder(fmt("2: (field_text_offs: '>bar baz foo< xyz')", field))); - assertThat( + MatcherAssert.assertThat( highlights( reader, new IntervalQuery( @@ -369,7 +371,7 @@ public class TestMatchRegionRetriever extends LuceneTestCase { Intervals.term("foo")))), containsInAnyOrder(fmt("2: (field_text_offs: '>bar baz foo< xyz')", field))); - assertThat( + MatcherAssert.assertThat( highlights( reader, new IntervalQuery( @@ -379,7 +381,7 @@ public class TestMatchRegionRetriever extends LuceneTestCase { Intervals.unordered(Intervals.term("foo"), Intervals.term("bar"))))), containsInAnyOrder(fmt("2: (field_text_offs: '>bar baz foo< xyz')", field))); - assertThat( + MatcherAssert.assertThat( highlights( reader, new IntervalQuery( @@ -392,7 +394,7 @@ public class TestMatchRegionRetriever extends LuceneTestCase { } @Test - public void testDegenerateIntervalsWithPositions() throws IOException { + public void testDegenerateIntervalsWithPositions() throws Exception { testDegenerateIntervals(FLD_TEXT_POS); } @@ -401,23 +403,23 @@ public class TestMatchRegionRetriever extends LuceneTestCase { bugUrl = "https://issues.apache.org/jira/browse/LUCENE-9634: " + "Highlighting of degenerate spans on fields with offsets doesn't work properly") - public void testDegenerateIntervalsWithOffsets() throws IOException { + public void testDegenerateIntervalsWithOffsets() throws Exception { testDegenerateIntervals(FLD_TEXT_POS_OFFS); } - public void testDegenerateIntervals(String field) throws IOException { + public void testDegenerateIntervals(String field) throws Exception { new IndexBuilder(this::toField) .doc(field, fmt("foo %s bar", STOPWORD1)) .build( analyzer, reader -> { - assertThat( + MatcherAssert.assertThat( highlights( reader, new IntervalQuery(field, Intervals.extend(Intervals.term("bar"), 1, 3))), containsInAnyOrder(fmt("0: (%s: 'foo %s >bar<')", field, STOPWORD1))); - assertThat( + MatcherAssert.assertThat( highlights( reader, new IntervalQuery(field, Intervals.extend(Intervals.term("bar"), 5, 100))), @@ -426,16 +428,16 @@ public class TestMatchRegionRetriever extends LuceneTestCase { } @Test - public void testMultivaluedFieldsWithOffsets() throws IOException { + public void testMultivaluedFieldsWithOffsets() throws Exception { checkMultivaluedFields(FLD_TEXT_POS_OFFS); } @Test - public void testMultivaluedFieldsWithPositions() throws IOException { + public void testMultivaluedFieldsWithPositions() throws Exception { checkMultivaluedFields(FLD_TEXT_POS); } - public void checkMultivaluedFields(String field) throws IOException { + public void checkMultivaluedFields(String field) throws Exception { new IndexBuilder(this::toField) .doc(field, "foo bar", "baz abc", "bad baz") .doc(field, "bar foo", "baz def") @@ -443,7 +445,7 @@ public class TestMatchRegionRetriever extends LuceneTestCase { .build( analyzer, reader -> { - assertThat( + MatcherAssert.assertThat( highlights(reader, stdQueryParser.apply("baz", field)), containsInAnyOrder( fmt("0: (%s: '>baz< abc | bad >baz<')", field), @@ -453,7 +455,7 @@ public class TestMatchRegionRetriever extends LuceneTestCase { } @Test - public void testMultiFieldHighlights() throws IOException { + public void testMultiFieldHighlights() throws Exception { for (String[] fieldPairs : new String[][] { {FLD_TEXT_POS_OFFS1, FLD_TEXT_POS_OFFS2}, @@ -477,7 +479,7 @@ public class TestMatchRegionRetriever extends LuceneTestCase { .sorted() .collect(Collectors.joining("")); - assertThat( + MatcherAssert.assertThat( highlights( reader, stdQueryParser.apply(field1 + ":baz" + " OR " + field2 + ":bar", field1)), @@ -491,7 +493,7 @@ public class TestMatchRegionRetriever extends LuceneTestCase { * org.apache.lucene.search.BooleanClause.Occur#SHOULD} clauses. Check that this isn't the case. */ @Test - public void testNoRewrite() throws IOException { + public void testNoRewrite() throws Exception { String field1 = FLD_TEXT_POS_OFFS1; String field2 = FLD_TEXT_POS_OFFS2; @@ -510,13 +512,13 @@ public class TestMatchRegionRetriever extends LuceneTestCase { analyzer, reader -> { String expected = fmt("0: (%s: '>0100<')(%s: 'loo >bar<')", field1, field2); - assertThat( + MatcherAssert.assertThat( highlights( reader, stdQueryParser.apply(fmt("+%s:01* OR %s:bar", field1, field2), field1)), containsInAnyOrder(expected)); - assertThat( + MatcherAssert.assertThat( highlights( reader, stdQueryParser.apply(fmt("+%s:01* AND %s:bar", field1, field2), field1)), @@ -525,22 +527,22 @@ public class TestMatchRegionRetriever extends LuceneTestCase { } @Test - public void testNestedQueryHitsWithOffsets() throws IOException { + public void testNestedQueryHitsWithOffsets() throws Exception { checkNestedQueryHits(FLD_TEXT_POS_OFFS); } @Test - public void testNestedQueryHitsWithPositions() throws IOException { + public void testNestedQueryHitsWithPositions() throws Exception { checkNestedQueryHits(FLD_TEXT_POS); } - public void checkNestedQueryHits(String field) throws IOException { + public void checkNestedQueryHits(String field) throws Exception { new IndexBuilder(this::toField) .doc(field, "foo bar baz abc") .build( analyzer, reader -> { - assertThat( + MatcherAssert.assertThat( highlights( reader, new BooleanQuery.Builder() @@ -549,7 +551,7 @@ public class TestMatchRegionRetriever extends LuceneTestCase { .build()), containsInAnyOrder(fmt("0: (%s: '>foo >bar< baz< abc')", field))); - assertThat( + MatcherAssert.assertThat( highlights( reader, new BooleanQuery.Builder() @@ -571,7 +573,7 @@ public class TestMatchRegionRetriever extends LuceneTestCase { checkGraphQuery(FLD_TEXT_SYNONYMS_POS); } - private void checkGraphQuery(String field) throws IOException { + private void checkGraphQuery(String field) throws Exception { new IndexBuilder(this::toField) .doc(field, "foo bar baz") .doc(field, "bar foo baz") @@ -580,25 +582,25 @@ public class TestMatchRegionRetriever extends LuceneTestCase { .build( analyzer, reader -> { - assertThat( + MatcherAssert.assertThat( highlights(reader, new TermQuery(new Term(field, "syn1"))), containsInAnyOrder(fmt("0: (%s: '>foo bar< baz')", field))); // [syn2 syn3] = baz // so both these queries highlight baz. - assertThat( + MatcherAssert.assertThat( highlights(reader, new TermQuery(new Term(field, "syn3"))), containsInAnyOrder( fmt("0: (%s: 'foo bar >baz<')", field), fmt("1: (%s: 'bar foo >baz<')", field), fmt("2: (%s: 'bar >baz< foo')", field))); - assertThat( + MatcherAssert.assertThat( highlights(reader, stdQueryParser.apply(field + ":\"syn2 syn3\"", field)), containsInAnyOrder( fmt("0: (%s: 'foo bar >baz<')", field), fmt("1: (%s: 'bar foo >baz<')", field), fmt("2: (%s: 'bar >baz< foo')", field))); - assertThat( + MatcherAssert.assertThat( highlights(reader, stdQueryParser.apply(field + ":\"foo syn2 syn3\"", field)), containsInAnyOrder(fmt("1: (%s: 'bar >foo baz<')", field))); }); @@ -614,7 +616,7 @@ public class TestMatchRegionRetriever extends LuceneTestCase { checkSpanQueries(FLD_TEXT_POS); } - private void checkSpanQueries(String field) throws IOException { + private void checkSpanQueries(String field) throws Exception { new IndexBuilder(this::toField) .doc(field, "foo bar baz") .doc(field, "bar foo baz") @@ -623,7 +625,7 @@ public class TestMatchRegionRetriever extends LuceneTestCase { .build( analyzer, reader -> { - assertThat( + MatcherAssert.assertThat( highlights( reader, SpanNearQuery.newOrderedNearQuery(field) @@ -632,7 +634,7 @@ public class TestMatchRegionRetriever extends LuceneTestCase { .build()), containsInAnyOrder(fmt("1: (%s: '>bar foo< baz')", field))); - assertThat( + MatcherAssert.assertThat( highlights( reader, SpanNearQuery.newOrderedNearQuery(field) @@ -642,7 +644,7 @@ public class TestMatchRegionRetriever extends LuceneTestCase { .build()), containsInAnyOrder(fmt("2: (%s: '>bar baz foo<')", field))); - assertThat( + MatcherAssert.assertThat( highlights( reader, SpanNearQuery.newUnorderedNearQuery(field) @@ -653,7 +655,7 @@ public class TestMatchRegionRetriever extends LuceneTestCase { fmt("0: (%s: '>foo bar< baz')", field), fmt("1: (%s: '>bar foo< baz')", field))); - assertThat( + MatcherAssert.assertThat( highlights( reader, SpanNearQuery.newUnorderedNearQuery(field) @@ -694,7 +696,7 @@ public class TestMatchRegionRetriever extends LuceneTestCase { } }; - assertThat( + MatcherAssert.assertThat( highlights(customSuppliers, reader, new TermQuery(new Term(field, "bar"))), containsInAnyOrder( fmt("0: (%s: '>foo bar<')", field), @@ -721,7 +723,7 @@ public class TestMatchRegionRetriever extends LuceneTestCase { .build( analyzer, reader -> { - assertThat( + MatcherAssert.assertThat( highlights(reader, new TermQuery(new Term(field, "bar"))), containsInAnyOrder( fmt("0: (%s: 'foo >bar<')", field), diff --git a/lucene/queries/src/java/org/apache/lucene/queries/intervals/Intervals.java b/lucene/queries/src/java/org/apache/lucene/queries/intervals/Intervals.java index b79d48fd2a4..51dfe48ad8b 100644 --- a/lucene/queries/src/java/org/apache/lucene/queries/intervals/Intervals.java +++ b/lucene/queries/src/java/org/apache/lucene/queries/intervals/Intervals.java @@ -31,20 +31,20 @@ import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.automaton.CompiledAutomaton; /** - * Constructor functions for {@link IntervalsSource} types + * Factory functions for creating {@link IntervalsSource interval sources}. * *

These sources implement minimum-interval algorithms taken from the paper Efficient Optimally Lazy Algorithms * for Minimal-Interval Semantics * - *

By default, sources that are sensitive to internal gaps (e.g. {@code PHRASE} and {@code - * MAXGAPS}) will rewrite their sub-sources so that disjunctions of different lengths are pulled up - * to the top of the interval tree. For example, {@code PHRASE(or(PHRASE("a", "b", "c"), "b"), "c")} - * will automatically rewrite itself to {@code OR(PHRASE("a", "b", "c", "c"), PHRASE("b", "c"))} to - * ensure that documents containing {@code "b c"} are matched. This can lead to less efficient - * queries, as more terms need to be loaded (for example, the {@code "c"} iterator above is loaded - * twice), so if you care more about speed than about accuracy you can use the {@link #or(boolean, - * IntervalsSource...)} factory method to prevent rewriting. + *

Note: by default, sources that are sensitive to internal gaps (e.g. {@code PHRASE} + * and {@code MAXGAPS}) will rewrite their sub-sources so that disjunctions of different lengths are + * pulled up to the top of the interval tree. For example, {@code PHRASE(or(PHRASE("a", "b", "c"), + * "b"), "c")} will automatically rewrite itself to {@code OR(PHRASE("a", "b", "c", "c"), + * PHRASE("b", "c"))} to ensure that documents containing {@code "b c"} are matched. This can lead + * to less efficient queries, as more terms need to be loaded (for example, the {@code "c"} iterator + * above is loaded twice), so if you care more about speed than about accuracy you can use the + * {@link #or(boolean, IntervalsSource...)} factory method to prevent rewriting. */ public final class Intervals { @@ -94,7 +94,7 @@ public final class Intervals { /** * Return an {@link IntervalsSource} exposing intervals for a phrase consisting of a list of - * IntervalsSources + * {@link IntervalsSource interval sources} */ public static IntervalsSource phrase(IntervalsSource... subSources) { return BlockIntervalsSource.build(Arrays.asList(subSources)); diff --git a/lucene/queries/src/java/org/apache/lucene/queries/intervals/package-info.java b/lucene/queries/src/java/org/apache/lucene/queries/intervals/package-info.java index 26e91d674d0..cbedc07f383 100644 --- a/lucene/queries/src/java/org/apache/lucene/queries/intervals/package-info.java +++ b/lucene/queries/src/java/org/apache/lucene/queries/intervals/package-info.java @@ -16,60 +16,72 @@ */ /** + * Intervals queries * - * - *

Intervals queries

- * - * This package contains experimental classes to search over intervals within fields + *

This package contains experimental classes to search over intervals within fields * *

IntervalsSource

* * The {@link org.apache.lucene.queries.intervals.IntervalsSource} class can be used to construct * proximity relationships between terms and intervals. They can be built using static methods in - * the {@link org.apache.lucene.queries.intervals.Intervals} class + * the {@link org.apache.lucene.queries.intervals.Intervals} class. * *

Basic intervals

* *
    *
  • {@link org.apache.lucene.queries.intervals.Intervals#term(String)} — Represents a - * single term + * single term. *
  • {@link org.apache.lucene.queries.intervals.Intervals#phrase(java.lang.String...)} — - * Represents a phrase + * Represents a phrase. + *
  • {@link org.apache.lucene.queries.intervals.Intervals#analyzedText(java.lang.String, + * org.apache.lucene.analysis.Analyzer, java.lang.String, int, boolean)} — Represents a + * phrase (or an unordered sequence) of tokens resulting from an analysis of a given text. *
  • {@link org.apache.lucene.queries.intervals.Intervals#ordered(IntervalsSource...)} — - * Represents an interval over an ordered set of terms or intervals + * Represents an interval over an ordered set of terms or intervals. *
  • {@link org.apache.lucene.queries.intervals.Intervals#unordered(IntervalsSource...)} — - * Represents an interval over an unordered set of terms or intervals + * Represents an interval over an unordered set of terms or intervals. *
  • {@link org.apache.lucene.queries.intervals.Intervals#or(IntervalsSource...)} — - * Represents the disjunction of a set of terms or intervals + * Represents the disjunction of a set of terms or intervals. + *
  • {@link + * org.apache.lucene.queries.intervals.Intervals#wildcard(org.apache.lucene.util.BytesRef)} + * — Represents an suffix wildcard (any prefix-matching term from the index). *
* *

Filters

* *
    *
  • {@link org.apache.lucene.queries.intervals.Intervals#maxwidth(int, IntervalsSource)} - * — Filters out intervals that are larger than a set width + * — Filters out intervals that are larger than a set width. *
  • {@link org.apache.lucene.queries.intervals.Intervals#maxgaps(int, IntervalsSource)} — * Filters out intervals that have more than a set number of gaps between their constituent - * sub-intervals + * sub-intervals. *
  • {@link org.apache.lucene.queries.intervals.Intervals#containedBy(IntervalsSource, - * IntervalsSource)} — Returns intervals that are contained by another interval + * IntervalsSource)} — Returns intervals that are contained by another interval. *
  • {@link org.apache.lucene.queries.intervals.Intervals#notContainedBy(IntervalsSource, - * IntervalsSource)} — Returns intervals that are *not* contained by another interval + * IntervalsSource)} — Returns intervals that are *not* contained by another interval. *
  • {@link org.apache.lucene.queries.intervals.Intervals#containing(IntervalsSource, - * IntervalsSource)} — Returns intervals that contain another interval + * IntervalsSource)} — Returns intervals that contain another interval. *
  • {@link org.apache.lucene.queries.intervals.Intervals#notContaining(IntervalsSource, - * IntervalsSource)} — Returns intervals that do not contain another interval + * IntervalsSource)} — Returns intervals that do not contain another interval. *
  • {@link org.apache.lucene.queries.intervals.Intervals#nonOverlapping(IntervalsSource, - * IntervalsSource)} — Returns intervals that do not overlap with another interval + * IntervalsSource)} — Returns intervals that do not overlap with another interval. *
  • {@link org.apache.lucene.queries.intervals.Intervals#notWithin(IntervalsSource, int, * IntervalsSource)} — Returns intervals that do not appear within a set number of - * positions of another interval + * positions of another interval. *
* + * The {@link org.apache.lucene.queries.intervals.Intervals} class contains more advanced filters, + * please refer to the documentation of that class. + * *

IntervalQuery

* * An {@link org.apache.lucene.queries.intervals.IntervalQuery} takes a field name and an {@link * org.apache.lucene.queries.intervals.IntervalsSource}, and matches all documents that contain * intervals defined by the source in that field. + * + *

Interval query support in query parsers

+ * + *

Lucene's {@code StandardQueryParser} (from the {@code queryparser} module) supports interval + * function expressions. */ package org.apache.lucene.queries.intervals; diff --git a/lucene/queryparser/docs/flexible/README.javacc b/lucene/queryparser/docs/flexible/README.javacc deleted file mode 100644 index bae12245f31..00000000000 --- a/lucene/queryparser/docs/flexible/README.javacc +++ /dev/null @@ -1,62 +0,0 @@ -NOTE: often, if you are making a small change to the .jj file, you can -simply run "ant javacc" and skip the steps below. JavaCC will print -warnings like this: - - Warning: ParseException.java: File is obsolete. Please rename or delete this file so that a new one can be generated for you. - -which you should ignore (ie, simply keep the ParseException.java class -that's already present). - -If, instead, you'd like to fully rebuild the StandardQueryParser, -here's how: - - * Delete these files: - - StandardQueryParser.java - StandardQueryParserConstants.java - StandardQueryParserTokenManager.java - TokenMgrError.java - JavaCharStream.java - Token.java - - * Run "ant javacc". That will generate the all the classes - - * To avoid lots of warnings in the generated code: - - add @SupressWarnings("all"), immediately preceding the class declaration to: - - QueryParserTokenManager.java - TokenMgrError.java - JavaCharStream.java - Token.java - JavaCharStream.java - - * Remove all imports from TokenMgrError.java - - * Fix the ParseException class: - - - Change it to extend from QueryNodeParseException: - - "public class ParseException extends QueryNodeParseException". - - - Recreate the all the constructors like this: - - public ParseException(Token currentTokenVal, - int[][] expectedTokenSequencesVal, String[] tokenImageVal) { - super(new MessageImpl(QueryParserMessages.INVALID_SYNTAX, initialise( - currentTokenVal, expectedTokenSequencesVal, tokenImageVal))); - this.currentToken = currentTokenVal; - this.expectedTokenSequences = expectedTokenSequencesVal; - this.tokenImage = tokenImageVal; - } - - public ParseException(Message message) { - super(message); - } - - public ParseException() { - super(new MessageImpl(QueryParserMessages.INVALID_SYNTAX, "Error")); - } - - - - Fix all imports diff --git a/lucene/queryparser/src/generated/checksums/javaccParserFlexible.json b/lucene/queryparser/src/generated/checksums/javaccParserFlexible.json index 7b35647be77..3533be4eca3 100644 --- a/lucene/queryparser/src/generated/checksums/javaccParserFlexible.json +++ b/lucene/queryparser/src/generated/checksums/javaccParserFlexible.json @@ -1,9 +1,9 @@ { "lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/ParseException.java": "3d5f272a6d56b3f4962b252267ce2662e734414e", - "lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.java": "75e9d84f424bb697f899fe3adacc0094bac00672", - "lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.jj": "08b62ed73607b1646af5dadb81c8bb34e381daee", - "lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParserConstants.java": "e73933bff38a62d90dab64f72a1a0deadfff246f", - "lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParserTokenManager.java": "6e503b48ffa9f4648798e5394f7baeec366d1f07", + "lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.java": "fd1fcc78bf1025fe6fe54ab6f9ae2f53cce33364", + "lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.jj": "eb0d1c55d029982ab8ea433cf9ef1088ba6ea3de", + "lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParserConstants.java": "d3c5d87c46635dbb6dc03bbdc0fb662b47ec318d", + "lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParserTokenManager.java": "d8e12b467779c1740ea2b672c10806ac25e0184e", "lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/Token.java": "f4cb9d01587279dba30e549ce4867e4381bbd9d7", "lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/TokenMgrError.java": "cdfa99af5fcf6b1e50691a1c1370ba60bf0d2d2d" } \ No newline at end of file diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/core/config/QueryConfigHandler.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/core/config/QueryConfigHandler.java index 89b17e15b80..c194d73286c 100644 --- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/core/config/QueryConfigHandler.java +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/core/config/QueryConfigHandler.java @@ -25,7 +25,7 @@ import org.apache.lucene.queryparser.flexible.core.util.StringUtils; * configuration, it creates an empty {@link FieldConfig} object and delegate it to field config * listeners, these are responsible for setting up all the field configuration. * - *

{@link QueryConfigHandler} should be extended by classes that intends to provide configuration + *

{@link QueryConfigHandler} should be extended by classes that intend to provide configuration * to {@link QueryNodeProcessor} objects. * *

The class that extends {@link QueryConfigHandler} should also provide {@link FieldConfig} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/core/messages/QueryParserMessages.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/core/messages/QueryParserMessages.java index e48d7b65bc3..30c616afc08 100644 --- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/core/messages/QueryParserMessages.java +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/core/messages/QueryParserMessages.java @@ -20,7 +20,6 @@ import org.apache.lucene.queryparser.flexible.messages.NLS; /** Flexible Query Parser message bundle class */ public class QueryParserMessages extends NLS { - private static final String BUNDLE_NAME = QueryParserMessages.class.getName(); private QueryParserMessages() { @@ -52,4 +51,5 @@ public class QueryParserMessages extends NLS { public static String NUMBER_CLASS_NOT_SUPPORTED_BY_NUMERIC_RANGE_QUERY; public static String UNSUPPORTED_NUMERIC_DATA_TYPE; public static String NUMERIC_CANNOT_BE_EMPTY; + public static String ANALYZER_REQUIRED; } diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/package-info.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/package-info.java new file mode 100644 index 00000000000..4d20996e41b --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/package-info.java @@ -0,0 +1,97 @@ +/* + * 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. + */ + +/** + * Flexible query parser is a modular, extensible framework for implementing Lucene query parsers. + * In the flexible query parser model, query parsing takes three steps: syntax parsing, processing + * (query semantics) and building (conversion to a Lucene {@link org.apache.lucene.search.Query}). + * + *

The flexible query parser module provides not just the framework but also the {@linkplain + * org.apache.lucene.queryparser.flexible.standard.StandardQueryParser} - the default implementation + * of a fully fledged query parser that supports most of the classic query parser's syntax but also + * adds support for interval functions, min-should-match operator on Boolean groups and many hooks + * for customization of how the parser behaves at runtime. + * + *

The flexible query parser is divided in two packages: + * + *

    + *
  • {@link org.apache.lucene.queryparser.flexible.core}: contains the query parser API classes, + * which should be extended by custom query parser implementations. + *
  • {@link org.apache.lucene.queryparser.flexible.standard}: contains an example Lucene query + * parser implementation built on top of the flexible query parser API. + *
+ * + *

Features

+ * + *
    + *
  1. full support for Boolean expressions, including groups + *
  2. {@linkplain org.apache.lucene.queryparser.flexible.core.parser.SyntaxParser syntax parsers} + * - support for arbitrary syntax parsers, that can be converted into {@link + * org.apache.lucene.queryparser.flexible.core.nodes.QueryNode} trees. + *
  3. {@linkplain org.apache.lucene.queryparser.flexible.core.processors.QueryNodeProcessor query + * node processors} - optimize, validate, rewrite the {@link + * org.apache.lucene.queryparser.flexible.core.nodes.QueryNode} trees + *
  4. {@linkplain + * org.apache.lucene.queryparser.flexible.core.processors.QueryNodeProcessorPipeline processor + * pipelines} - select your favorite query processors and build a pipeline to implement the + * features you need. + *
  5. {@linkplain org.apache.lucene.queryparser.flexible.core.config.QueryConfigHandler query + * configuration handlers} + *
  6. {@linkplain org.apache.lucene.queryparser.flexible.core.builders.QueryBuilder query + * builders} - convert {@link org.apache.lucene.queryparser.flexible.core.nodes.QueryNode} + * trees into Lucene {@link org.apache.lucene.search.Query} instances. + *
+ * + *

Design

+ * + *

The flexible query parser was designed to have a very generic architecture, so that it can be + * easily used for different products with varying query syntax needs. + * + *

The query parser has three layers and its core is what we call the {@linkplain + * org.apache.lucene.queryparser.flexible.core.nodes.QueryNode query node tree}. It is a tree of + * objects that represent the syntax of the original query, for example, for 'a AND b' the tree + * could look like this: + * + *

+ *       AND
+ *      /   \
+ *     A     B
+ * 
+ * + *

The three flexible query parser layers are: + * + *

+ *
{@link org.apache.lucene.queryparser.flexible.core.parser.SyntaxParser} + *
This layer is the text parsing layer which simply transforms the query text string into a + * {@link org.apache.lucene.queryparser.flexible.core.nodes.QueryNode} tree. Every text parser + * must implement the interface {@link + * org.apache.lucene.queryparser.flexible.core.parser.SyntaxParser}. The default + * implementation is {@link + * org.apache.lucene.queryparser.flexible.standard.parser.StandardSyntaxParser}. + *
{@link org.apache.lucene.queryparser.flexible.core.processors.QueryNodeProcessor} + *
The query node processor does most of the work: it contains a chain of {@linkplain + * org.apache.lucene.queryparser.flexible.core.processors.QueryNodeProcessor query node + * processors}. Each processor can walk the tree and modify nodes or even the tree's + * structure. This allows for query optimization before the node tree is converted to an + * actual query. + *
{@link org.apache.lucene.queryparser.flexible.core.builders.QueryBuilder} + *
The third layer is a configurable map of builders, which map {@linkplain + * org.apache.lucene.queryparser.flexible.core.nodes.QueryNode query nodes} to their adapters + * that convert each node into a {@link org.apache.lucene.search.Query}. + *
+ */ +package org.apache.lucene.queryparser.flexible; diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/StandardQueryParser.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/StandardQueryParser.java index c84ea55f2b0..e0881b30120 100644 --- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/StandardQueryParser.java +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/StandardQueryParser.java @@ -39,57 +39,189 @@ import org.apache.lucene.search.MultiTermQuery; import org.apache.lucene.search.Query; /** - * This class is a helper that enables users to easily use the Lucene query parser. + * The {@link StandardQueryParser} is a pre-assembled query parser that supports most features of + * the {@linkplain org.apache.lucene.queryparser.classic.QueryParser classic Lucene query parser}, + * allows dynamic configuration of some of its features (like multi-field expansion or wildcard + * query restrictions) and adds support for new query types and expressions. * - *

To construct a Query object from a query string, use the {@link #parse(String, String)} - * method: + *

The {@link StandardSyntaxParser} is an extension of the {@link QueryParserHelper} with + * reasonable defaults for syntax tree parsing ({@link StandardSyntaxParser}, node processor + * pipeline ({@link StandardQueryNodeProcessorPipeline} and node tree to {@link Query} builder + * ({@link StandardQueryTreeBuilder}). * - *

- * StandardQueryParser queryParserHelper = new StandardQueryParser();
- * Query query = queryParserHelper.parse("a AND b", "defaultField");
- * 
+ *

Typical usage, including configuration tweaks: * - *

To change any configuration before parsing the query string do, for example:
+ *

{@code
+ * StandardQueryParser qpHelper = new StandardQueryParser();
+ * StandardQueryConfigHandler config =  qpHelper.getQueryConfigHandler();
+ * config.setAllowLeadingWildcard(true);
+ * config.setAnalyzer(new WhitespaceAnalyzer());
+ * Query query = qpHelper.parse("apache AND lucene", "defaultField");
+ * }
* - *
- * // the query config handler returned by {@link StandardQueryParser} is a {@link StandardQueryConfigHandler}
- * queryParserHelper.getQueryConfigHandler().setAnalyzer(new WhitespaceAnalyzer());
- * 
+ *

Supported query syntax

* - *

The syntax for query strings is as follows (copied from the old QueryParser javadoc): A Query - * is a series of clauses. A clause may be prefixed by: + *

Standard query parser borrows most of its syntax from the {@linkplain + * org.apache.lucene.queryparser.classic classic query parser} but adds more features and + * expressions on top of that syntax. + * + *

A query consists of clauses, field specifications, grouping and Boolean operators and + * interval functions. We will discuss them in order. + * + *

Basic clauses

+ * + *

A query must contain one or more clauses. A clause can be a literal term, a phrase, a wildcard + * expression or other expression that + * + *

The following are some examples of simple one-clause queries: * *

    - *
  • a plus (+) or a minus (-) sign, indicating that the clause is - * required or prohibited respectively; or - *
  • a term followed by a colon, indicating the field to be searched. This enables one to - * construct queries which search multiple fields. + *
  • test + *

    selects documents containing the word test (term clause). + *

  • "test equipment" + *

    phrase search; selects documents containing the phrase test equipment (phrase + * clause). + *

  • "test failure"~4 + *

    proximity search; selects documents containing the words test and + * failure within 4 words (positions) from each other. The provided "proximity" is + * technically translated into "edit distance" (maximum number of atomic word-moving + * operations required to transform the document's phrase into the query phrase). + *

  • tes* + *

    prefix wildcard matching; selects documents containing words starting with tes, + * such as: test, testing or testable. + *

  • /.est(s|ing)/ + *

    documents containing words matching the provided regular expression, such as + * resting or nests. + *

  • nest~2 + *

    fuzzy term matching; documents containing words within 2-edits distance (2 additions, + * removals or replacements of a letter) from nest, such as test, + * net or rests. *

* - * A clause may be either: + *

Field specifications

+ * + *

Most clauses can be prefixed by a field name and a colon: the clause will then apply to that + * field only. If the field specification is omitted, the query parser will expand the clause over + * all fields specified by a call to {@link StandardQueryParser#setMultiFields(CharSequence[])} or + * will use the default field provided in the call to {@link #parse(String, String)}. + * + *

The following are some examples of field-prefixed clauses: * *

    - *
  • a term, indicating all the documents that contain this term; or - *
  • a nested query, enclosed in parentheses. Note that this may be used with a +/ - * - prefix to require any of a set of terms. + *
  • title:test + *

    documents containing test in the title field. + *

  • title:(die OR hard) + *

    documents containing die or hard in the title field. *

* - * Thus, in BNF, the query grammar is: + *

Boolean operators and grouping

* - *
- *   Query  ::= ( Clause )*
- *   Clause ::= ["+", "-"] [<TERM> ":"] ( <TERM> | "(" Query ")" )
- * 
+ *

You can combine clauses using Boolean AND, OR and NOT operators to form more complex + * expressions, for example: * - *

Examples of appropriately formatted queries can be found in the - * query syntax documentation. + *

    + *
  • test AND results + *

    selects documents containing both the word test and the word results. + *

  • test OR suite OR results + *

    selects documents with at least one of test, suite or + * results. + *

  • title:test AND NOT title:complete + *

    selects documents containing test and not containing complete in the + * title field. + *

  • title:test AND (pass* OR fail*) + *

    grouping; use parentheses to specify the precedence of terms in a Boolean clause. Query + * will match documents containing test in the title field and a word + * starting with pass or fail in the default search fields. + *

  • title:(pass fail skip) + *

    shorthand notation; documents containing at least one of pass, fail or + * skip in the title field. + *

  • title:(+test +"result unknown") + *

    shorthand notation; documents containing both pass and result unknown + * in the title field. + *

* - *

The text parser used by this helper is a {@link StandardSyntaxParser}. + *

Note the Boolean operators must be written in all caps, otherwise they are parsed as regular + * terms. * - *

The query node processor used by this helper is a {@link StandardQueryNodeProcessorPipeline}. + *

Range operators

* - *

The builder used by this helper is a {@link StandardQueryTreeBuilder}. + *

To search for ranges of textual or numeric values, use square or curly brackets, for example: + * + *

    + *
  • name:[Jones TO Smith] + *

    inclusive range; selects documents whose name + * field has any value between Jones and Smith, including + * boundaries. + *

  • score:{2.5 TO 7.3} + *

    exclusive range; selects documents whose score field is between 2.5 and + * 7.3, excluding boundaries. + *

  • score:{2.5 TO *] + *

    one-sided range; selects documents whose score field is larger than 2.5. + *

+ * + *

Term boosting

+ * + *

Terms, quoted terms, term range expressions and grouped clauses can have a floating-point + * weight boost applied to them to increase their score relative to other clauses. For + * example: + * + *

    + *
  • jones^2 OR smith^0.5 + *

    prioritize documents with jones term over matches on the smith + * term. + *

  • field:(a OR b NOT c)^2.5 OR field:d + *

    apply the boost to a sub-query. + *

+ * + *

Special character escaping

+ * + *

Most search terms can be put in double quotes making special-character escaping not necessary. + * If the search term contains the quote character (or cannot be quoted for some reason), any + * character can be quoted with a backslash. For example: + * + *

    + *
  • \:\(quoted\+term\)\: + *

    a single search term (quoted+term): with escape sequences. An alternative + * quoted form would be simpler: ":(quoted+term):" + * . + *

+ * + *

Minimum-should-match constraint for Boolean disjunction groups

+ * + *

A minimum-should-match operator can be applied to a disjunction Boolean query (a query with + * only "OR"-subclauses) and forces the query to match documents with at least the provided number + * of these subclauses. For example: + * + *

    + *
  • (blue crab fish)@2 + *

    matches all documents with at least two terms from the set [blue, crab, fish] (in any + * order). + *

  • ((yellow OR blue) crab fish)@2 + *

    sub-clauses of a Boolean query can themselves be complex queries; here the + * min-should-match selects documents that match at least two of the provided three + * sub-clauses. + *

+ * + *

Interval function clauses

+ * + *

Interval functions are a powerful tool to express search needs in terms of one or more * + * contiguous fragments of text and their relationship to one another. All interval clauses start + * with the {@code fn:} prefix (possibly prefixed by a field specification). For example: + * + *

    + *
  • fn:ordered(quick brown fox) + *

    matches all documents (in the default field or in multi-field expansion) with at least + * one ordered sequence of quick, + * brown and fox terms. + *

  • title:fn:maxwidth(5 fn:atLeast(2 quick brown fox)) + *

    matches all documents in the title + * field where at least two of the three terms (quick, + * brown and fox) occur within five positions of each other. + *

+ * + * Please refer to the {@linkplain org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn + * interval functions package} for more information on which functions are available and how they + * work. * * @see StandardQueryParser * @see StandardQueryConfigHandler diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/builders/IntervalQueryNodeBuilder.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/builders/IntervalQueryNodeBuilder.java new file mode 100644 index 00000000000..e76a4596455 --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/builders/IntervalQueryNodeBuilder.java @@ -0,0 +1,30 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.builders; + +import org.apache.lucene.queryparser.flexible.core.QueryNodeException; +import org.apache.lucene.queryparser.flexible.core.nodes.QueryNode; +import org.apache.lucene.queryparser.flexible.standard.nodes.IntervalQueryNode; +import org.apache.lucene.search.Query; + +/** Builds a {@link Query} from an {@link IntervalQueryNode}. */ +public class IntervalQueryNodeBuilder implements StandardQueryBuilder { + @Override + public Query build(QueryNode queryNode) throws QueryNodeException { + return ((IntervalQueryNode) queryNode).getQuery(); + } +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/builders/MinShouldMatchNodeBuilder.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/builders/MinShouldMatchNodeBuilder.java new file mode 100644 index 00000000000..bbb19618d06 --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/builders/MinShouldMatchNodeBuilder.java @@ -0,0 +1,46 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.builders; + +import java.util.List; +import org.apache.lucene.queryparser.flexible.core.builders.QueryBuilder; +import org.apache.lucene.queryparser.flexible.core.builders.QueryTreeBuilder; +import org.apache.lucene.queryparser.flexible.core.nodes.QueryNode; +import org.apache.lucene.queryparser.flexible.standard.nodes.MinShouldMatchNode; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.Query; + +/** Builds a {@link BooleanQuery} from a {@link MinShouldMatchNode}. */ +public class MinShouldMatchNodeBuilder implements QueryBuilder { + @Override + public Query build(QueryNode queryNode) { + MinShouldMatchNode mmNode = (MinShouldMatchNode) queryNode; + + List children = queryNode.getChildren(); + if (children.size() != 1) { + throw new RuntimeException("Unexpected number of node children: " + children.size()); + } + + Query q = (Query) mmNode.groupQueryNode.getTag(QueryTreeBuilder.QUERY_TREE_BUILDER_TAGID); + + BooleanQuery booleanQuery = (BooleanQuery) q; + BooleanQuery.Builder builder = new BooleanQuery.Builder(); + builder.setMinimumNumberShouldMatch(mmNode.minShouldMatch); + booleanQuery.clauses().forEach(builder::add); + return builder.build(); + } +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/builders/StandardQueryTreeBuilder.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/builders/StandardQueryTreeBuilder.java index 6e9d1e2953d..39bf23756b4 100644 --- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/builders/StandardQueryTreeBuilder.java +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/builders/StandardQueryTreeBuilder.java @@ -29,6 +29,8 @@ import org.apache.lucene.queryparser.flexible.core.nodes.ModifierQueryNode; import org.apache.lucene.queryparser.flexible.core.nodes.QueryNode; import org.apache.lucene.queryparser.flexible.core.nodes.SlopQueryNode; import org.apache.lucene.queryparser.flexible.core.nodes.TokenizedPhraseQueryNode; +import org.apache.lucene.queryparser.flexible.standard.nodes.IntervalQueryNode; +import org.apache.lucene.queryparser.flexible.standard.nodes.MinShouldMatchNode; import org.apache.lucene.queryparser.flexible.standard.nodes.MultiPhraseQueryNode; import org.apache.lucene.queryparser.flexible.standard.nodes.PointQueryNode; import org.apache.lucene.queryparser.flexible.standard.nodes.PointRangeQueryNode; @@ -69,6 +71,8 @@ public class StandardQueryTreeBuilder extends QueryTreeBuilder implements Standa setBuilder(SynonymQueryNode.class, new SynonymQueryNodeBuilder()); setBuilder(MultiPhraseQueryNode.class, new MultiPhraseQueryNodeBuilder()); setBuilder(MatchAllDocsQueryNode.class, new MatchAllDocsQueryNodeBuilder()); + setBuilder(MinShouldMatchNode.class, new MinShouldMatchNodeBuilder()); + setBuilder(IntervalQueryNode.class, new IntervalQueryNodeBuilder()); } @Override diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/IntervalQueryNode.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/IntervalQueryNode.java new file mode 100644 index 00000000000..494595a0e2d --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/IntervalQueryNode.java @@ -0,0 +1,76 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.nodes; + +import java.util.Locale; +import java.util.Objects; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.queries.intervals.IntervalQuery; +import org.apache.lucene.queryparser.flexible.core.nodes.FieldableNode; +import org.apache.lucene.queryparser.flexible.core.nodes.QueryNodeImpl; +import org.apache.lucene.queryparser.flexible.core.parser.EscapeQuerySyntax; +import org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn.IntervalFunction; +import org.apache.lucene.queryparser.flexible.standard.parser.EscapeQuerySyntaxImpl; +import org.apache.lucene.search.Query; + +/** Node that represents an interval function. */ +public class IntervalQueryNode extends QueryNodeImpl implements FieldableNode { + private final IntervalFunction source; + private String field; + private Analyzer analyzer; + + public IntervalQueryNode(String field, IntervalFunction source) { + this.field = field; + this.source = Objects.requireNonNull(source); + } + + public Query getQuery() { + Objects.requireNonNull(field, "Field must not be null for interval queries."); + Objects.requireNonNull(analyzer, "Analyzer must not be null for interval queries."); + return new IntervalQuery(field, source.toIntervalSource(field, analyzer)); + } + + @Override + public String toQueryString(EscapeQuerySyntax escapeSyntaxParser) { + return String.format(Locale.ROOT, "%s:%s", field, source); + } + + @Override + public String toString() { + return toQueryString(new EscapeQuerySyntaxImpl()); + } + + @Override + public CharSequence getField() { + return field; + } + + @Override + public void setField(CharSequence fieldName) { + this.field = Objects.requireNonNull(fieldName.toString()); + } + + @Override + public IntervalQueryNode cloneTree() { + return new IntervalQueryNode(field, source); + } + + public void setAnalyzer(Analyzer analyzer) { + this.analyzer = + Objects.requireNonNull(analyzer, "Analyzer must not be null for interval queries."); + } +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/MinShouldMatchNode.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/MinShouldMatchNode.java new file mode 100644 index 00000000000..2970d9f784e --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/MinShouldMatchNode.java @@ -0,0 +1,41 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.nodes; + +import org.apache.lucene.queryparser.flexible.core.nodes.GroupQueryNode; +import org.apache.lucene.queryparser.flexible.core.nodes.QueryNodeImpl; +import org.apache.lucene.queryparser.flexible.core.parser.EscapeQuerySyntax; + +/** Node that represents a minimum-should-match restriction on a {@link GroupQueryNode}. */ +public class MinShouldMatchNode extends QueryNodeImpl { + public final int minShouldMatch; + public final GroupQueryNode groupQueryNode; + + public MinShouldMatchNode(int minShouldMatch, GroupQueryNode groupQueryNode) { + this.minShouldMatch = minShouldMatch; + this.groupQueryNode = groupQueryNode; + + this.setLeaf(false); + this.allocate(); + add(groupQueryNode); + } + + @Override + public CharSequence toQueryString(EscapeQuerySyntax escapeSyntaxParser) { + return groupQueryNode.toQueryString(escapeSyntaxParser) + "@" + minShouldMatch; + } +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/WildcardQueryNode.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/WildcardQueryNode.java index b49cafc4311..06e1a1d8c68 100644 --- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/WildcardQueryNode.java +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/WildcardQueryNode.java @@ -21,7 +21,7 @@ import org.apache.lucene.queryparser.flexible.core.parser.EscapeQuerySyntax; /** * A {@link WildcardQueryNode} represents wildcard query This does not apply to phrases. Examples: - * a*b*c Fl?w? m?ke*g + * {@code a*b*c Fl?w? m?ke*g}. */ public class WildcardQueryNode extends FieldQueryNode { diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/After.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/After.java new file mode 100644 index 00000000000..d67d68d475e --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/After.java @@ -0,0 +1,45 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn; + +import java.util.Locale; +import java.util.Objects; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.queries.intervals.Intervals; +import org.apache.lucene.queries.intervals.IntervalsSource; + +/** Node that represents {@link Intervals#after(IntervalsSource, IntervalsSource)}. */ +public class After extends IntervalFunction { + private final IntervalFunction source; + private final IntervalFunction reference; + + public After(IntervalFunction source, IntervalFunction reference) { + this.source = Objects.requireNonNull(source); + this.reference = Objects.requireNonNull(reference); + } + + @Override + public IntervalsSource toIntervalSource(String field, Analyzer analyzer) { + return Intervals.after( + source.toIntervalSource(field, analyzer), reference.toIntervalSource(field, analyzer)); + } + + @Override + public String toString() { + return String.format(Locale.ROOT, "fn:after(%s %s)", source, reference); + } +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/AnalyzedText.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/AnalyzedText.java new file mode 100644 index 00000000000..4cba173983c --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/AnalyzedText.java @@ -0,0 +1,56 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn; + +import java.io.IOException; +import java.util.regex.Pattern; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.queries.intervals.Intervals; +import org.apache.lucene.queries.intervals.IntervalsSource; + +/** Node that represents {@link Intervals#analyzedText(String, Analyzer, String, int, boolean)}. */ +public class AnalyzedText extends IntervalFunction { + private final String term; + + public AnalyzedText(String term) { + this.term = term; + } + + @Override + public IntervalsSource toIntervalSource(String field, Analyzer analyzer) { + int gaps = 0; + boolean ordered = true; + try { + return Intervals.analyzedText(term, analyzer, field, gaps, ordered); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public String toString() { + if (requiresQuotes(term)) { + return '"' + term + '"'; + } else { + return term; + } + } + + private boolean requiresQuotes(String term) { + return Pattern.compile("[\\s]").matcher(term).find(); + } +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/AtLeast.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/AtLeast.java new file mode 100644 index 00000000000..622db786070 --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/AtLeast.java @@ -0,0 +1,54 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn; + +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.stream.Collectors; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.queries.intervals.Intervals; +import org.apache.lucene.queries.intervals.IntervalsSource; + +/** Node that represents {@link Intervals#atLeast(int, IntervalsSource...)}. */ +public class AtLeast extends IntervalFunction { + private final int minShouldMatch; + private final List sources; + + public AtLeast(int minShouldMatch, List sources) { + this.minShouldMatch = minShouldMatch; + this.sources = Objects.requireNonNull(sources); + } + + @Override + public IntervalsSource toIntervalSource(String field, Analyzer analyzer) { + return Intervals.atLeast( + minShouldMatch, + sources.stream() + .map(intervalFunction -> intervalFunction.toIntervalSource(field, analyzer)) + .toArray(IntervalsSource[]::new)); + } + + @Override + public String toString() { + return String.format( + Locale.ROOT, + "fn:atLeast(%s %s)", + minShouldMatch, + sources.stream().map(IntervalFunction::toString).collect(Collectors.joining(" "))); + } +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Before.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Before.java new file mode 100644 index 00000000000..a512a501c35 --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Before.java @@ -0,0 +1,45 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn; + +import java.util.Locale; +import java.util.Objects; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.queries.intervals.Intervals; +import org.apache.lucene.queries.intervals.IntervalsSource; + +/** Node that represents {@link Intervals#before(IntervalsSource, IntervalsSource)}. */ +public class Before extends IntervalFunction { + private final IntervalFunction source; + private final IntervalFunction reference; + + public Before(IntervalFunction source, IntervalFunction reference) { + this.source = Objects.requireNonNull(source); + this.reference = Objects.requireNonNull(reference); + } + + @Override + public IntervalsSource toIntervalSource(String field, Analyzer analyzer) { + return Intervals.before( + source.toIntervalSource(field, analyzer), reference.toIntervalSource(field, analyzer)); + } + + @Override + public String toString() { + return String.format(Locale.ROOT, "fn:before(%s %s)", source, reference); + } +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/ContainedBy.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/ContainedBy.java new file mode 100644 index 00000000000..4a45f737170 --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/ContainedBy.java @@ -0,0 +1,45 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn; + +import java.util.Locale; +import java.util.Objects; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.queries.intervals.Intervals; +import org.apache.lucene.queries.intervals.IntervalsSource; + +/** Node that represents {@link Intervals#containedBy(IntervalsSource, IntervalsSource)}. */ +public class ContainedBy extends IntervalFunction { + private final IntervalFunction big; + private final IntervalFunction small; + + public ContainedBy(IntervalFunction small, IntervalFunction big) { + this.small = Objects.requireNonNull(small); + this.big = Objects.requireNonNull(big); + } + + @Override + public IntervalsSource toIntervalSource(String field, Analyzer analyzer) { + return Intervals.containedBy( + small.toIntervalSource(field, analyzer), big.toIntervalSource(field, analyzer)); + } + + @Override + public String toString() { + return String.format(Locale.ROOT, "fn:containedBy(%s %s)", small, big); + } +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Containing.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Containing.java new file mode 100644 index 00000000000..3713d4c377a --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Containing.java @@ -0,0 +1,45 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn; + +import java.util.Locale; +import java.util.Objects; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.queries.intervals.Intervals; +import org.apache.lucene.queries.intervals.IntervalsSource; + +/** Node that represents {@link Intervals#containing(IntervalsSource, IntervalsSource)}. */ +public class Containing extends IntervalFunction { + private final IntervalFunction big; + private final IntervalFunction small; + + public Containing(IntervalFunction big, IntervalFunction small) { + this.big = Objects.requireNonNull(big); + this.small = Objects.requireNonNull(small); + } + + @Override + public IntervalsSource toIntervalSource(String field, Analyzer analyzer) { + return Intervals.containing( + big.toIntervalSource(field, analyzer), small.toIntervalSource(field, analyzer)); + } + + @Override + public String toString() { + return String.format(Locale.ROOT, "fn:containing(%s %s)", big, small); + } +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Extend.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Extend.java new file mode 100644 index 00000000000..cea69488a94 --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Extend.java @@ -0,0 +1,45 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn; + +import java.util.Locale; +import java.util.Objects; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.queries.intervals.Intervals; +import org.apache.lucene.queries.intervals.IntervalsSource; + +/** Node that represents {@link Intervals#extend(IntervalsSource, int, int)}. */ +public class Extend extends IntervalFunction { + private final int before, after; + private final IntervalFunction source; + + public Extend(IntervalFunction source, int before, int after) { + this.source = Objects.requireNonNull(source); + this.before = before; + this.after = after; + } + + @Override + public IntervalsSource toIntervalSource(String field, Analyzer analyzer) { + return Intervals.extend(source.toIntervalSource(field, analyzer), before, after); + } + + @Override + public String toString() { + return String.format(Locale.ROOT, "fn:extend(%s %d %d)", source, before, after); + } +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/IntervalFunction.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/IntervalFunction.java new file mode 100644 index 00000000000..be166eb5cb0 --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/IntervalFunction.java @@ -0,0 +1,28 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn; + +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.queries.intervals.IntervalsSource; + +/** Representation of an interval function that can be converted to {@link IntervalsSource}. */ +public abstract class IntervalFunction { + public abstract IntervalsSource toIntervalSource(String field, Analyzer analyzer); + + @Override + public abstract String toString(); +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/MaxGaps.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/MaxGaps.java new file mode 100644 index 00000000000..fa3d2826369 --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/MaxGaps.java @@ -0,0 +1,44 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn; + +import java.util.Locale; +import java.util.Objects; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.queries.intervals.Intervals; +import org.apache.lucene.queries.intervals.IntervalsSource; + +/** Node that represents {@link Intervals#maxgaps(int, IntervalsSource)}. */ +public class MaxGaps extends IntervalFunction { + private final int maxGaps; + private final IntervalFunction source; + + public MaxGaps(int maxGaps, IntervalFunction source) { + this.maxGaps = maxGaps; + this.source = Objects.requireNonNull(source); + } + + @Override + public IntervalsSource toIntervalSource(String field, Analyzer analyzer) { + return Intervals.maxgaps(maxGaps, source.toIntervalSource(field, analyzer)); + } + + @Override + public String toString() { + return String.format(Locale.ROOT, "fn:maxgaps(%s %s)", maxGaps, source); + } +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/MaxWidth.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/MaxWidth.java new file mode 100644 index 00000000000..22f31927e6e --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/MaxWidth.java @@ -0,0 +1,44 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn; + +import java.util.Locale; +import java.util.Objects; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.queries.intervals.Intervals; +import org.apache.lucene.queries.intervals.IntervalsSource; + +/** Node that represents {@link Intervals#maxwidth(int, IntervalsSource)}. */ +public class MaxWidth extends IntervalFunction { + private final int width; + private final IntervalFunction source; + + public MaxWidth(int width, IntervalFunction source) { + this.width = width; + this.source = Objects.requireNonNull(source); + } + + @Override + public IntervalsSource toIntervalSource(String field, Analyzer analyzer) { + return Intervals.maxwidth(width, source.toIntervalSource(field, analyzer)); + } + + @Override + public String toString() { + return String.format(Locale.ROOT, "fn:maxwidth(%s %s)", width, source); + } +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/NonOverlapping.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/NonOverlapping.java new file mode 100644 index 00000000000..6655438615b --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/NonOverlapping.java @@ -0,0 +1,45 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn; + +import java.util.Locale; +import java.util.Objects; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.queries.intervals.Intervals; +import org.apache.lucene.queries.intervals.IntervalsSource; + +/** Node that represents {@link Intervals#nonOverlapping(IntervalsSource, IntervalsSource)} . */ +public class NonOverlapping extends IntervalFunction { + private final IntervalFunction minuend; + private final IntervalFunction subtrahend; + + public NonOverlapping(IntervalFunction minuend, IntervalFunction subtrahend) { + this.minuend = Objects.requireNonNull(minuend); + this.subtrahend = Objects.requireNonNull(subtrahend); + } + + @Override + public IntervalsSource toIntervalSource(String field, Analyzer analyzer) { + return Intervals.nonOverlapping( + minuend.toIntervalSource(field, analyzer), subtrahend.toIntervalSource(field, analyzer)); + } + + @Override + public String toString() { + return String.format(Locale.ROOT, "fn:nonOverlapping(%s %s)", minuend, subtrahend); + } +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/NotContainedBy.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/NotContainedBy.java new file mode 100644 index 00000000000..3bc2ed2c38a --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/NotContainedBy.java @@ -0,0 +1,45 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn; + +import java.util.Locale; +import java.util.Objects; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.queries.intervals.Intervals; +import org.apache.lucene.queries.intervals.IntervalsSource; + +/** Node that represents {@link Intervals#notContainedBy(IntervalsSource, IntervalsSource)}. */ +public class NotContainedBy extends IntervalFunction { + private final IntervalFunction small; + private final IntervalFunction big; + + public NotContainedBy(IntervalFunction small, IntervalFunction big) { + this.small = Objects.requireNonNull(small); + this.big = Objects.requireNonNull(big); + } + + @Override + public IntervalsSource toIntervalSource(String field, Analyzer analyzer) { + return Intervals.notContainedBy( + small.toIntervalSource(field, analyzer), big.toIntervalSource(field, analyzer)); + } + + @Override + public String toString() { + return String.format(Locale.ROOT, "fn:notContainedBy(%s %s)", small, big); + } +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/NotContaining.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/NotContaining.java new file mode 100644 index 00000000000..6ab80bf0b77 --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/NotContaining.java @@ -0,0 +1,45 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn; + +import java.util.Locale; +import java.util.Objects; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.queries.intervals.Intervals; +import org.apache.lucene.queries.intervals.IntervalsSource; + +/** Node that represents {@link Intervals#notContaining(IntervalsSource, IntervalsSource)}. */ +public class NotContaining extends IntervalFunction { + private final IntervalFunction minuend; + private final IntervalFunction subtrahend; + + public NotContaining(IntervalFunction minuend, IntervalFunction subtrahend) { + this.minuend = Objects.requireNonNull(minuend); + this.subtrahend = Objects.requireNonNull(subtrahend); + } + + @Override + public IntervalsSource toIntervalSource(String field, Analyzer analyzer) { + return Intervals.notContaining( + minuend.toIntervalSource(field, analyzer), subtrahend.toIntervalSource(field, analyzer)); + } + + @Override + public String toString() { + return String.format(Locale.ROOT, "fn:notContaining(%s %s)", minuend, subtrahend); + } +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/NotWithin.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/NotWithin.java new file mode 100644 index 00000000000..adae1c5382e --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/NotWithin.java @@ -0,0 +1,48 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn; + +import java.util.Locale; +import java.util.Objects; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.queries.intervals.Intervals; +import org.apache.lucene.queries.intervals.IntervalsSource; + +/** Node that represents {@link Intervals#notWithin(IntervalsSource, int, IntervalsSource)}. */ +public class NotWithin extends IntervalFunction { + private final int positions; + private final IntervalFunction minuend, subtrahend; + + public NotWithin(IntervalFunction minuend, int positions, IntervalFunction subtrahend) { + this.positions = positions; + this.minuend = Objects.requireNonNull(minuend); + this.subtrahend = Objects.requireNonNull(subtrahend); + } + + @Override + public IntervalsSource toIntervalSource(String field, Analyzer analyzer) { + return Intervals.notWithin( + minuend.toIntervalSource(field, analyzer), + positions, + subtrahend.toIntervalSource(field, analyzer)); + } + + @Override + public String toString() { + return String.format(Locale.ROOT, "fn:notWithin(%s %d %s)", minuend, positions, subtrahend); + } +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Or.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Or.java new file mode 100644 index 00000000000..b1058db46de --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Or.java @@ -0,0 +1,50 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn; + +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.stream.Collectors; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.queries.intervals.Intervals; +import org.apache.lucene.queries.intervals.IntervalsSource; + +/** Node that represents {@link Intervals#or(IntervalsSource...)}. */ +public class Or extends IntervalFunction { + private final List sources; + + public Or(List sources) { + this.sources = Objects.requireNonNull(sources); + } + + @Override + public IntervalsSource toIntervalSource(String field, Analyzer analyzer) { + return Intervals.or( + sources.stream() + .map(intervalFunction -> intervalFunction.toIntervalSource(field, analyzer)) + .toArray(IntervalsSource[]::new)); + } + + @Override + public String toString() { + return String.format( + Locale.ROOT, + "fn:or(%s)", + sources.stream().map(IntervalFunction::toString).collect(Collectors.joining(" "))); + } +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Ordered.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Ordered.java new file mode 100644 index 00000000000..839cbeb4e2a --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Ordered.java @@ -0,0 +1,50 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn; + +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.stream.Collectors; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.queries.intervals.Intervals; +import org.apache.lucene.queries.intervals.IntervalsSource; + +/** Node that represents {@link Intervals#ordered(IntervalsSource...)}. */ +public class Ordered extends IntervalFunction { + private final List sources; + + public Ordered(List sources) { + this.sources = Objects.requireNonNull(sources); + } + + @Override + public IntervalsSource toIntervalSource(String field, Analyzer analyzer) { + return Intervals.ordered( + sources.stream() + .map(intervalFunction -> intervalFunction.toIntervalSource(field, analyzer)) + .toArray(IntervalsSource[]::new)); + } + + @Override + public String toString() { + return String.format( + Locale.ROOT, + "fn:ordered(%s)", + sources.stream().map(IntervalFunction::toString).collect(Collectors.joining(" "))); + } +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Overlapping.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Overlapping.java new file mode 100644 index 00000000000..4824a858446 --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Overlapping.java @@ -0,0 +1,45 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn; + +import java.util.Locale; +import java.util.Objects; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.queries.intervals.Intervals; +import org.apache.lucene.queries.intervals.IntervalsSource; + +/** Node that represents {@link Intervals#overlapping(IntervalsSource, IntervalsSource)}. */ +public class Overlapping extends IntervalFunction { + private final IntervalFunction source; + private final IntervalFunction reference; + + public Overlapping(IntervalFunction source, IntervalFunction reference) { + this.source = Objects.requireNonNull(source); + this.reference = Objects.requireNonNull(reference); + } + + @Override + public IntervalsSource toIntervalSource(String field, Analyzer analyzer) { + return Intervals.overlapping( + source.toIntervalSource(field, analyzer), reference.toIntervalSource(field, analyzer)); + } + + @Override + public String toString() { + return String.format(Locale.ROOT, "fn:overlapping(%s %s)", source, reference); + } +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Phrase.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Phrase.java new file mode 100644 index 00000000000..63781138b5d --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Phrase.java @@ -0,0 +1,50 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn; + +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.stream.Collectors; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.queries.intervals.Intervals; +import org.apache.lucene.queries.intervals.IntervalsSource; + +/** Node that represents {@link Intervals#phrase(String...)}. */ +public class Phrase extends IntervalFunction { + private final List sources; + + public Phrase(List sources) { + this.sources = Objects.requireNonNull(sources); + } + + @Override + public IntervalsSource toIntervalSource(String field, Analyzer analyzer) { + return Intervals.phrase( + sources.stream() + .map(intervalFunction -> intervalFunction.toIntervalSource(field, analyzer)) + .toArray(IntervalsSource[]::new)); + } + + @Override + public String toString() { + return String.format( + Locale.ROOT, + "fn:phrase(%s)", + sources.stream().map(IntervalFunction::toString).collect(Collectors.joining(" "))); + } +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Unordered.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Unordered.java new file mode 100644 index 00000000000..7b876a5a208 --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Unordered.java @@ -0,0 +1,50 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn; + +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.stream.Collectors; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.queries.intervals.Intervals; +import org.apache.lucene.queries.intervals.IntervalsSource; + +/** Node that represents {@link Intervals#unordered(IntervalsSource...)}. */ +public class Unordered extends IntervalFunction { + private final List sources; + + public Unordered(List sources) { + this.sources = Objects.requireNonNull(sources); + } + + @Override + public IntervalsSource toIntervalSource(String field, Analyzer analyzer) { + return Intervals.unordered( + sources.stream() + .map(intervalFunction -> intervalFunction.toIntervalSource(field, analyzer)) + .toArray(IntervalsSource[]::new)); + } + + @Override + public String toString() { + return String.format( + Locale.ROOT, + "fn:unordered(%s)", + sources.stream().map(IntervalFunction::toString).collect(Collectors.joining(" "))); + } +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/UnorderedNoOverlaps.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/UnorderedNoOverlaps.java new file mode 100644 index 00000000000..18d01ede718 --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/UnorderedNoOverlaps.java @@ -0,0 +1,44 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn; + +import java.util.Locale; +import java.util.Objects; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.queries.intervals.Intervals; +import org.apache.lucene.queries.intervals.IntervalsSource; + +/** Node that represents {@link Intervals#unorderedNoOverlaps(IntervalsSource, IntervalsSource)}. */ +public class UnorderedNoOverlaps extends IntervalFunction { + private final IntervalFunction a, b; + + public UnorderedNoOverlaps(IntervalFunction a, IntervalFunction b) { + this.a = Objects.requireNonNull(a); + this.b = Objects.requireNonNull(b); + } + + @Override + public IntervalsSource toIntervalSource(String field, Analyzer analyzer) { + return Intervals.unorderedNoOverlaps( + a.toIntervalSource(field, analyzer), b.toIntervalSource(field, analyzer)); + } + + @Override + public String toString() { + return String.format(Locale.ROOT, "fn:unorderedNoOverlaps(%s %s)", a, b); + } +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Wildcard.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Wildcard.java new file mode 100644 index 00000000000..ce4bb072adf --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Wildcard.java @@ -0,0 +1,42 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn; + +import java.util.Locale; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.queries.intervals.Intervals; +import org.apache.lucene.queries.intervals.IntervalsSource; +import org.apache.lucene.util.BytesRef; + +/** Node that represents {@link Intervals#wildcard(BytesRef)}. */ +public class Wildcard extends IntervalFunction { + private final String wildcard; + + public Wildcard(String wildcard) { + this.wildcard = wildcard; + } + + @Override + public IntervalsSource toIntervalSource(String field, Analyzer analyzer) { + return Intervals.wildcard(new BytesRef(wildcard)); + } + + @Override + public String toString() { + return String.format(Locale.ROOT, "fn:wildcard(%s)", wildcard); + } +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Within.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Within.java new file mode 100644 index 00000000000..c3790844a15 --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/Within.java @@ -0,0 +1,48 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn; + +import java.util.Locale; +import java.util.Objects; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.queries.intervals.Intervals; +import org.apache.lucene.queries.intervals.IntervalsSource; + +/** Node that represents {@link Intervals#within(IntervalsSource, int, IntervalsSource)}. */ +public class Within extends IntervalFunction { + private final int positions; + private final IntervalFunction source, reference; + + public Within(IntervalFunction source, int positions, IntervalFunction reference) { + this.positions = positions; + this.source = Objects.requireNonNull(source); + this.reference = Objects.requireNonNull(reference); + } + + @Override + public IntervalsSource toIntervalSource(String field, Analyzer analyzer) { + return Intervals.within( + source.toIntervalSource(field, analyzer), + positions, + reference.toIntervalSource(field, analyzer)); + } + + @Override + public String toString() { + return String.format(Locale.ROOT, "fn:within(%s %d %s)", source, positions, reference); + } +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/package-info.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/package-info.java new file mode 100644 index 00000000000..e8abe61644b --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/nodes/intervalfn/package-info.java @@ -0,0 +1,726 @@ +/* + * 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. + */ + +/** + * This package contains classes that implement {@linkplain + * org.apache.lucene.queries.intervals.Intervals interval function} support for the {@linkplain + * org.apache.lucene.queryparser.flexible.standard.parser.StandardSyntaxParser standard syntax + * parser}. + * + *

What are interval functions?

+ * + *

Interval functions are a powerful tool to express search needs in terms of one or more + * contiguous fragments of text and their relationship to one another. Interval functions are + * implemented by an {@linkplain org.apache.lucene.queries.intervals.IntervalQuery IntervalQuery} + * but many ready-to-use factory methods are provided in the {@linkplain + * org.apache.lucene.queries.intervals.Intervals Intervals} class. + * + *

When Lucene indexes documents (or rather: document fields) the input text is typically split + * into tokens. The details of how this tokenization is performed depends on how the + * field's {@link org.apache.lucene.analysis.Analyzer} is set up. In the end, each token would + * typically have an associated position in the token stream. For example, the following + * sentence: + * + *

The quick brown fox jumps over the lazy dog + * + *

could be transformed into the following token stream (note some token positions are "blank" + * (grayed out) — these positions reflect stop words that are typically not indexed at all). + * + *

The quick2 brown3 fox4 + * jumps5 over6 the + * lazy7 dog8 + * + *

Remembering that intervals are contiguous spans between two positions in a document, consider + * the following example interval function query: fn:ordered(brown dog). This query + * selects any span of text between terms brown and dog. In our example, + * this would correspond to the highlighted fragment below. + * + *

The quick brown fox jumps + * over the lazy dog + * + *

This type of interval function can be called an interval selector. The second class + * of interval functions works by combining or filtering other intervals depending on certain + * criteria. + * + *

The matching interval in the above example can be of any length — if the word + * brown occurs at the beginning of the document and the word dog at the very + * end of the document, the interval would be very long (it would cover the entire document!). Let's + * say we want to restrict the matches to only those intervals with at most 3 positions between the + * search terms: fn:maxgaps(3 fn:ordered(brown dog)). + * + *

There are five tokens in between search terms (so five "gaps" between the matching interval's + * positions) and the above query no longer matches our example document at all. + * + *

Interval filtering functions allow expressing a variety of conditions other Lucene queries + * cannot. For example, consider this interval query that searches for words lazy or + * quick but only if they are in the neighborhood of one position from any of the words + * dog or fox: + * + *

fn:within(fn:or(lazy quick) 1 fn:or(dog fox)) + * + *

The result of this query is correctly shown below (only the word lazy matches the + * query, quick is 2 positions away from fox). + * + *

The quick brown fox jumps over the lazy dog + * + *

The remaining part of this document provides more information on the available functions and + * their expected behavior. + * + *

Classification of interval functions

+ * + *

The following groups of interval functions are available in the {@link + * org.apache.lucene.queryparser.flexible.standard.StandardQueryParser}. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Interval functions grouped by similar functionality.
TermsAlternativesLengthContextOrderingContainment
+ * term literals
+ * fn:wildcard
+ *
+ * fn:or
+ * fn:atLeast + *
+ * fn:maxgaps
+ * fn:maxwidth + *
+ * fn:before
+ * fn:after
+ * fn:extend
+ * fn:within
+ * fn:notWithin + *
+ * fn:ordered
+ * fn:unordered
+ * fn:phrase
+ * fn:unorderedNoOverlaps + *
+ * fn:containedBy
+ * fn:notContainedBy
+ * fn:containing
+ * fn:notContaining
+ * fn:overlapping
+ * fn:nonOverlapping + *
+ * + *

All examples in the description of interval functions (below) assume a document with the + * following content: + * + *

The quick brown fox jumps over the lazy dog + * + *

term literals

+ * + *

Quoted or unquoted character sequences are converted into (analyzed) text intervals. While a + * single term typically results in a single-term interval, a quoted multi-term phrase will produce + * an interval matching the corresponding sequence of tokens. Note this is different from the + * fn:phrase function which takes a sequence of sub-intervals. + * + *

+ *
Examples + *
+ *
    + *
  • fn:or(quick "fox") + *

    The quick brown fox jumps over + * the lazy dog + *

  • fn:or(\"quick fox\") (The document would not match — no phrase + * quick fox exists.) + *

    The quick brown fox jumps + * over the lazy dog + *

  • fn:phrase(quick brown fox) + *

    The quick brown fox jumps over the lazy dog + *

+ *
+ * + *

fn:wildcard

+ * + *

Matches the disjunction of all terms that match a wildcard glob. + * + *

Important! The expanded wildcard must not match more than 128 terms. This is an + * internal limitation that prevents blowing up memory on, for example, prefix expansions that would + * cover huge numbers of alternatives. + * + *

+ *
Arguments + *
+ *

fn:wildcard(glob) + *

+ *
glob + *
term glob to expand (based on the contents of the index). + *
+ *
Examples + *
+ *
    + *
  • fn:wildcard(jump*) + *

    The quick brown fox jumps over the lazy dog + *

  • fn:wildcard(br*n) + *

    The quick brown fox jumps over the lazy dog + *

+ *
+ * + *

fn:or

+ * + *

Matches the disjunction of nested intervals. + * + *

+ *
Arguments + *
+ *

fn:or(sources...) + *

+ *
sources + *
sub-intervals (terms or other functions) + *
+ *
Examples + *
+ *
    + *
  • fn:or(dog fox) + *

    The quick brown fox jumps over the lazy dog + *

+ *
+ * + *

fn:atLeast

+ * + *

Matches documents that contain at least the provided number of source intervals. + * + *

+ *
Arguments + *
+ *

fn:atLeast(min sources...) + *

+ *
min + *
an integer specifying minimum number of sub-interval arguments that must match. + *
sources + *
sub-intervals (terms or other functions) + *
+ *
Examples + *
+ *
    + *
  • fn:atLeast(2 quick fox "furry dog") + *

    The quick brown fox jumps over the lazy dog + *

  • fn:atLeast(2 fn:unordered(furry dog) fn:unordered(brown dog) lazy quick) + * (This query results in multiple overlapping intervals.) + *

    The quick brown fox jumps over the lazy dog
    + * The quick brown fox jumps over the lazy dog
    + * The quick brown fox jumps over the lazy dog + *

+ *
+ * + *

fn:maxgaps

+ * + *

Accepts source interval if it has at most max position gaps. + * + *

+ *
Arguments + *
+ *

fn:maxgaps(gaps source) + *

+ *
gaps + *
an integer specifying maximum number of source's position gaps. + *
source + *
source sub-interval. + *
+ *
Examples + *
+ *
    + *
  • fn:maxgaps(0 fn:ordered(fn:or(quick lazy) fn:or(fox dog))) + *

    The quick brown fox jumps + * over the lazy dog + *

  • fn:maxgaps(1 fn:ordered(fn:or(quick lazy) fn:or(fox dog))) + *

    The quick brown fox jumps over the lazy + * dog + *

+ *
+ * + *

fn:maxwidth

+ * + *

Accepts source interval if it has at most the given width (position span). + * + *

+ *
Arguments + *
+ *

fn:maxwidth(max source) + *

+ *
max + *
an integer specifying maximum width of source's position span. + *
source + *
source sub-interval. + *
+ *
Examples + *
+ *
    + *
  • fn:maxwidth(2 fn:ordered(fn:or(quick lazy) fn:or(fox dog))) + *

    The quick brown fox jumps + * over the lazy dog + *

  • fn:maxwidth(3 fn:ordered(fn:or(quick lazy) fn:or(fox dog))) + *

    The quick brown fox jumps over the lazy + * dog + *

+ *
+ * + *

fn:phrase

+ * + *

Matches an ordered, gapless sequence of source intervals. + * + *

+ *
Arguments + *
+ *

fn:phrase(sources...) + *

+ *
sources + *
sub-intervals (terms or other functions) + *
+ *
Examples + *
+ *
    + *
  • fn:phrase(quick brown fox) + *

    The quick brown fox jumps over the lazy dog + *

  • fn:phrase(fn:ordered(quick fox) jumps) + *

    The quick brown fox jumps over the lazy dog + *

+ *
+ * + *

fn:ordered

+ * + *

Matches an ordered span containing all source intervals, possibly with gaps in between their + * respective source interval positions. Source intervals must not overlap. + * + *

+ *
Arguments + *
+ *

fn:ordered(sources...) + *

+ *
sources + *
sub-intervals (terms or other functions) + *
+ *
Examples + *
+ *
    + *
  • fn:ordered(quick jumps dog) + *

    The quick brown fox jumps over the lazy dog + *

  • fn:ordered(quick fn:or(fox dog)) (Note only the shorter match out of + * the two alternatives is included in the result; the algorithm is not required to + * return or highlight all matching interval alternatives). + *

    The quick brown fox jumps over the lazy dog + *

  • fn:ordered(quick jumps fn:or(fox dog)) + *

    The quick brown fox jumps over the lazy dog + *

  • fn:ordered(fn:phrase(brown fox) fn:phrase(fox jumps)) (Sources + * overlap, no matches.) + *

    The quick brown fox jumps + * over the lazy dog + *

+ *
+ * + *

fn:unordered

+ * + *

Matches an unordered span containing all source intervals, possibly with gaps in between their + * respective source interval positions. Source intervals may overlap. + * + *

+ *
Arguments + *
+ *

fn:unordered(sources...) + *

+ *
sources + *
sub-intervals (terms or other functions) + *
+ *
Examples + *
+ *
    + *
  • fn:unordered(dog jumps quick) + *

    The quick brown fox jumps over the lazy dog + *

  • fn:unordered(fn:or(fox dog) quick) (Note only the shorter match out + * of the two alternatives is included in the result; the algorithm is not required to + * return or highlight all matching interval alternatives). + *

    The quick brown fox jumps over the lazy dog + *

  • fn:unordered(fn:phrase(brown fox) fn:phrase(fox jumps)) + *

    The quick brown fox jumps over the lazy dog + *

+ *
+ * + *

fn:unorderedNoOverlaps

+ * + *

Matches an unordered span containing two source intervals, possibly with gaps in between their + * respective source interval positions. Source intervals must not overlap. + * + *

Note that, unlike fn:unordered, this function takes a fixed number of arguments + * (two). + * + *

+ *
Arguments + *
+ *

fn:unorderedNoOverlaps(source1 source2) + *

+ *
source1 + *
sub-interval (term or other function) + *
source2 + *
sub-interval (term or other function) + *
+ *
Examples + *
+ *
    + *
  • fn:unorderedNoOverlaps(fn:phrase(fox jumps) brown) + *

    The quick brown fox jumps over the lazy dog + *

  • fn:unorderedNoOverlaps(fn:phrase(brown fox) fn:phrase(fox jumps)) + * (Sources overlap, no matches.) + *

    The quick brown fox jumps + * over the lazy dog + *

+ *
+ * + *

fn:before

+ * + *

Matches intervals from the source that appear before intervals from the reference. + * + *

Reference intervals will not be part of the match (this is a filtering function). + * + *

+ *
Arguments + *
+ *

fn:before(source reference) + *

+ *
source + *
source sub-interval (term or other function) + *
reference + *
reference sub-interval (term or other function) + *
+ *
Examples + *
+ *
    + *
  • fn:before(fn:or(brown lazy) fox) + *

    The quick brown fox jumps over the lazy dog + *

  • fn:before(fn:or(brown lazy) fn:or(dog fox)) + *

    The quick brown fox jumps over the lazy + * dog + *

+ *
+ * + *

fn:after

+ * + *

Matches intervals from the source that appear after intervals from the reference. + * + *

Reference intervals will not be part of the match (this is a filtering function). + * + *

+ *
Arguments + *
+ *

fn:after(source reference) + *

+ *
source + *
source sub-interval (term or other function) + *
reference + *
reference sub-interval (term or other function) + *
+ *
Examples + *
+ *
    + *
  • fn:after(fn:or(brown lazy) fox) + *

    The quick brown fox jumps + * over the lazy dog + *

  • fn:after(fn:or(brown lazy) fn:or(dog fox)) + *

    The quick brown fox jumps + * over the lazy dog + *

+ *
+ * + *

fn:extend

+ * + *

Matches an interval around another source, extending its span by a number of positions before + * and after. + * + *

This is an advanced function that allows extending the left and right "context" of another + * interval. + * + *

+ *
Arguments + *
+ *

fn:extend(source before after) + *

+ *
source + *
source sub-interval (term or other function) + *
before + *
an integer number of positions to extend to the left of the source + *
after + *
an integer number of positions to extend to the right of the source + *
+ *
Examples + *
+ *
    + *
  • fn:extend(fox 1 2) + *

    The quick brown fox jumps over the lazy dog + *

  • fn:extend(fn:or(dog fox) 2 0) + *

    The quick brown fox jumps over the lazy + * dog + *

+ *
+ * + *

fn:within

+ * + *

Matches intervals of the source that appear within the provided number of positions from the + * intervals of the reference. + * + *

+ *
Arguments + *
+ *

fn:within(source positions reference) + *

+ *
source + *
source sub-interval (term or other function) + *
positions + *
an integer number of maximum positions between source and reference + *
reference + *
reference sub-interval (term or other function) + *
+ *
Examples + *
+ *
    + *
  • fn:within(fn:or(fox dog) 1 fn:or(quick lazy)) + *

    The quick brown fox jumps + * over the lazy dog + *

  • fn:within(fn:or(fox dog) 2 fn:or(quick lazy)) + *

    The quick brown fox jumps over the lazy dog + *

+ *
+ * + *

fn:notWithin

+ * + *

Matches intervals of the source that do not appear within the provided number of + * positions from the intervals of the reference. + * + *

+ *
Arguments + *
+ *

fn:notWithin(source positions reference) + *

+ *
source + *
source sub-interval (term or other function) + *
positions + *
an integer number of maximum positions between source and reference + *
reference + *
reference sub-interval (term or other function) + *
+ *
Examples + *
+ *
    + *
  • fn:notWithin(fn:or(fox dog) 1 fn:or(quick lazy)) + *

    The quick brown fox jumps over the lazy dog + *

+ *
+ * + *

fn:containedBy

+ * + *

Matches intervals of the source that are contained by intervals of the reference. + * + *

+ *
Arguments + *
+ *

fn:containedBy(source reference) + *

+ *
source + *
source sub-interval (term or other function) + *
reference + *
reference sub-interval (term or other function) + *
+ *
Examples + *
+ *
    + *
  • fn:containedBy(fn:or(fox dog) fn:ordered(quick lazy)) + *

    The quick brown fox jumps over the lazy dog + *

  • fn:containedBy(fn:or(fox dog) fn:extend(lazy 3 3)) + *

    The quick brown fox jumps + * over the lazy dog + *

+ *
+ * + *

fn:notContainedBy

+ * + *

Matches intervals of the source that are not contained by intervals of the reference. + * + *

+ *
Arguments + *
+ *

fn:notContainedBy(source reference) + *

+ *
source + *
source sub-interval (term or other function) + *
reference + *
reference sub-interval (term or other function) + *
+ *
Examples + *
+ *
    + *
  • fn:notContainedBy(fn:or(fox dog) fn:ordered(quick lazy)) + *

    The quick brown fox jumps + * over the lazy dog + *

  • fn:notContainedBy(fn:or(fox dog) fn:extend(lazy 3 3)) + *

    The quick brown fox jumps over the lazy dog + *

+ *
+ * + *

fn:containing

+ * + *

Matches intervals of the source that contain at least one intervals of the reference. + * + *

+ *
Arguments + *
+ *

fn:containing(source reference) + *

+ *
source + *
source sub-interval (term or other function) + *
reference + *
reference sub-interval (term or other function) + *
+ *
Examples + *
+ *
    + *
  • fn:containing(fn:extend(fn:or(lazy brown) 1 1) fn:or(fox dog)) + *

    The quick brown fox jumps over the lazy + * dog + *

  • fn:containing(fn:atLeast(2 quick fox dog) jumps) + *

    The quick brown fox jumps over the lazy dog + *

+ *
+ * + *

fn:notContaining

+ * + *

Matches intervals of the source that do not contain any intervals of the reference. + * + *

+ *
Arguments + *
+ *

fn:notContaining(source reference) + *

+ *
source + *
source sub-interval (term or other function) + *
reference + *
reference sub-interval (term or other function) + *
+ *
Examples + *
+ *
    + *
  • fn:notContaining(fn:extend(fn:or(fox dog) 1 0) fn:or(brown yellow)) + *

    The quick brown fox jumps + * over the lazy dog + *

  • fn:notContaining(fn:ordered(fn:or(the The) fn:or(fox dog)) brown) + *

    The quick brown fox jumps + * over the lazy dog + *

+ *
+ * + *

fn:overlapping

+ * + *

Matches intervals of the source that overlap with at least one interval of the reference. + * + *

+ *
Arguments + *
+ *

fn:overlapping(source reference) + *

+ *
source + *
source sub-interval (term or other function) + *
reference + *
reference sub-interval (term or other function) + *
+ *
Examples + *
+ *
    + *
  • fn:overlapping(fn:phrase(brown fox) fn:phrase(fox jumps)) + *

    The quick brown fox jumps over the lazy dog + *

  • fn:overlapping(fn:or(fox dog) fn:extend(lazy 2 2)) + *

    The quick brown fox jumps + * over the lazy dog + *

+ *
+ * + *

fn:nonOverlapping

+ * + *

Matches intervals of the source that do not overlap with any intervals of the + * reference. + * + *

+ *
Arguments + *
+ *

fn:nonOverlapping(source reference) + *

+ *
source + *
source sub-interval (term or other function) + *
reference + *
reference sub-interval (term or other function) + *
+ *
Examples + *
+ *
    + *
  • fn:nonOverlapping(fn:phrase(brown fox) fn:phrase(lazy dog)) + *

    The quick brown fox jumps over the lazy dog + *

  • fn:nonOverlapping(fn:or(fox dog) fn:extend(lazy 2 2)) + *

    The quick brown fox jumps over the lazy dog + *

+ *
+ */ +package org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn; diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/package-info.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/package-info.java index ae123419cb9..569df7a029c 100644 --- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/package-info.java +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/package-info.java @@ -16,10 +16,7 @@ */ /** - * Implementation of the {@linkplain org.apache.lucene.queryparser.classic Lucene classic query - * parser} using the flexible query parser frameworks - * - *

Lucene Flexible Query Parser Implementation

+ * Lucene Flexible Query Parser Implementation * *

The old Lucene query parser used to have only one class that performed all the parsing * operations. In the new query parser structure, the parsing was divided in 3 steps: parsing diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.java index 8824040a8e2..f9575cb374c 100644 --- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.java +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.java @@ -24,26 +24,50 @@ import java.io.Reader; import java.util.Collections; import java.util.ArrayList; -import org.apache.lucene.queryparser.flexible.messages.Message; -import org.apache.lucene.queryparser.flexible.messages.MessageImpl; -import org.apache.lucene.queryparser.flexible.core.QueryNodeParseException; -import org.apache.lucene.queryparser.flexible.core.messages.QueryParserMessages; -import org.apache.lucene.queryparser.flexible.core.parser.SyntaxParser; import org.apache.lucene.queryparser.flexible.core.nodes.AndQueryNode; import org.apache.lucene.queryparser.flexible.core.nodes.BooleanQueryNode; import org.apache.lucene.queryparser.flexible.core.nodes.BoostQueryNode; import org.apache.lucene.queryparser.flexible.core.nodes.FieldQueryNode; import org.apache.lucene.queryparser.flexible.core.nodes.FuzzyQueryNode; -import org.apache.lucene.queryparser.flexible.core.nodes.ModifierQueryNode; import org.apache.lucene.queryparser.flexible.core.nodes.GroupQueryNode; +import org.apache.lucene.queryparser.flexible.core.nodes.ModifierQueryNode; import org.apache.lucene.queryparser.flexible.core.nodes.OrQueryNode; -import org.apache.lucene.queryparser.flexible.core.nodes.SlopQueryNode; import org.apache.lucene.queryparser.flexible.core.nodes.QueryNode; import org.apache.lucene.queryparser.flexible.core.nodes.QuotedFieldQueryNode; -import org.apache.lucene.queryparser.flexible.standard.nodes.TermRangeQueryNode; +import org.apache.lucene.queryparser.flexible.core.nodes.SlopQueryNode; +import org.apache.lucene.queryparser.flexible.messages.Message; +import org.apache.lucene.queryparser.flexible.messages.MessageImpl; +import org.apache.lucene.queryparser.flexible.core.QueryNodeParseException; +import org.apache.lucene.queryparser.flexible.core.messages.QueryParserMessages; +import org.apache.lucene.queryparser.flexible.core.parser.SyntaxParser; +import org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn.After; +import org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn.AnalyzedText; +import org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn.AtLeast; +import org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn.Before; +import org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn.ContainedBy; +import org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn.Containing; +import org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn.Extend; +import org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn.IntervalFunction; +import org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn.MaxGaps; +import org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn.MaxWidth; +import org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn.NonOverlapping; +import org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn.NotContainedBy; +import org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn.NotContaining; +import org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn.NotWithin; +import org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn.Or; +import org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn.Ordered; +import org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn.Overlapping; +import org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn.Phrase; +import org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn.Unordered; +import org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn.UnorderedNoOverlaps; +import org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn.Wildcard; +import org.apache.lucene.queryparser.flexible.standard.nodes.intervalfn.Within; +import org.apache.lucene.queryparser.flexible.standard.nodes.IntervalQueryNode; +import org.apache.lucene.queryparser.flexible.standard.nodes.MinShouldMatchNode; import org.apache.lucene.queryparser.flexible.standard.nodes.RegexpQueryNode; import org.apache.lucene.queryparser.charstream.CharStream; import org.apache.lucene.queryparser.charstream.FastCharStream; +import org.apache.lucene.queryparser.flexible.standard.nodes.TermRangeQueryNode; import static org.apache.lucene.queryparser.flexible.standard.parser.EscapeQuerySyntaxImpl.discardEscapeChar; @@ -77,6 +101,14 @@ import static org.apache.lucene.queryparser.flexible.standard.parser.EscapeQuery } } + public static float parseFloat(Token token) { + return Float.parseFloat(token.image); + } + + public static int parseInt(Token token) { + return Integer.parseInt(token.image); + } + final public QueryNode TopLevelQuery(CharSequence field) throws ParseException {QueryNode q; q = Query(field); jj_consume_token(0); @@ -92,15 +124,16 @@ import static org.apache.lucene.queryparser.flexible.standard.parser.EscapeQuery clauses.add(node); switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) { case NOT: + case FN_PREFIX: case PLUS: case MINUS: - case LPAREN: case QUOTED: case NUMBER: case TERM: case REGEXPTERM: case RANGEIN_START: - case RANGEEX_START:{ + case RANGEEX_START: + case LPAREN:{ ; break; } @@ -221,17 +254,18 @@ if (modifier != ModifierQueryNode.Modifier.MOD_NONE) { } final private QueryNode Clause(CharSequence field) throws ParseException {QueryNode q; - if (jj_2_2(2)) { + if (jj_2_3(2)) { q = FieldRangeExpr(field); } else { switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) { - case LPAREN: + case FN_PREFIX: case QUOTED: case NUMBER: case TERM: case REGEXPTERM: case RANGEIN_START: - case RANGEEX_START:{ + case RANGEEX_START: + case LPAREN:{ if (jj_2_1(2)) { field = FieldName(); switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) { @@ -251,24 +285,26 @@ if (modifier != ModifierQueryNode.Modifier.MOD_NONE) { } else { ; } - switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) { - case QUOTED: - case NUMBER: - case TERM: - case REGEXPTERM: - case RANGEIN_START: - case RANGEEX_START:{ + if (jj_2_2(2)) { q = Term(field); - break; + } else { + switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) { + case LPAREN:{ + q = GroupingExpr(field); + break; + } + case FN_PREFIX: + case QUOTED: + case NUMBER: + case TERM:{ + q = IntervalExpr(field); + break; + } + default: + jj_la1[7] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); } - case LPAREN:{ - q = GroupingExpr(field); - break; - } - default: - jj_la1[7] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); } break; } @@ -288,8 +324,8 @@ if (modifier != ModifierQueryNode.Modifier.MOD_NONE) { throw new Error("Missing return statement in function"); } - final private GroupQueryNode GroupingExpr(CharSequence field) throws ParseException {QueryNode q; - Token boost; + final private QueryNode GroupingExpr(CharSequence field) throws ParseException {QueryNode q; + Token boost, minShouldMatch = null; jj_consume_token(LPAREN); q = Query(field); jj_consume_token(RPAREN); @@ -302,27 +338,497 @@ if (modifier != ModifierQueryNode.Modifier.MOD_NONE) { jj_la1[9] = jj_gen; ; } -{if ("" != null) return new GroupQueryNode(q);} + switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) { + case 55:{ + jj_consume_token(55); + minShouldMatch = jj_consume_token(NUMBER); + break; + } + default: + jj_la1[10] = jj_gen; + ; + } +if (minShouldMatch != null) { + q = new MinShouldMatchNode(parseInt(minShouldMatch), new GroupQueryNode(q)); + } else { + q = new GroupQueryNode(q); + } + {if ("" != null) return q;} + throw new Error("Missing return statement in function"); +} + + final private IntervalQueryNode IntervalExpr(CharSequence field) throws ParseException {IntervalFunction source; + source = IntervalFun(); +{if ("" != null) return new IntervalQueryNode(field == null ? null : field.toString(), source);} + throw new Error("Missing return statement in function"); +} + + final private IntervalFunction IntervalFun() throws ParseException {IntervalFunction source; + if (jj_2_4(2)) { + source = IntervalAtLeast(); +{if ("" != null) return source;} + } else if (jj_2_5(2)) { + source = IntervalMaxWidth(); +{if ("" != null) return source;} + } else if (jj_2_6(2)) { + source = IntervalMaxGaps(); +{if ("" != null) return source;} + } else if (jj_2_7(2)) { + source = IntervalOrdered(); +{if ("" != null) return source;} + } else if (jj_2_8(2)) { + source = IntervalUnordered(); +{if ("" != null) return source;} + } else if (jj_2_9(2)) { + source = IntervalUnorderedNoOverlaps(); +{if ("" != null) return source;} + } else if (jj_2_10(2)) { + source = IntervalOr(); +{if ("" != null) return source;} + } else if (jj_2_11(2)) { + source = IntervalWildcard(); +{if ("" != null) return source;} + } else if (jj_2_12(2)) { + source = IntervalAfter(); +{if ("" != null) return source;} + } else if (jj_2_13(2)) { + source = IntervalBefore(); +{if ("" != null) return source;} + } else if (jj_2_14(2)) { + source = IntervalPhrase(); +{if ("" != null) return source;} + } else if (jj_2_15(2)) { + source = IntervalContaining(); +{if ("" != null) return source;} + } else if (jj_2_16(2)) { + source = IntervalNotContaining(); +{if ("" != null) return source;} + } else if (jj_2_17(2)) { + source = IntervalContainedBy(); +{if ("" != null) return source;} + } else if (jj_2_18(2)) { + source = IntervalNotContainedBy(); +{if ("" != null) return source;} + } else if (jj_2_19(2)) { + source = IntervalWithin(); +{if ("" != null) return source;} + } else if (jj_2_20(2)) { + source = IntervalNotWithin(); +{if ("" != null) return source;} + } else if (jj_2_21(2)) { + source = IntervalOverlapping(); +{if ("" != null) return source;} + } else if (jj_2_22(2)) { + source = IntervalNonOverlapping(); +{if ("" != null) return source;} + } else if (jj_2_23(2)) { + source = IntervalExtend(); +{if ("" != null) return source;} + } else if (jj_2_24(2)) { + source = IntervalText(); +{if ("" != null) return source;} + } else { + jj_consume_token(-1); + throw new ParseException(); + } + throw new Error("Missing return statement in function"); +} + + final private IntervalFunction IntervalAtLeast() throws ParseException {IntervalFunction source; + ArrayList sources = new ArrayList(); + Token minShouldMatch; + jj_consume_token(FN_PREFIX); + jj_consume_token(ATLEAST); + jj_consume_token(LPAREN); + minShouldMatch = jj_consume_token(NUMBER); + label_4: + while (true) { + source = IntervalFun(); +sources.add(source); + switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) { + case FN_PREFIX: + case QUOTED: + case NUMBER: + case TERM:{ + ; + break; + } + default: + jj_la1[11] = jj_gen; + break label_4; + } + } + jj_consume_token(RPAREN); +{if ("" != null) return new AtLeast(parseInt(minShouldMatch), sources);} + throw new Error("Missing return statement in function"); +} + + final private IntervalFunction IntervalMaxWidth() throws ParseException {IntervalFunction source; + Token maxWidth; + jj_consume_token(FN_PREFIX); + jj_consume_token(MAXWIDTH); + jj_consume_token(LPAREN); + maxWidth = jj_consume_token(NUMBER); + source = IntervalFun(); + jj_consume_token(RPAREN); +{if ("" != null) return new MaxWidth(parseInt(maxWidth), source);} + throw new Error("Missing return statement in function"); +} + + final private IntervalFunction IntervalMaxGaps() throws ParseException {IntervalFunction source; + Token maxGaps; + jj_consume_token(FN_PREFIX); + jj_consume_token(MAXGAPS); + jj_consume_token(LPAREN); + maxGaps = jj_consume_token(NUMBER); + source = IntervalFun(); + jj_consume_token(RPAREN); +{if ("" != null) return new MaxGaps(parseInt(maxGaps), source);} + throw new Error("Missing return statement in function"); +} + + final private IntervalFunction IntervalUnordered() throws ParseException {IntervalFunction source; + ArrayList sources = new ArrayList(); + jj_consume_token(FN_PREFIX); + jj_consume_token(UNORDERED); + jj_consume_token(LPAREN); + label_5: + while (true) { + source = IntervalFun(); +sources.add(source); + switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) { + case FN_PREFIX: + case QUOTED: + case NUMBER: + case TERM:{ + ; + break; + } + default: + jj_la1[12] = jj_gen; + break label_5; + } + } + jj_consume_token(RPAREN); +{if ("" != null) return new Unordered(sources);} + throw new Error("Missing return statement in function"); +} + + final private IntervalFunction IntervalUnorderedNoOverlaps() throws ParseException {IntervalFunction a, b; + jj_consume_token(FN_PREFIX); + jj_consume_token(UNORDERED_NO_OVERLAPS); + jj_consume_token(LPAREN); + a = IntervalFun(); + b = IntervalFun(); + jj_consume_token(RPAREN); +{if ("" != null) return new UnorderedNoOverlaps(a, b);} + throw new Error("Missing return statement in function"); +} + + final private IntervalFunction IntervalOrdered() throws ParseException {IntervalFunction source; + ArrayList sources = new ArrayList(); + jj_consume_token(FN_PREFIX); + jj_consume_token(ORDERED); + jj_consume_token(LPAREN); + label_6: + while (true) { + source = IntervalFun(); +sources.add(source); + switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) { + case FN_PREFIX: + case QUOTED: + case NUMBER: + case TERM:{ + ; + break; + } + default: + jj_la1[13] = jj_gen; + break label_6; + } + } + jj_consume_token(RPAREN); +{if ("" != null) return new Ordered(sources);} + throw new Error("Missing return statement in function"); +} + + final private IntervalFunction IntervalOr() throws ParseException {IntervalFunction source; + ArrayList sources = new ArrayList(); + jj_consume_token(FN_PREFIX); + jj_consume_token(FN_OR); + jj_consume_token(LPAREN); + label_7: + while (true) { + source = IntervalFun(); +sources.add(source); + switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) { + case FN_PREFIX: + case QUOTED: + case NUMBER: + case TERM:{ + ; + break; + } + default: + jj_la1[14] = jj_gen; + break label_7; + } + } + jj_consume_token(RPAREN); +{if ("" != null) return new Or(sources);} + throw new Error("Missing return statement in function"); +} + + final private IntervalFunction IntervalPhrase() throws ParseException {IntervalFunction source; + ArrayList sources = new ArrayList(); + jj_consume_token(FN_PREFIX); + jj_consume_token(PHRASE); + jj_consume_token(LPAREN); + label_8: + while (true) { + source = IntervalFun(); +sources.add(source); + switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) { + case FN_PREFIX: + case QUOTED: + case NUMBER: + case TERM:{ + ; + break; + } + default: + jj_la1[15] = jj_gen; + break label_8; + } + } + jj_consume_token(RPAREN); +{if ("" != null) return new Phrase(sources);} + throw new Error("Missing return statement in function"); +} + + final private IntervalFunction IntervalBefore() throws ParseException {IntervalFunction source; + IntervalFunction reference; + jj_consume_token(FN_PREFIX); + jj_consume_token(BEFORE); + jj_consume_token(LPAREN); + source = IntervalFun(); + reference = IntervalFun(); + jj_consume_token(RPAREN); +{if ("" != null) return new Before(source, reference);} + throw new Error("Missing return statement in function"); +} + + final private IntervalFunction IntervalAfter() throws ParseException {IntervalFunction source; + IntervalFunction reference; + jj_consume_token(FN_PREFIX); + jj_consume_token(AFTER); + jj_consume_token(LPAREN); + source = IntervalFun(); + reference = IntervalFun(); + jj_consume_token(RPAREN); +{if ("" != null) return new After(source, reference);} + throw new Error("Missing return statement in function"); +} + + final private IntervalFunction IntervalContaining() throws ParseException {IntervalFunction big; + IntervalFunction small; + jj_consume_token(FN_PREFIX); + jj_consume_token(CONTAINING); + jj_consume_token(LPAREN); + big = IntervalFun(); + small = IntervalFun(); + jj_consume_token(RPAREN); +{if ("" != null) return new Containing(big, small);} + throw new Error("Missing return statement in function"); +} + + final private IntervalFunction IntervalNotContaining() throws ParseException {IntervalFunction minuend; + IntervalFunction subtrahend; + jj_consume_token(FN_PREFIX); + jj_consume_token(NOT_CONTAINING); + jj_consume_token(LPAREN); + minuend = IntervalFun(); + subtrahend = IntervalFun(); + jj_consume_token(RPAREN); +{if ("" != null) return new NotContaining(minuend, subtrahend);} + throw new Error("Missing return statement in function"); +} + + final private IntervalFunction IntervalContainedBy() throws ParseException {IntervalFunction big; + IntervalFunction small; + jj_consume_token(FN_PREFIX); + jj_consume_token(CONTAINED_BY); + jj_consume_token(LPAREN); + small = IntervalFun(); + big = IntervalFun(); + jj_consume_token(RPAREN); +{if ("" != null) return new ContainedBy(small, big);} + throw new Error("Missing return statement in function"); +} + + final private IntervalFunction IntervalNotContainedBy() throws ParseException {IntervalFunction big; + IntervalFunction small; + jj_consume_token(FN_PREFIX); + jj_consume_token(NOT_CONTAINED_BY); + jj_consume_token(LPAREN); + small = IntervalFun(); + big = IntervalFun(); + jj_consume_token(RPAREN); +{if ("" != null) return new NotContainedBy(small, big);} + throw new Error("Missing return statement in function"); +} + + final private IntervalFunction IntervalWithin() throws ParseException {IntervalFunction source, reference; + Token positions; + jj_consume_token(FN_PREFIX); + jj_consume_token(WITHIN); + jj_consume_token(LPAREN); + source = IntervalFun(); + positions = jj_consume_token(NUMBER); + reference = IntervalFun(); + jj_consume_token(RPAREN); +{if ("" != null) return new Within(source, parseInt(positions), reference);} + throw new Error("Missing return statement in function"); +} + + final private IntervalFunction IntervalExtend() throws ParseException {IntervalFunction source; + Token before, after; + jj_consume_token(FN_PREFIX); + jj_consume_token(EXTEND); + jj_consume_token(LPAREN); + source = IntervalFun(); + before = jj_consume_token(NUMBER); + after = jj_consume_token(NUMBER); + jj_consume_token(RPAREN); +{if ("" != null) return new Extend(source, parseInt(before), parseInt(after));} + throw new Error("Missing return statement in function"); +} + + final private IntervalFunction IntervalNotWithin() throws ParseException {IntervalFunction minuend, subtrahend; + Token positions; + jj_consume_token(FN_PREFIX); + jj_consume_token(NOT_WITHIN); + jj_consume_token(LPAREN); + minuend = IntervalFun(); + positions = jj_consume_token(NUMBER); + subtrahend = IntervalFun(); + jj_consume_token(RPAREN); +{if ("" != null) return new NotWithin(minuend, parseInt(positions), subtrahend);} + throw new Error("Missing return statement in function"); +} + + final private IntervalFunction IntervalOverlapping() throws ParseException {IntervalFunction source, reference; + jj_consume_token(FN_PREFIX); + jj_consume_token(OVERLAPPING); + jj_consume_token(LPAREN); + source = IntervalFun(); + reference = IntervalFun(); + jj_consume_token(RPAREN); +{if ("" != null) return new Overlapping(source, reference);} + throw new Error("Missing return statement in function"); +} + + final private IntervalFunction IntervalNonOverlapping() throws ParseException {IntervalFunction minuend, subtrahend; + jj_consume_token(FN_PREFIX); + jj_consume_token(NON_OVERLAPPING); + jj_consume_token(LPAREN); + minuend = IntervalFun(); + subtrahend = IntervalFun(); + jj_consume_token(RPAREN); +{if ("" != null) return new NonOverlapping(minuend, subtrahend);} + throw new Error("Missing return statement in function"); +} + + final private IntervalFunction IntervalWildcard() throws ParseException {String wildcard; + jj_consume_token(FN_PREFIX); + jj_consume_token(WILDCARD); + jj_consume_token(LPAREN); + switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) { + case NUMBER: + case TERM:{ + switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) { + case TERM:{ + jj_consume_token(TERM); + break; + } + case NUMBER:{ + jj_consume_token(NUMBER); + break; + } + default: + jj_la1[16] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } +wildcard = token.image; + break; + } + case QUOTED:{ + jj_consume_token(QUOTED); +wildcard = token.image.substring(1, token.image.length() - 1); + break; + } + default: + jj_la1[17] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + jj_consume_token(RPAREN); +{if ("" != null) return new Wildcard(wildcard);} + throw new Error("Missing return statement in function"); +} + + final private IntervalFunction IntervalText() throws ParseException { + switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) { + case QUOTED:{ + jj_consume_token(QUOTED); +{if ("" != null) return new AnalyzedText(token.image.substring(1, token.image.length() - 1));} + break; + } + case NUMBER: + case TERM:{ + switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) { + case TERM:{ + jj_consume_token(TERM); + break; + } + case NUMBER:{ + jj_consume_token(NUMBER); + break; + } + default: + jj_la1[18] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } +{if ("" != null) return new AnalyzedText(token.image);} + break; + } + default: + jj_la1[19] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } throw new Error("Missing return statement in function"); } final private QueryNode Boost(QueryNode node) throws ParseException {Token boost; jj_consume_token(CARAT); boost = jj_consume_token(NUMBER); -{if ("" != null) return node == null ? node : new BoostQueryNode(node, Float.parseFloat(boost.image));} +{if ("" != null) return node == null ? node : new BoostQueryNode(node, parseFloat(boost));} throw new Error("Missing return statement in function"); } final private QueryNode FuzzyOp(CharSequence field, Token term, QueryNode node) throws ParseException {Token similarity = null; jj_consume_token(TILDE); - if (jj_2_3(2)) { + if (jj_2_25(2)) { similarity = jj_consume_token(NUMBER); } else { ; } float fms = org.apache.lucene.search.FuzzyQuery.defaultMaxEdits; if (similarity != null) { - fms = Float.parseFloat(similarity.image); + fms = parseFloat(similarity); if (fms < 0.0f) { {if (true) throw new ParseException(new MessageImpl(QueryParserMessages.INVALID_SYNTAX_FUZZY_LIMITS));} } else if (fms >= 1.0f && fms != (int) fms) { @@ -355,7 +861,7 @@ float fms = org.apache.lucene.search.FuzzyQuery.defaultMaxEdits; break; } default: - jj_la1[10] = jj_gen; + jj_la1[20] = jj_gen; jj_consume_token(-1); throw new ParseException(); } @@ -374,7 +880,7 @@ operator = token; break; } default: - jj_la1[11] = jj_gen; + jj_la1[21] = jj_gen; jj_consume_token(-1); throw new ParseException(); } @@ -419,7 +925,8 @@ if (term.kind == QUOTED) { switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) { case REGEXPTERM:{ term = jj_consume_token(REGEXPTERM); -q = new RegexpQueryNode(field, term.image.substring(1, term.image.length() - 1)); +String v = term.image.substring(1, term.image.length() - 1); + q = new RegexpQueryNode(field, v, 0, v.length()); break; } case NUMBER: @@ -434,7 +941,7 @@ q = new RegexpQueryNode(field, term.image.substring(1, term.image.length() - 1)) break; } default: - jj_la1[12] = jj_gen; + jj_la1[22] = jj_gen; jj_consume_token(-1); throw new ParseException(); } @@ -445,7 +952,7 @@ q = new FieldQueryNode(field, discardEscapeChar(term.image), term.beginColumn, t break; } default: - jj_la1[13] = jj_gen; + jj_la1[23] = jj_gen; ; } break; @@ -460,7 +967,7 @@ q = new FieldQueryNode(field, discardEscapeChar(term.image), term.beginColumn, t break; } default: - jj_la1[14] = jj_gen; + jj_la1[24] = jj_gen; jj_consume_token(-1); throw new ParseException(); } @@ -470,7 +977,7 @@ q = new FieldQueryNode(field, discardEscapeChar(term.image), term.beginColumn, t break; } default: - jj_la1[15] = jj_gen; + jj_la1[25] = jj_gen; ; } {if ("" != null) return q;} @@ -486,11 +993,11 @@ String image = term.image.substring(1, term.image.length() - 1); case TILDE:{ jj_consume_token(TILDE); slop = jj_consume_token(NUMBER); -q = new SlopQueryNode(q, (int) Float.parseFloat(slop.image)); +q = new SlopQueryNode(q, parseInt(slop)); break; } default: - jj_la1[16] = jj_gen; + jj_la1[26] = jj_gen; ; } {if ("" != null) return q;} @@ -511,7 +1018,7 @@ leftInclusive = true; break; } default: - jj_la1[17] = jj_gen; + jj_la1[27] = jj_gen; jj_consume_token(-1); throw new ParseException(); } @@ -529,7 +1036,7 @@ leftInclusive = true; break; } default: - jj_la1[18] = jj_gen; + jj_la1[28] = jj_gen; jj_consume_token(-1); throw new ParseException(); } @@ -549,7 +1056,7 @@ left = token; break; } default: - jj_la1[19] = jj_gen; + jj_la1[29] = jj_gen; jj_consume_token(-1); throw new ParseException(); } @@ -565,7 +1072,7 @@ rightInclusive = true; break; } default: - jj_la1[20] = jj_gen; + jj_la1[30] = jj_gen; jj_consume_token(-1); throw new ParseException(); } @@ -609,21 +1116,314 @@ if (left.kind == RANGE_QUOTED) { finally { jj_save(2, xla); } } - private boolean jj_3R_4() + private boolean jj_2_4(int xla) + { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return (!jj_3_4()); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(3, xla); } + } + + private boolean jj_2_5(int xla) + { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return (!jj_3_5()); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(4, xla); } + } + + private boolean jj_2_6(int xla) + { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return (!jj_3_6()); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(5, xla); } + } + + private boolean jj_2_7(int xla) + { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return (!jj_3_7()); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(6, xla); } + } + + private boolean jj_2_8(int xla) + { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return (!jj_3_8()); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(7, xla); } + } + + private boolean jj_2_9(int xla) + { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return (!jj_3_9()); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(8, xla); } + } + + private boolean jj_2_10(int xla) + { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return (!jj_3_10()); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(9, xla); } + } + + private boolean jj_2_11(int xla) + { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return (!jj_3_11()); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(10, xla); } + } + + private boolean jj_2_12(int xla) + { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return (!jj_3_12()); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(11, xla); } + } + + private boolean jj_2_13(int xla) + { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return (!jj_3_13()); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(12, xla); } + } + + private boolean jj_2_14(int xla) + { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return (!jj_3_14()); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(13, xla); } + } + + private boolean jj_2_15(int xla) + { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return (!jj_3_15()); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(14, xla); } + } + + private boolean jj_2_16(int xla) + { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return (!jj_3_16()); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(15, xla); } + } + + private boolean jj_2_17(int xla) + { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return (!jj_3_17()); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(16, xla); } + } + + private boolean jj_2_18(int xla) + { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return (!jj_3_18()); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(17, xla); } + } + + private boolean jj_2_19(int xla) + { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return (!jj_3_19()); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(18, xla); } + } + + private boolean jj_2_20(int xla) + { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return (!jj_3_20()); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(19, xla); } + } + + private boolean jj_2_21(int xla) + { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return (!jj_3_21()); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(20, xla); } + } + + private boolean jj_2_22(int xla) + { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return (!jj_3_22()); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(21, xla); } + } + + private boolean jj_2_23(int xla) + { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return (!jj_3_23()); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(22, xla); } + } + + private boolean jj_2_24(int xla) + { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return (!jj_3_24()); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(23, xla); } + } + + private boolean jj_2_25(int xla) + { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return (!jj_3_25()); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(24, xla); } + } + + private boolean jj_3R_39() + { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(25)) { + jj_scanpos = xsp; + if (jj_scan_token(24)) return true; + } + return false; + } + + private boolean jj_3R_38() + { + if (jj_scan_token(QUOTED)) return true; + return false; + } + + private boolean jj_3R_32() + { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_38()) { + jj_scanpos = xsp; + if (jj_3R_39()) return true; + } + return false; + } + + private boolean jj_3R_26() + { + if (jj_scan_token(FN_PREFIX)) return true; + if (jj_scan_token(NOT_CONTAINED_BY)) return true; + return false; + } + + private boolean jj_3R_17() + { + if (jj_scan_token(FN_PREFIX)) return true; + if (jj_scan_token(UNORDERED_NO_OVERLAPS)) return true; + return false; + } + + private boolean jj_3R_25() + { + if (jj_scan_token(FN_PREFIX)) return true; + if (jj_scan_token(CONTAINED_BY)) return true; + return false; + } + + private boolean jj_3R_19() + { + if (jj_scan_token(FN_PREFIX)) return true; + if (jj_scan_token(WILDCARD)) return true; + return false; + } + + private boolean jj_3R_45() + { + if (jj_scan_token(RANGEIN_START)) return true; + return false; + } + + private boolean jj_3R_16() + { + if (jj_scan_token(FN_PREFIX)) return true; + if (jj_scan_token(UNORDERED)) return true; + return false; + } + + private boolean jj_3R_41() + { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_45()) { + jj_scanpos = xsp; + if (jj_scan_token(28)) return true; + } + xsp = jj_scanpos; + if (jj_scan_token(54)) { + jj_scanpos = xsp; + if (jj_scan_token(53)) { + jj_scanpos = xsp; + if (jj_scan_token(50)) return true; + } + } + return false; + } + + private boolean jj_3R_24() + { + if (jj_scan_token(FN_PREFIX)) return true; + if (jj_scan_token(NOT_CONTAINING)) return true; + return false; + } + + private boolean jj_3R_30() + { + if (jj_scan_token(FN_PREFIX)) return true; + if (jj_scan_token(NON_OVERLAPPING)) return true; + return false; + } + + private boolean jj_3R_14() + { + if (jj_scan_token(FN_PREFIX)) return true; + if (jj_scan_token(MAXGAPS)) return true; + return false; + } + + private boolean jj_3R_23() + { + if (jj_scan_token(FN_PREFIX)) return true; + if (jj_scan_token(CONTAINING)) return true; + return false; + } + + private boolean jj_3R_9() { if (jj_scan_token(TERM)) return true; return false; } - private boolean jj_3_2() + private boolean jj_3R_29() { - if (jj_3R_5()) return true; + if (jj_scan_token(FN_PREFIX)) return true; + if (jj_scan_token(OVERLAPPING)) return true; return false; } - private boolean jj_3R_5() + private boolean jj_3R_11() { - if (jj_3R_4()) return true; + if (jj_3R_9()) return true; Token xsp; xsp = jj_scanpos; if (jj_scan_token(17)) { @@ -639,15 +1439,21 @@ if (left.kind == RANGE_QUOTED) { return false; } - private boolean jj_3_3() + private boolean jj_3R_46() { - if (jj_scan_token(NUMBER)) return true; + if (jj_scan_token(TILDE)) return true; + return false; + } + + private boolean jj_3_2() + { + if (jj_3R_10()) return true; return false; } private boolean jj_3_1() { - if (jj_3R_4()) return true; + if (jj_3R_9()) return true; Token xsp; xsp = jj_scanpos; if (jj_scan_token(15)) { @@ -657,6 +1463,297 @@ if (left.kind == RANGE_QUOTED) { return false; } + private boolean jj_3R_13() + { + if (jj_scan_token(FN_PREFIX)) return true; + if (jj_scan_token(MAXWIDTH)) return true; + return false; + } + + private boolean jj_3R_20() + { + if (jj_scan_token(FN_PREFIX)) return true; + if (jj_scan_token(AFTER)) return true; + return false; + } + + private boolean jj_3_3() + { + if (jj_3R_11()) return true; + return false; + } + + private boolean jj_3R_42() + { + if (jj_scan_token(QUOTED)) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_46()) jj_scanpos = xsp; + return false; + } + + private boolean jj_3R_28() + { + if (jj_scan_token(FN_PREFIX)) return true; + if (jj_scan_token(NOT_WITHIN)) return true; + return false; + } + + private boolean jj_3R_21() + { + if (jj_scan_token(FN_PREFIX)) return true; + if (jj_scan_token(BEFORE)) return true; + return false; + } + + private boolean jj_3R_12() + { + if (jj_scan_token(FN_PREFIX)) return true; + if (jj_scan_token(ATLEAST)) return true; + return false; + } + + private boolean jj_3_25() + { + if (jj_scan_token(NUMBER)) return true; + return false; + } + + private boolean jj_3_24() + { + if (jj_3R_32()) return true; + return false; + } + + private boolean jj_3_23() + { + if (jj_3R_31()) return true; + return false; + } + + private boolean jj_3R_40() + { + if (jj_3R_44()) return true; + return false; + } + + private boolean jj_3R_36() + { + if (jj_3R_42()) return true; + return false; + } + + private boolean jj_3_22() + { + if (jj_3R_30()) return true; + return false; + } + + private boolean jj_3R_44() + { + if (jj_scan_token(TILDE)) return true; + return false; + } + + private boolean jj_3R_37() + { + if (jj_3R_43()) return true; + return false; + } + + private boolean jj_3R_35() + { + if (jj_3R_41()) return true; + return false; + } + + private boolean jj_3_21() + { + if (jj_3R_29()) return true; + return false; + } + + private boolean jj_3_20() + { + if (jj_3R_28()) return true; + return false; + } + + private boolean jj_3R_22() + { + if (jj_scan_token(FN_PREFIX)) return true; + if (jj_scan_token(PHRASE)) return true; + return false; + } + + private boolean jj_3_19() + { + if (jj_3R_27()) return true; + return false; + } + + private boolean jj_3R_34() + { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(25)) { + jj_scanpos = xsp; + if (jj_scan_token(24)) return true; + } + xsp = jj_scanpos; + if (jj_3R_40()) jj_scanpos = xsp; + return false; + } + + private boolean jj_3R_31() + { + if (jj_scan_token(FN_PREFIX)) return true; + if (jj_scan_token(EXTEND)) return true; + return false; + } + + private boolean jj_3_18() + { + if (jj_3R_26()) return true; + return false; + } + + private boolean jj_3_17() + { + if (jj_3R_25()) return true; + return false; + } + + private boolean jj_3_16() + { + if (jj_3R_24()) return true; + return false; + } + + private boolean jj_3_15() + { + if (jj_3R_23()) return true; + return false; + } + + private boolean jj_3_14() + { + if (jj_3R_22()) return true; + return false; + } + + private boolean jj_3R_33() + { + if (jj_scan_token(REGEXPTERM)) return true; + return false; + } + + private boolean jj_3_13() + { + if (jj_3R_21()) return true; + return false; + } + + private boolean jj_3_12() + { + if (jj_3R_20()) return true; + return false; + } + + private boolean jj_3_11() + { + if (jj_3R_19()) return true; + return false; + } + + private boolean jj_3_10() + { + if (jj_3R_18()) return true; + return false; + } + + private boolean jj_3_9() + { + if (jj_3R_17()) return true; + return false; + } + + private boolean jj_3_8() + { + if (jj_3R_16()) return true; + return false; + } + + private boolean jj_3_7() + { + if (jj_3R_15()) return true; + return false; + } + + private boolean jj_3R_18() + { + if (jj_scan_token(FN_PREFIX)) return true; + if (jj_scan_token(FN_OR)) return true; + return false; + } + + private boolean jj_3R_10() + { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_33()) { + jj_scanpos = xsp; + if (jj_3R_34()) { + jj_scanpos = xsp; + if (jj_3R_35()) { + jj_scanpos = xsp; + if (jj_3R_36()) return true; + } + } + } + xsp = jj_scanpos; + if (jj_3R_37()) jj_scanpos = xsp; + return false; + } + + private boolean jj_3_6() + { + if (jj_3R_14()) return true; + return false; + } + + private boolean jj_3_5() + { + if (jj_3R_13()) return true; + return false; + } + + private boolean jj_3R_43() + { + if (jj_scan_token(CARAT)) return true; + return false; + } + + private boolean jj_3_4() + { + if (jj_3R_12()) return true; + return false; + } + + private boolean jj_3R_27() + { + if (jj_scan_token(FN_PREFIX)) return true; + if (jj_scan_token(WITHIN)) return true; + return false; + } + + private boolean jj_3R_15() + { + if (jj_scan_token(FN_PREFIX)) return true; + if (jj_scan_token(ORDERED)) return true; + return false; + } + /** Generated Token Manager. */ public StandardSyntaxParserTokenManager token_source; /** Current token. */ @@ -667,7 +1764,7 @@ if (left.kind == RANGE_QUOTED) { private Token jj_scanpos, jj_lastpos; private int jj_la; private int jj_gen; - final private int[] jj_la1 = new int[21]; + final private int[] jj_la1 = new int[31]; static private int[] jj_la1_0; static private int[] jj_la1_1; static { @@ -675,12 +1772,12 @@ if (left.kind == RANGE_QUOTED) { jj_la1_init_1(); } private static void jj_la1_init_0() { - jj_la1_0 = new int[] {0x1f803c00,0x200,0x100,0x1400,0x1c00,0x1c00,0x18000,0x1f802000,0x1f802000,0x200000,0x1e0000,0x3800000,0x3000000,0x400000,0x1f800000,0x200000,0x400000,0x18000000,0x20000000,0x20000000,0xc0000000,}; + jj_la1_0 = new int[] {0x3f803c00,0x200,0x100,0x2400,0x3400,0x3400,0x18000,0x23800800,0x3f800800,0x200000,0x0,0x3800800,0x3800800,0x3800800,0x3800800,0x3800800,0x3000000,0x3800000,0x3000000,0x3800000,0x1e0000,0x3800000,0x3000000,0x400000,0x1f800000,0x200000,0x400000,0x18000000,0x0,0x0,0x0,}; } private static void jj_la1_init_1() { - jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x3,0x0,}; + jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x640000,0x640000,0x180000,}; } - final private JJCalls[] jj_2_rtns = new JJCalls[3]; + final private JJCalls[] jj_2_rtns = new JJCalls[25]; private boolean jj_rescan = false; private int jj_gc = 0; @@ -690,7 +1787,7 @@ if (left.kind == RANGE_QUOTED) { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 21; i++) jj_la1[i] = -1; + for (int i = 0; i < 31; i++) jj_la1[i] = -1; for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } @@ -700,7 +1797,7 @@ if (left.kind == RANGE_QUOTED) { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 21; i++) jj_la1[i] = -1; + for (int i = 0; i < 31; i++) jj_la1[i] = -1; for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } @@ -710,7 +1807,7 @@ if (left.kind == RANGE_QUOTED) { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 21; i++) jj_la1[i] = -1; + for (int i = 0; i < 31; i++) jj_la1[i] = -1; for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } @@ -720,7 +1817,7 @@ if (left.kind == RANGE_QUOTED) { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 21; i++) jj_la1[i] = -1; + for (int i = 0; i < 31; i++) jj_la1[i] = -1; for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } @@ -846,12 +1943,12 @@ if (left.kind == RANGE_QUOTED) { /** Generate ParseException. */ public ParseException generateParseException() { jj_expentries.clear(); - boolean[] la1tokens = new boolean[34]; + boolean[] la1tokens = new boolean[56]; if (jj_kind >= 0) { la1tokens[jj_kind] = true; jj_kind = -1; } - for (int i = 0; i < 21; i++) { + for (int i = 0; i < 31; i++) { if (jj_la1[i] == jj_gen) { for (int j = 0; j < 32; j++) { if ((jj_la1_0[i] & (1< // Every character that follows a backslash is considered as an escaped character | <#_ESCAPED_CHAR: "\\" ~[] > - | <#_TERM_START_CHAR: ( ~[ " ", "\t", "\n", "\r", "\u3000", "+", "-", "!", "(", ")", ":", "^", - "<", ">", "=", "[", "]", "\"", "{", "}", "~", "\\", "/" ] + | <#_TERM_START_CHAR: ( ~[ " ", "\t", "\n", "\r", "\u3000", "+", "-", "!", "(", ")", ":", "^", "@", + "<", ">", "=", "[", "]", "\"", "{", "}", "~", "\\", "/"] | <_ESCAPED_CHAR> ) > | <#_TERM_CHAR: ( <_TERM_START_CHAR> | <_ESCAPED_CHAR> | "-" | "+" ) > | <#_WHITESPACE: ( " " | "\t" | "\n" | "\r" | "\u3000") > | <#_QUOTED_CHAR: ( ~[ "\"", "\\" ] | <_ESCAPED_CHAR> ) > } - SKIP : { + SKIP : { < <_WHITESPACE> > } @@ -112,9 +144,9 @@ PARSER_END(StandardSyntaxParser) | | + | : Function | | - | | | | : Range } + TOKEN : { + : DEFAULT +} + + TOKEN : { + + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | +} + TOKEN : { | : DEFAULT @@ -265,7 +324,8 @@ private QueryNode Clause(CharSequence field) : { { ( LOOKAHEAD(2) q = FieldRangeExpr(field) - | (LOOKAHEAD(2) field = FieldName() ( | ))? ( q = Term(field) | q = GroupingExpr(field)) + | (LOOKAHEAD(2) field = FieldName() ( | ))? + (LOOKAHEAD(2) q = Term(field) | q = GroupingExpr(field) | q = IntervalExpr(field)) ) { return q; @@ -289,17 +349,316 @@ private CharSequence FieldName() : { * GroupingExpr ::= '(' Query ')' ('^' )? * } */ -private GroupQueryNode GroupingExpr(CharSequence field) : { +private QueryNode GroupingExpr(CharSequence field) : { QueryNode q; - Token boost; + Token boost, minShouldMatch = null; } { - q = Query(field) (q = Boost(q))? + q = Query(field) (q = Boost(q))? ("@" minShouldMatch = )? { - return new GroupQueryNode(q); + if (minShouldMatch != null) { + q = new MinShouldMatchNode(parseInt(minShouldMatch), new GroupQueryNode(q)); + } else { + q = new GroupQueryNode(q); + } + return q; } } + +/** + * An interval expression (functions) node. + */ +private IntervalQueryNode IntervalExpr(CharSequence field) : { + IntervalFunction source; +} + { + source = IntervalFun() + { + return new IntervalQueryNode(field == null ? null : field.toString(), source); + } + } + +private IntervalFunction IntervalFun() : { + IntervalFunction source; +} +{ + LOOKAHEAD(2) source = IntervalAtLeast() { return source; } + | LOOKAHEAD(2) source = IntervalMaxWidth() { return source; } + | LOOKAHEAD(2) source = IntervalMaxGaps() { return source; } + | LOOKAHEAD(2) source = IntervalOrdered() { return source; } + | LOOKAHEAD(2) source = IntervalUnordered() { return source; } + | LOOKAHEAD(2) source = IntervalUnorderedNoOverlaps() { return source; } + | LOOKAHEAD(2) source = IntervalOr() { return source; } + | LOOKAHEAD(2) source = IntervalWildcard() { return source; } + | LOOKAHEAD(2) source = IntervalAfter() { return source; } + | LOOKAHEAD(2) source = IntervalBefore() { return source; } + | LOOKAHEAD(2) source = IntervalPhrase() { return source; } + | LOOKAHEAD(2) source = IntervalContaining() { return source; } + | LOOKAHEAD(2) source = IntervalNotContaining() { return source; } + | LOOKAHEAD(2) source = IntervalContainedBy() { return source; } + | LOOKAHEAD(2) source = IntervalNotContainedBy() { return source; } + | LOOKAHEAD(2) source = IntervalWithin() { return source; } + | LOOKAHEAD(2) source = IntervalNotWithin() { return source; } + | LOOKAHEAD(2) source = IntervalOverlapping() { return source; } + | LOOKAHEAD(2) source = IntervalNonOverlapping() { return source; } + | LOOKAHEAD(2) source = IntervalExtend() { return source; } + | LOOKAHEAD(2) source = IntervalText() { return source; } +} + +private IntervalFunction IntervalAtLeast() : { + IntervalFunction source; + ArrayList sources = new ArrayList(); + Token minShouldMatch; +} +{ + + minShouldMatch = (source = IntervalFun() { sources.add(source); })+ + { + return new AtLeast(parseInt(minShouldMatch), sources); + } +} + +private IntervalFunction IntervalMaxWidth() : { + IntervalFunction source; + Token maxWidth; +} +{ + + maxWidth = source = IntervalFun() + { + return new MaxWidth(parseInt(maxWidth), source); + } +} + +private IntervalFunction IntervalMaxGaps() : { + IntervalFunction source; + Token maxGaps; +} +{ + + maxGaps = source = IntervalFun() + { + return new MaxGaps(parseInt(maxGaps), source); + } +} + +private IntervalFunction IntervalUnordered() : { + IntervalFunction source; + ArrayList sources = new ArrayList(); +} +{ + + (source = IntervalFun() { sources.add(source); })+ + { + return new Unordered(sources); + } +} + +private IntervalFunction IntervalUnorderedNoOverlaps() : { + IntervalFunction a, b; +} +{ + + a = IntervalFun() b = IntervalFun() + { + return new UnorderedNoOverlaps(a, b); + } +} + +private IntervalFunction IntervalOrdered() : { + IntervalFunction source; + ArrayList sources = new ArrayList(); +} +{ + + (source = IntervalFun() { sources.add(source); })+ + { + return new Ordered(sources); + } +} + +private IntervalFunction IntervalOr() : { + IntervalFunction source; + ArrayList sources = new ArrayList(); +} +{ + + (source = IntervalFun() { sources.add(source); })+ + { + return new Or(sources); + } +} + +private IntervalFunction IntervalPhrase() : { + IntervalFunction source; + ArrayList sources = new ArrayList(); +} +{ + + (source = IntervalFun() { sources.add(source); })+ + { + return new Phrase(sources); + } +} + +private IntervalFunction IntervalBefore() : { + IntervalFunction source; + IntervalFunction reference; +} +{ + source = IntervalFun() reference = IntervalFun() + { + return new Before(source, reference); + } +} + +private IntervalFunction IntervalAfter() : { + IntervalFunction source; + IntervalFunction reference; +} +{ + source = IntervalFun() reference = IntervalFun() + { + return new After(source, reference); + } +} + +private IntervalFunction IntervalContaining() : { + IntervalFunction big; + IntervalFunction small; +} +{ + big = IntervalFun() small = IntervalFun() + { + return new Containing(big, small); + } +} + +private IntervalFunction IntervalNotContaining() : { + IntervalFunction minuend; + IntervalFunction subtrahend; +} +{ + minuend = IntervalFun() subtrahend = IntervalFun() + { + return new NotContaining(minuend, subtrahend); + } +} + +private IntervalFunction IntervalContainedBy() : { + IntervalFunction big; + IntervalFunction small; +} +{ + small = IntervalFun() big = IntervalFun() + { + return new ContainedBy(small, big); + } +} + +private IntervalFunction IntervalNotContainedBy() : { + IntervalFunction big; + IntervalFunction small; +} +{ + small = IntervalFun() big = IntervalFun() + { + return new NotContainedBy(small, big); + } +} + +private IntervalFunction IntervalWithin() : { + IntervalFunction source, reference; + Token positions; +} +{ + + + source = IntervalFun() + positions = + reference = IntervalFun() + + { + return new Within(source, parseInt(positions), reference); + } +} + +private IntervalFunction IntervalExtend() : { + IntervalFunction source; + Token before, after; +} +{ + + + source = IntervalFun() + before = + after = + + { + return new Extend(source, parseInt(before), parseInt(after)); + } +} + +private IntervalFunction IntervalNotWithin() : { + IntervalFunction minuend, subtrahend; + Token positions; +} +{ + + + minuend = IntervalFun() + positions = + subtrahend = IntervalFun() + + { + return new NotWithin(minuend, parseInt(positions), subtrahend); + } +} + +private IntervalFunction IntervalOverlapping() : { + IntervalFunction source, reference; +} +{ + source = IntervalFun() reference = IntervalFun() + { + return new Overlapping(source, reference); + } +} + +private IntervalFunction IntervalNonOverlapping() : { + IntervalFunction minuend, subtrahend; +} +{ + minuend = IntervalFun() subtrahend = IntervalFun() + { + return new NonOverlapping(minuend, subtrahend); + } +} + +private IntervalFunction IntervalWildcard() : { + String wildcard; +} +{ + + + ( + ( | ) { wildcard = token.image; } + | { wildcard = token.image.substring(1, token.image.length() - 1); } + ) + + { + return new Wildcard(wildcard); + } +} + +private IntervalFunction IntervalText() : { +} +{ + () { return new AnalyzedText(token.image.substring(1, token.image.length() - 1)); } + | ( | ) { return new AnalyzedText(token.image); } +} + /** * Score boost modifier. * @@ -313,7 +672,7 @@ private QueryNode Boost(QueryNode node) : { { boost = { - return node == null ? node : new BoostQueryNode(node, Float.parseFloat(boost.image)); + return node == null ? node : new BoostQueryNode(node, parseFloat(boost)); } } @@ -332,7 +691,7 @@ private QueryNode FuzzyOp(CharSequence field, Token term, QueryNode node) : { { float fms = org.apache.lucene.search.FuzzyQuery.defaultMaxEdits; if (similarity != null) { - fms = Float.parseFloat(similarity.image); + fms = parseFloat(similarity); if (fms < 0.0f) { throw new ParseException(new MessageImpl(QueryParserMessages.INVALID_SYNTAX_FUZZY_LIMITS)); } else if (fms >= 1.0f && fms != (int) fms) { @@ -411,7 +770,10 @@ private QueryNode Term(CharSequence field) : { { ( term = - { q = new RegexpQueryNode(field, term.image.substring(1, term.image.length() - 1)); } + { + String v = term.image.substring(1, term.image.length() - 1); + q = new RegexpQueryNode(field, v, 0, v.length()); + } | (term = | term = ) { q = new FieldQueryNode(field, discardEscapeChar(term.image), term.beginColumn, term.endColumn); } ( q = FuzzyOp(field, term, q) )? @@ -442,7 +804,7 @@ private QueryNode QuotedTerm(CharSequence field) : { String image = term.image.substring(1, term.image.length() - 1); q = new QuotedFieldQueryNode(field, discardEscapeChar(image), term.beginColumn + 1, term.endColumn - 1); } - ( slop = { q = new SlopQueryNode(q, (int) Float.parseFloat(slop.image)); } )? + ( slop = { q = new SlopQueryNode(q, parseInt(slop)); } )? { return q; } diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParserConstants.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParserConstants.java index 1e07b01834b..cdd241a13fe 100644 --- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParserConstants.java +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParserConstants.java @@ -29,11 +29,11 @@ public interface StandardSyntaxParserConstants { /** RegularExpression Id. */ int NOT = 10; /** RegularExpression Id. */ - int PLUS = 11; + int FN_PREFIX = 11; /** RegularExpression Id. */ - int MINUS = 12; + int PLUS = 12; /** RegularExpression Id. */ - int LPAREN = 13; + int MINUS = 13; /** RegularExpression Id. */ int RPAREN = 14; /** RegularExpression Id. */ @@ -65,20 +65,64 @@ public interface StandardSyntaxParserConstants { /** RegularExpression Id. */ int RANGEEX_START = 28; /** RegularExpression Id. */ - int RANGE_TO = 29; + int LPAREN = 29; /** RegularExpression Id. */ - int RANGEIN_END = 30; + int ATLEAST = 30; /** RegularExpression Id. */ - int RANGEEX_END = 31; + int AFTER = 31; /** RegularExpression Id. */ - int RANGE_QUOTED = 32; + int BEFORE = 32; /** RegularExpression Id. */ - int RANGE_GOOP = 33; + int CONTAINED_BY = 33; + /** RegularExpression Id. */ + int CONTAINING = 34; + /** RegularExpression Id. */ + int EXTEND = 35; + /** RegularExpression Id. */ + int FN_OR = 36; + /** RegularExpression Id. */ + int MAXGAPS = 37; + /** RegularExpression Id. */ + int MAXWIDTH = 38; + /** RegularExpression Id. */ + int NON_OVERLAPPING = 39; + /** RegularExpression Id. */ + int NOT_CONTAINED_BY = 40; + /** RegularExpression Id. */ + int NOT_CONTAINING = 41; + /** RegularExpression Id. */ + int NOT_WITHIN = 42; + /** RegularExpression Id. */ + int ORDERED = 43; + /** RegularExpression Id. */ + int OVERLAPPING = 44; + /** RegularExpression Id. */ + int PHRASE = 45; + /** RegularExpression Id. */ + int UNORDERED = 46; + /** RegularExpression Id. */ + int UNORDERED_NO_OVERLAPS = 47; + /** RegularExpression Id. */ + int WILDCARD = 48; + /** RegularExpression Id. */ + int WITHIN = 49; + /** RegularExpression Id. */ + int RANGE_TO = 50; + /** RegularExpression Id. */ + int RANGEIN_END = 51; + /** RegularExpression Id. */ + int RANGEEX_END = 52; + /** RegularExpression Id. */ + int RANGE_QUOTED = 53; + /** RegularExpression Id. */ + int RANGE_GOOP = 54; /** Lexical state. */ - int Range = 0; + int Function = 0; /** Lexical state. */ - int DEFAULT = 1; + int Range = 1; + /** Lexical state. */ + int DEFAULT = 2; /** Literal token values. */ String[] tokenImage = { @@ -93,9 +137,9 @@ public interface StandardSyntaxParserConstants { "", "", "", + "\"fn:\"", "\"+\"", "\"-\"", - "\"(\"", "\")\"", "\":\"", "\"=\"", @@ -111,11 +155,33 @@ public interface StandardSyntaxParserConstants { "", "\"[\"", "\"{\"", + "\"(\"", + "", + "\"after\"", + "\"before\"", + "", + "\"containing\"", + "\"extend\"", + "\"or\"", + "", + "", + "", + "", + "", + "", + "\"ordered\"", + "\"overlapping\"", + "\"phrase\"", + "\"unordered\"", + "", + "\"wildcard\"", + "\"within\"", "\"TO\"", "\"]\"", "\"}\"", "", "", + "\"@\"", }; } diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParserTokenManager.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParserTokenManager.java index 3dc6507c591..e757f74ce4e 100644 --- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParserTokenManager.java +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParserTokenManager.java @@ -38,6 +38,30 @@ package org.apache.lucene.queryparser.flexible.standard.parser; + + + + + + + + + + + + + + + + + + + + + + + + @@ -52,15 +76,30 @@ package org.apache.lucene.queryparser.flexible.standard.parser; // (debugStream omitted). /** Set debug output. */ // (setDebugStream omitted). -private final int jjStopStringLiteralDfa_1(int pos, long active0){ +private final int jjStopStringLiteralDfa_2(int pos, long active0){ switch (pos) { + case 0: + if ((active0 & 0x800L) != 0L) + { + jjmatchedKind = 25; + return 32; + } + return -1; + case 1: + if ((active0 & 0x800L) != 0L) + { + jjmatchedKind = 25; + jjmatchedPos = 1; + return 32; + } + return -1; default : return -1; } } -private final int jjStartNfa_1(int pos, long active0){ - return jjMoveNfa_1(jjStopStringLiteralDfa_1(pos, active0), pos + 1); +private final int jjStartNfa_2(int pos, long active0){ + return jjMoveNfa_2(jjStopStringLiteralDfa_2(pos, active0), pos + 1); } private int jjStopAtPos(int pos, int kind) { @@ -68,43 +107,47 @@ private int jjStopAtPos(int pos, int kind) jjmatchedPos = pos; return pos + 1; } -private int jjMoveStringLiteralDfa0_1(){ +private int jjMoveStringLiteralDfa0_2(){ switch(curChar) { case 40: - return jjStopAtPos(0, 13); + return jjStopAtPos(0, 29); case 41: return jjStopAtPos(0, 14); case 43: - return jjStopAtPos(0, 11); - case 45: return jjStopAtPos(0, 12); + case 45: + return jjStopAtPos(0, 13); case 58: return jjStopAtPos(0, 15); case 60: jjmatchedKind = 17; - return jjMoveStringLiteralDfa1_1(0x40000L); + return jjMoveStringLiteralDfa1_2(0x40000L); case 61: return jjStopAtPos(0, 16); case 62: jjmatchedKind = 19; - return jjMoveStringLiteralDfa1_1(0x100000L); + return jjMoveStringLiteralDfa1_2(0x100000L); + case 64: + return jjStopAtPos(0, 55); case 91: return jjStopAtPos(0, 27); case 94: return jjStopAtPos(0, 21); + case 102: + return jjMoveStringLiteralDfa1_2(0x800L); case 123: return jjStopAtPos(0, 28); case 126: return jjStopAtPos(0, 22); default : - return jjMoveNfa_1(0, 0); + return jjMoveNfa_2(0, 0); } } -private int jjMoveStringLiteralDfa1_1(long active0){ +private int jjMoveStringLiteralDfa1_2(long active0){ try { curChar = input_stream.readChar(); } catch(java.io.IOException e) { - jjStopStringLiteralDfa_1(0, active0); + jjStopStringLiteralDfa_2(0, active0); return 1; } switch(curChar) @@ -115,10 +158,31 @@ private int jjMoveStringLiteralDfa1_1(long active0){ else if ((active0 & 0x100000L) != 0L) return jjStopAtPos(1, 20); break; + case 110: + return jjMoveStringLiteralDfa2_2(active0, 0x800L); default : break; } - return jjStartNfa_1(0, active0); + return jjStartNfa_2(0, active0); +} +private int jjMoveStringLiteralDfa2_2(long old0, long active0){ + if (((active0 &= old0)) == 0L) + return jjStartNfa_2(0, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_2(1, active0); + return 2; + } + switch(curChar) + { + case 58: + if ((active0 & 0x800L) != 0L) + return jjStopAtPos(2, 11); + break; + default : + break; + } + return jjStartNfa_2(1, active0); } static final long[] jjbitVec0 = { 0x1L, 0x0L, 0x0L, 0x0L @@ -132,7 +196,7 @@ static final long[] jjbitVec3 = { static final long[] jjbitVec4 = { 0xfffefffffffffffeL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL }; -private int jjMoveNfa_1(int startState, int curPos) +private int jjMoveNfa_2(int startState, int curPos) { int startsAt = 0; jjnewStateCnt = 32; @@ -150,6 +214,14 @@ private int jjMoveNfa_1(int startState, int curPos) { switch(jjstateSet[--i]) { + case 32: + case 23: + if ((0x8bff7cf8ffffd9ffL & l) == 0L) + break; + if (kind > 25) + kind = 25; + { jjCheckNAddTwoStates(23, 24); } + break; case 0: if ((0x8bff54f8ffffd9ffL & l) != 0L) { @@ -232,13 +304,6 @@ private int jjMoveNfa_1(int startState, int curPos) kind = 25; { jjCheckNAddTwoStates(23, 24); } break; - case 23: - if ((0x8bff7cf8ffffd9ffL & l) == 0L) - break; - if (kind > 25) - kind = 25; - { jjCheckNAddTwoStates(23, 24); } - break; case 25: if (kind > 25) kind = 25; @@ -268,8 +333,18 @@ private int jjMoveNfa_1(int startState, int curPos) { switch(jjstateSet[--i]) { + case 32: + if ((0x97ffffff87fffffeL & l) != 0L) + { + if (kind > 25) + kind = 25; + { jjCheckNAddTwoStates(23, 24); } + } + else if (curChar == 92) + { jjCheckNAddTwoStates(25, 25); } + break; case 0: - if ((0x97ffffff87ffffffL & l) != 0L) + if ((0x97ffffff87fffffeL & l) != 0L) { if (kind > 25) kind = 25; @@ -338,8 +413,14 @@ private int jjMoveNfa_1(int startState, int curPos) { jjCheckNAddStates(3, 5); } break; case 22: + if ((0x97ffffff87fffffeL & l) == 0L) + break; + if (kind > 25) + kind = 25; + { jjCheckNAddTwoStates(23, 24); } + break; case 23: - if ((0x97ffffff87ffffffL & l) == 0L) + if ((0x97ffffff87fffffeL & l) == 0L) break; if (kind > 25) kind = 25; @@ -380,6 +461,14 @@ private int jjMoveNfa_1(int startState, int curPos) { switch(jjstateSet[--i]) { + case 32: + case 23: + if (!jjCanMove_2(hiByte, i1, i2, l1, l2)) + break; + if (kind > 25) + kind = 25; + { jjCheckNAddTwoStates(23, 24); } + break; case 0: if (jjCanMove_0(hiByte, i1, i2, l1, l2)) { @@ -399,7 +488,6 @@ private int jjMoveNfa_1(int startState, int curPos) { jjCheckNAddStates(3, 5); } break; case 22: - case 23: if (!jjCanMove_2(hiByte, i1, i2, l1, l2)) break; if (kind > 25) @@ -438,11 +526,56 @@ private final int jjStopStringLiteralDfa_0(int pos, long active0){ switch (pos) { case 0: - if ((active0 & 0x20000000L) != 0L) - { - jjmatchedKind = 33; - return 6; - } + if ((active0 & 0x400000000000L) != 0L) + return 19; + if ((active0 & 0x400000000L) != 0L) + return 47; + if ((active0 & 0x80000000L) != 0L) + return 63; + return -1; + case 1: + if ((active0 & 0x400000000000L) != 0L) + return 18; + if ((active0 & 0x400000000L) != 0L) + return 46; + return -1; + case 2: + if ((active0 & 0x400000000000L) != 0L) + return 17; + if ((active0 & 0x400000000L) != 0L) + return 45; + return -1; + case 3: + if ((active0 & 0x400000000L) != 0L) + return 44; + if ((active0 & 0x400000000000L) != 0L) + return 16; + return -1; + case 4: + if ((active0 & 0x400000000000L) != 0L) + return 15; + if ((active0 & 0x400000000L) != 0L) + return 43; + return -1; + case 5: + if ((active0 & 0x400000000L) != 0L) + return 42; + if ((active0 & 0x400000000000L) != 0L) + return 14; + return -1; + case 6: + if ((active0 & 0x400000000000L) != 0L) + return 13; + if ((active0 & 0x400000000L) != 0L) + return 41; + return -1; + case 7: + if ((active0 & 0x400000000000L) != 0L) + return 12; + return -1; + case 8: + if ((active0 & 0x400000000000L) != 0L) + return 11; return -1; default : return -1; @@ -454,12 +587,24 @@ private final int jjStartNfa_0(int pos, long active0){ private int jjMoveStringLiteralDfa0_0(){ switch(curChar) { - case 84: - return jjMoveStringLiteralDfa1_0(0x20000000L); - case 93: - return jjStopAtPos(0, 30); - case 125: - return jjStopAtPos(0, 31); + case 40: + return jjStopAtPos(0, 29); + case 97: + return jjMoveStringLiteralDfa1_0(0x80000000L); + case 98: + return jjMoveStringLiteralDfa1_0(0x100000000L); + case 99: + return jjMoveStringLiteralDfa1_0(0x400000000L); + case 101: + return jjMoveStringLiteralDfa1_0(0x800000000L); + case 111: + return jjMoveStringLiteralDfa1_0(0x181000000000L); + case 112: + return jjMoveStringLiteralDfa1_0(0x200000000000L); + case 117: + return jjMoveStringLiteralDfa1_0(0x400000000000L); + case 119: + return jjMoveStringLiteralDfa1_0(0x3000000000000L); default : return jjMoveNfa_0(0, 0); } @@ -472,15 +617,273 @@ private int jjMoveStringLiteralDfa1_0(long active0){ } switch(curChar) { - case 79: - if ((active0 & 0x20000000L) != 0L) - return jjStartNfaWithStates_0(1, 29, 6); - break; + case 101: + return jjMoveStringLiteralDfa2_0(active0, 0x100000000L); + case 102: + return jjMoveStringLiteralDfa2_0(active0, 0x80000000L); + case 104: + return jjMoveStringLiteralDfa2_0(active0, 0x200000000000L); + case 105: + return jjMoveStringLiteralDfa2_0(active0, 0x3000000000000L); + case 110: + return jjMoveStringLiteralDfa2_0(active0, 0x400000000000L); + case 111: + return jjMoveStringLiteralDfa2_0(active0, 0x400000000L); + case 114: + if ((active0 & 0x1000000000L) != 0L) + { + jjmatchedKind = 36; + jjmatchedPos = 1; + } + return jjMoveStringLiteralDfa2_0(active0, 0x80000000000L); + case 118: + return jjMoveStringLiteralDfa2_0(active0, 0x100000000000L); + case 120: + return jjMoveStringLiteralDfa2_0(active0, 0x800000000L); default : break; } return jjStartNfa_0(0, active0); } +private int jjMoveStringLiteralDfa2_0(long old0, long active0){ + if (((active0 &= old0)) == 0L) + return jjStartNfa_0(0, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(1, active0); + return 2; + } + switch(curChar) + { + case 100: + return jjMoveStringLiteralDfa3_0(active0, 0x80000000000L); + case 101: + return jjMoveStringLiteralDfa3_0(active0, 0x100000000000L); + case 102: + return jjMoveStringLiteralDfa3_0(active0, 0x100000000L); + case 108: + return jjMoveStringLiteralDfa3_0(active0, 0x1000000000000L); + case 110: + return jjMoveStringLiteralDfa3_0(active0, 0x400000000L); + case 111: + return jjMoveStringLiteralDfa3_0(active0, 0x400000000000L); + case 114: + return jjMoveStringLiteralDfa3_0(active0, 0x200000000000L); + case 116: + return jjMoveStringLiteralDfa3_0(active0, 0x2000880000000L); + default : + break; + } + return jjStartNfa_0(1, active0); +} +private int jjMoveStringLiteralDfa3_0(long old0, long active0){ + if (((active0 &= old0)) == 0L) + return jjStartNfa_0(1, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(2, active0); + return 3; + } + switch(curChar) + { + case 97: + return jjMoveStringLiteralDfa4_0(active0, 0x200000000000L); + case 100: + return jjMoveStringLiteralDfa4_0(active0, 0x1000000000000L); + case 101: + return jjMoveStringLiteralDfa4_0(active0, 0x80880000000L); + case 104: + return jjMoveStringLiteralDfa4_0(active0, 0x2000000000000L); + case 111: + return jjMoveStringLiteralDfa4_0(active0, 0x100000000L); + case 114: + return jjMoveStringLiteralDfa4_0(active0, 0x500000000000L); + case 116: + return jjMoveStringLiteralDfa4_0(active0, 0x400000000L); + default : + break; + } + return jjStartNfa_0(2, active0); +} +private int jjMoveStringLiteralDfa4_0(long old0, long active0){ + if (((active0 &= old0)) == 0L) + return jjStartNfa_0(2, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(3, active0); + return 4; + } + switch(curChar) + { + case 97: + return jjMoveStringLiteralDfa5_0(active0, 0x400000000L); + case 99: + return jjMoveStringLiteralDfa5_0(active0, 0x1000000000000L); + case 100: + return jjMoveStringLiteralDfa5_0(active0, 0x400000000000L); + case 105: + return jjMoveStringLiteralDfa5_0(active0, 0x2000000000000L); + case 108: + return jjMoveStringLiteralDfa5_0(active0, 0x100000000000L); + case 110: + return jjMoveStringLiteralDfa5_0(active0, 0x800000000L); + case 114: + if ((active0 & 0x80000000L) != 0L) + return jjStopAtPos(4, 31); + return jjMoveStringLiteralDfa5_0(active0, 0x80100000000L); + case 115: + return jjMoveStringLiteralDfa5_0(active0, 0x200000000000L); + default : + break; + } + return jjStartNfa_0(3, active0); +} +private int jjMoveStringLiteralDfa5_0(long old0, long active0){ + if (((active0 &= old0)) == 0L) + return jjStartNfa_0(3, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(4, active0); + return 5; + } + switch(curChar) + { + case 97: + return jjMoveStringLiteralDfa6_0(active0, 0x1100000000000L); + case 100: + if ((active0 & 0x800000000L) != 0L) + return jjStopAtPos(5, 35); + break; + case 101: + if ((active0 & 0x100000000L) != 0L) + return jjStopAtPos(5, 32); + else if ((active0 & 0x200000000000L) != 0L) + return jjStopAtPos(5, 45); + return jjMoveStringLiteralDfa6_0(active0, 0x480000000000L); + case 105: + return jjMoveStringLiteralDfa6_0(active0, 0x400000000L); + case 110: + if ((active0 & 0x2000000000000L) != 0L) + return jjStopAtPos(5, 49); + break; + default : + break; + } + return jjStartNfa_0(4, active0); +} +private int jjMoveStringLiteralDfa6_0(long old0, long active0){ + if (((active0 &= old0)) == 0L) + return jjStartNfa_0(4, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(5, active0); + return 6; + } + switch(curChar) + { + case 100: + if ((active0 & 0x80000000000L) != 0L) + return jjStopAtPos(6, 43); + break; + case 110: + return jjMoveStringLiteralDfa7_0(active0, 0x400000000L); + case 112: + return jjMoveStringLiteralDfa7_0(active0, 0x100000000000L); + case 114: + return jjMoveStringLiteralDfa7_0(active0, 0x1400000000000L); + default : + break; + } + return jjStartNfa_0(5, active0); +} +private int jjMoveStringLiteralDfa7_0(long old0, long active0){ + if (((active0 &= old0)) == 0L) + return jjStartNfa_0(5, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(6, active0); + return 7; + } + switch(curChar) + { + case 100: + if ((active0 & 0x1000000000000L) != 0L) + return jjStopAtPos(7, 48); + break; + case 101: + return jjMoveStringLiteralDfa8_0(active0, 0x400000000000L); + case 105: + return jjMoveStringLiteralDfa8_0(active0, 0x400000000L); + case 112: + return jjMoveStringLiteralDfa8_0(active0, 0x100000000000L); + default : + break; + } + return jjStartNfa_0(6, active0); +} +private int jjMoveStringLiteralDfa8_0(long old0, long active0){ + if (((active0 &= old0)) == 0L) + return jjStartNfa_0(6, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(7, active0); + return 8; + } + switch(curChar) + { + case 100: + if ((active0 & 0x400000000000L) != 0L) + return jjStartNfaWithStates_0(8, 46, 11); + break; + case 105: + return jjMoveStringLiteralDfa9_0(active0, 0x100000000000L); + case 110: + return jjMoveStringLiteralDfa9_0(active0, 0x400000000L); + default : + break; + } + return jjStartNfa_0(7, active0); +} +private int jjMoveStringLiteralDfa9_0(long old0, long active0){ + if (((active0 &= old0)) == 0L) + return jjStartNfa_0(7, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(8, active0); + return 9; + } + switch(curChar) + { + case 103: + if ((active0 & 0x400000000L) != 0L) + return jjStopAtPos(9, 34); + break; + case 110: + return jjMoveStringLiteralDfa10_0(active0, 0x100000000000L); + default : + break; + } + return jjStartNfa_0(8, active0); +} +private int jjMoveStringLiteralDfa10_0(long old0, long active0){ + if (((active0 &= old0)) == 0L) + return jjStartNfa_0(8, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(9, active0); + return 10; + } + switch(curChar) + { + case 103: + if ((active0 & 0x100000000000L) != 0L) + return jjStopAtPos(10, 44); + break; + default : + break; + } + return jjStartNfa_0(9, active0); +} private int jjStartNfaWithStates_0(int pos, int kind, int state) { jjmatchedKind = kind; @@ -490,6 +893,883 @@ private int jjStartNfaWithStates_0(int pos, int kind, int state) return jjMoveNfa_0(state, pos + 1); } private int jjMoveNfa_0(int startState, int curPos) +{ + int startsAt = 0; + jjnewStateCnt = 183; + int i = 1; + jjstateSet[0] = startState; + int kind = 0x7fffffff; + for (;;) + { + if (++jjround == 0x7fffffff) + ReInitRounds(); + if (curChar < 64) + { + long l = 1L << curChar; + do + { + switch(jjstateSet[--i]) + { + case 0: + if ((0x100002600L & l) != 0L) + kind = 7; + break; + default : break; + } + } while(i != startsAt); + } + else if (curChar < 128) + { + long l = 1L << (curChar & 077); + do + { + switch(jjstateSet[--i]) + { + case 18: + if (curChar == 111) + jjstateSet[jjnewStateCnt++] = 34; + if (curChar == 111) + jjstateSet[jjnewStateCnt++] = 17; + break; + case 13: + if (curChar == 101) + jjstateSet[jjnewStateCnt++] = 29; + if (curChar == 101) + jjstateSet[jjnewStateCnt++] = 12; + break; + case 19: + if (curChar == 110) + jjstateSet[jjnewStateCnt++] = 35; + if (curChar == 110) + jjstateSet[jjnewStateCnt++] = 18; + break; + case 63: + if (curChar == 116) + jjstateSet[jjnewStateCnt++] = 67; + if (curChar == 116) + jjstateSet[jjnewStateCnt++] = 62; + break; + case 0: + if (curChar == 109) + { jjAddStates(6, 9); } + else if (curChar == 110) + { jjAddStates(10, 17); } + else if (curChar == 97) + { jjAddStates(18, 19); } + else if (curChar == 99) + { jjAddStates(20, 21); } + else if (curChar == 117) + { jjAddStates(22, 23); } + break; + case 46: + if (curChar == 110) + jjstateSet[jjnewStateCnt++] = 54; + if (curChar == 110) + jjstateSet[jjnewStateCnt++] = 45; + break; + case 41: + if (curChar == 101) + jjstateSet[jjnewStateCnt++] = 49; + if (curChar == 101) + jjstateSet[jjnewStateCnt++] = 40; + break; + case 14: + if (curChar == 114) + jjstateSet[jjnewStateCnt++] = 30; + if (curChar == 114) + jjstateSet[jjnewStateCnt++] = 13; + break; + case 47: + if (curChar == 111) + jjstateSet[jjnewStateCnt++] = 55; + if (curChar == 111) + jjstateSet[jjnewStateCnt++] = 46; + break; + case 42: + if (curChar == 110) + jjstateSet[jjnewStateCnt++] = 50; + if (curChar == 110) + jjstateSet[jjnewStateCnt++] = 41; + break; + case 15: + if (curChar == 101) + jjstateSet[jjnewStateCnt++] = 31; + if (curChar == 101) + jjstateSet[jjnewStateCnt++] = 14; + break; + case 43: + if (curChar == 105) + jjstateSet[jjnewStateCnt++] = 51; + if (curChar == 105) + jjstateSet[jjnewStateCnt++] = 42; + break; + case 16: + if (curChar == 100) + jjstateSet[jjnewStateCnt++] = 32; + if (curChar == 100) + jjstateSet[jjnewStateCnt++] = 15; + break; + case 44: + if (curChar == 97) + jjstateSet[jjnewStateCnt++] = 52; + if (curChar == 97) + jjstateSet[jjnewStateCnt++] = 43; + break; + case 11: + if (curChar == 110) + jjstateSet[jjnewStateCnt++] = 27; + else if (curChar == 78) + jjstateSet[jjnewStateCnt++] = 10; + break; + case 17: + if (curChar == 114) + jjstateSet[jjnewStateCnt++] = 33; + if (curChar == 114) + jjstateSet[jjnewStateCnt++] = 16; + break; + case 45: + if (curChar == 116) + jjstateSet[jjnewStateCnt++] = 53; + if (curChar == 116) + jjstateSet[jjnewStateCnt++] = 44; + break; + case 12: + if (curChar == 100) + jjstateSet[jjnewStateCnt++] = 28; + if (curChar == 100) + jjstateSet[jjnewStateCnt++] = 11; + break; + case 1: + if (curChar == 117) + { jjAddStates(22, 23); } + break; + case 2: + if (curChar == 115 && kind > 47) + kind = 47; + break; + case 3: + case 20: + if (curChar == 112) + { jjCheckNAdd(2); } + break; + case 4: + if (curChar == 97) + jjstateSet[jjnewStateCnt++] = 3; + break; + case 5: + if (curChar == 108) + jjstateSet[jjnewStateCnt++] = 4; + break; + case 6: + if (curChar == 114) + jjstateSet[jjnewStateCnt++] = 5; + break; + case 7: + if (curChar == 101) + jjstateSet[jjnewStateCnt++] = 6; + break; + case 8: + if (curChar == 118) + jjstateSet[jjnewStateCnt++] = 7; + break; + case 9: + if (curChar == 79) + jjstateSet[jjnewStateCnt++] = 8; + break; + case 10: + if (curChar == 111) + jjstateSet[jjnewStateCnt++] = 9; + break; + case 21: + if (curChar == 97) + jjstateSet[jjnewStateCnt++] = 20; + break; + case 22: + if (curChar == 108) + jjstateSet[jjnewStateCnt++] = 21; + break; + case 23: + if (curChar == 114) + jjstateSet[jjnewStateCnt++] = 22; + break; + case 24: + if (curChar == 101) + jjstateSet[jjnewStateCnt++] = 23; + break; + case 25: + if (curChar == 118) + jjstateSet[jjnewStateCnt++] = 24; + break; + case 26: + if (curChar == 111) + jjstateSet[jjnewStateCnt++] = 25; + break; + case 27: + if (curChar == 111) + jjstateSet[jjnewStateCnt++] = 26; + break; + case 28: + if (curChar == 110) + jjstateSet[jjnewStateCnt++] = 27; + break; + case 29: + if (curChar == 100) + jjstateSet[jjnewStateCnt++] = 28; + break; + case 30: + if (curChar == 101) + jjstateSet[jjnewStateCnt++] = 29; + break; + case 31: + if (curChar == 114) + jjstateSet[jjnewStateCnt++] = 30; + break; + case 32: + if (curChar == 101) + jjstateSet[jjnewStateCnt++] = 31; + break; + case 33: + if (curChar == 100) + jjstateSet[jjnewStateCnt++] = 32; + break; + case 34: + if (curChar == 114) + jjstateSet[jjnewStateCnt++] = 33; + break; + case 35: + if (curChar == 111) + jjstateSet[jjnewStateCnt++] = 34; + break; + case 36: + if (curChar == 110) + jjstateSet[jjnewStateCnt++] = 35; + break; + case 37: + if (curChar == 99) + { jjAddStates(20, 21); } + break; + case 38: + if (curChar == 121 && kind > 33) + kind = 33; + break; + case 39: + if (curChar == 66) + { jjCheckNAdd(38); } + break; + case 40: + if (curChar == 100) + jjstateSet[jjnewStateCnt++] = 39; + break; + case 48: + if (curChar == 98) + { jjCheckNAdd(38); } + break; + case 49: + if (curChar == 100) + jjstateSet[jjnewStateCnt++] = 48; + break; + case 50: + if (curChar == 101) + jjstateSet[jjnewStateCnt++] = 49; + break; + case 51: + if (curChar == 110) + jjstateSet[jjnewStateCnt++] = 50; + break; + case 52: + if (curChar == 105) + jjstateSet[jjnewStateCnt++] = 51; + break; + case 53: + if (curChar == 97) + jjstateSet[jjnewStateCnt++] = 52; + break; + case 54: + if (curChar == 116) + jjstateSet[jjnewStateCnt++] = 53; + break; + case 55: + if (curChar == 110) + jjstateSet[jjnewStateCnt++] = 54; + break; + case 56: + if (curChar == 111) + jjstateSet[jjnewStateCnt++] = 55; + break; + case 57: + if (curChar == 97) + { jjAddStates(18, 19); } + break; + case 58: + if (curChar == 116 && kind > 30) + kind = 30; + break; + case 59: + case 64: + if (curChar == 115) + { jjCheckNAdd(58); } + break; + case 60: + if (curChar == 97) + jjstateSet[jjnewStateCnt++] = 59; + break; + case 61: + if (curChar == 101) + jjstateSet[jjnewStateCnt++] = 60; + break; + case 62: + if (curChar == 108) + jjstateSet[jjnewStateCnt++] = 61; + break; + case 65: + if (curChar == 97) + jjstateSet[jjnewStateCnt++] = 64; + break; + case 66: + if (curChar == 101) + jjstateSet[jjnewStateCnt++] = 65; + break; + case 67: + if (curChar == 76) + jjstateSet[jjnewStateCnt++] = 66; + break; + case 68: + if (curChar == 116) + jjstateSet[jjnewStateCnt++] = 67; + break; + case 69: + if (curChar == 110) + { jjAddStates(10, 17); } + break; + case 70: + if (curChar == 103 && kind > 39) + kind = 39; + break; + case 71: + case 83: + if (curChar == 110) + { jjCheckNAdd(70); } + break; + case 72: + if (curChar == 105) + jjstateSet[jjnewStateCnt++] = 71; + break; + case 73: + if (curChar == 112) + jjstateSet[jjnewStateCnt++] = 72; + break; + case 74: + if (curChar == 112) + jjstateSet[jjnewStateCnt++] = 73; + break; + case 75: + if (curChar == 97) + jjstateSet[jjnewStateCnt++] = 74; + break; + case 76: + if (curChar == 108) + jjstateSet[jjnewStateCnt++] = 75; + break; + case 77: + if (curChar == 114) + jjstateSet[jjnewStateCnt++] = 76; + break; + case 78: + if (curChar == 101) + jjstateSet[jjnewStateCnt++] = 77; + break; + case 79: + if (curChar == 118) + jjstateSet[jjnewStateCnt++] = 78; + break; + case 80: + if (curChar == 79) + jjstateSet[jjnewStateCnt++] = 79; + break; + case 81: + if (curChar == 110) + jjstateSet[jjnewStateCnt++] = 80; + break; + case 82: + if (curChar == 111) + jjstateSet[jjnewStateCnt++] = 81; + break; + case 84: + if (curChar == 105) + jjstateSet[jjnewStateCnt++] = 83; + break; + case 85: + if (curChar == 112) + jjstateSet[jjnewStateCnt++] = 84; + break; + case 86: + if (curChar == 112) + jjstateSet[jjnewStateCnt++] = 85; + break; + case 87: + if (curChar == 97) + jjstateSet[jjnewStateCnt++] = 86; + break; + case 88: + if (curChar == 108) + jjstateSet[jjnewStateCnt++] = 87; + break; + case 89: + if (curChar == 114) + jjstateSet[jjnewStateCnt++] = 88; + break; + case 90: + if (curChar == 101) + jjstateSet[jjnewStateCnt++] = 89; + break; + case 91: + if (curChar == 118) + jjstateSet[jjnewStateCnt++] = 90; + break; + case 92: + if (curChar == 111) + jjstateSet[jjnewStateCnt++] = 91; + break; + case 93: + if (curChar == 110) + jjstateSet[jjnewStateCnt++] = 92; + break; + case 94: + if (curChar == 111) + jjstateSet[jjnewStateCnt++] = 93; + break; + case 95: + if (curChar == 121 && kind > 40) + kind = 40; + break; + case 96: + if (curChar == 66) + { jjCheckNAdd(95); } + break; + case 97: + if (curChar == 100) + jjstateSet[jjnewStateCnt++] = 96; + break; + case 98: + if (curChar == 101) + jjstateSet[jjnewStateCnt++] = 97; + break; + case 99: + if (curChar == 110) + jjstateSet[jjnewStateCnt++] = 98; + break; + case 100: + if (curChar == 105) + jjstateSet[jjnewStateCnt++] = 99; + break; + case 101: + if (curChar == 97) + jjstateSet[jjnewStateCnt++] = 100; + break; + case 102: + if (curChar == 116) + jjstateSet[jjnewStateCnt++] = 101; + break; + case 103: + if (curChar == 110) + jjstateSet[jjnewStateCnt++] = 102; + break; + case 104: + if (curChar == 111) + jjstateSet[jjnewStateCnt++] = 103; + break; + case 105: + if (curChar == 67) + jjstateSet[jjnewStateCnt++] = 104; + break; + case 106: + if (curChar == 116) + jjstateSet[jjnewStateCnt++] = 105; + break; + case 107: + if (curChar == 111) + jjstateSet[jjnewStateCnt++] = 106; + break; + case 108: + if (curChar == 98) + { jjCheckNAdd(95); } + break; + case 109: + if (curChar == 100) + jjstateSet[jjnewStateCnt++] = 108; + break; + case 110: + if (curChar == 101) + jjstateSet[jjnewStateCnt++] = 109; + break; + case 111: + if (curChar == 110) + jjstateSet[jjnewStateCnt++] = 110; + break; + case 112: + if (curChar == 105) + jjstateSet[jjnewStateCnt++] = 111; + break; + case 113: + if (curChar == 97) + jjstateSet[jjnewStateCnt++] = 112; + break; + case 114: + if (curChar == 116) + jjstateSet[jjnewStateCnt++] = 113; + break; + case 115: + if (curChar == 110) + jjstateSet[jjnewStateCnt++] = 114; + break; + case 116: + if (curChar == 111) + jjstateSet[jjnewStateCnt++] = 115; + break; + case 117: + if (curChar == 99) + jjstateSet[jjnewStateCnt++] = 116; + break; + case 118: + if (curChar == 116) + jjstateSet[jjnewStateCnt++] = 117; + break; + case 119: + if (curChar == 111) + jjstateSet[jjnewStateCnt++] = 118; + break; + case 120: + if (curChar == 103 && kind > 41) + kind = 41; + break; + case 121: + case 132: + if (curChar == 110) + { jjCheckNAdd(120); } + break; + case 122: + if (curChar == 105) + jjstateSet[jjnewStateCnt++] = 121; + break; + case 123: + if (curChar == 110) + jjstateSet[jjnewStateCnt++] = 122; + break; + case 124: + if (curChar == 105) + jjstateSet[jjnewStateCnt++] = 123; + break; + case 125: + if (curChar == 97) + jjstateSet[jjnewStateCnt++] = 124; + break; + case 126: + if (curChar == 116) + jjstateSet[jjnewStateCnt++] = 125; + break; + case 127: + if (curChar == 110) + jjstateSet[jjnewStateCnt++] = 126; + break; + case 128: + if (curChar == 111) + jjstateSet[jjnewStateCnt++] = 127; + break; + case 129: + if (curChar == 67) + jjstateSet[jjnewStateCnt++] = 128; + break; + case 130: + if (curChar == 116) + jjstateSet[jjnewStateCnt++] = 129; + break; + case 131: + if (curChar == 111) + jjstateSet[jjnewStateCnt++] = 130; + break; + case 133: + if (curChar == 105) + jjstateSet[jjnewStateCnt++] = 132; + break; + case 134: + if (curChar == 110) + jjstateSet[jjnewStateCnt++] = 133; + break; + case 135: + if (curChar == 105) + jjstateSet[jjnewStateCnt++] = 134; + break; + case 136: + if (curChar == 97) + jjstateSet[jjnewStateCnt++] = 135; + break; + case 137: + if (curChar == 116) + jjstateSet[jjnewStateCnt++] = 136; + break; + case 138: + if (curChar == 110) + jjstateSet[jjnewStateCnt++] = 137; + break; + case 139: + if (curChar == 111) + jjstateSet[jjnewStateCnt++] = 138; + break; + case 140: + if (curChar == 99) + jjstateSet[jjnewStateCnt++] = 139; + break; + case 141: + if (curChar == 116) + jjstateSet[jjnewStateCnt++] = 140; + break; + case 142: + if (curChar == 111) + jjstateSet[jjnewStateCnt++] = 141; + break; + case 143: + if (curChar == 110 && kind > 42) + kind = 42; + break; + case 144: + case 151: + if (curChar == 105) + { jjCheckNAdd(143); } + break; + case 145: + if (curChar == 104) + jjstateSet[jjnewStateCnt++] = 144; + break; + case 146: + if (curChar == 116) + jjstateSet[jjnewStateCnt++] = 145; + break; + case 147: + if (curChar == 105) + jjstateSet[jjnewStateCnt++] = 146; + break; + case 148: + if (curChar == 87) + jjstateSet[jjnewStateCnt++] = 147; + break; + case 149: + if (curChar == 116) + jjstateSet[jjnewStateCnt++] = 148; + break; + case 150: + if (curChar == 111) + jjstateSet[jjnewStateCnt++] = 149; + break; + case 152: + if (curChar == 104) + jjstateSet[jjnewStateCnt++] = 151; + break; + case 153: + if (curChar == 116) + jjstateSet[jjnewStateCnt++] = 152; + break; + case 154: + if (curChar == 105) + jjstateSet[jjnewStateCnt++] = 153; + break; + case 155: + if (curChar == 119) + jjstateSet[jjnewStateCnt++] = 154; + break; + case 156: + if (curChar == 116) + jjstateSet[jjnewStateCnt++] = 155; + break; + case 157: + if (curChar == 111) + jjstateSet[jjnewStateCnt++] = 156; + break; + case 158: + if (curChar == 109) + { jjAddStates(6, 9); } + break; + case 159: + if (curChar == 115 && kind > 37) + kind = 37; + break; + case 160: + case 165: + if (curChar == 112) + { jjCheckNAdd(159); } + break; + case 161: + if (curChar == 97) + jjstateSet[jjnewStateCnt++] = 160; + break; + case 162: + if (curChar == 103) + jjstateSet[jjnewStateCnt++] = 161; + break; + case 163: + if (curChar == 120) + jjstateSet[jjnewStateCnt++] = 162; + break; + case 164: + if (curChar == 97) + jjstateSet[jjnewStateCnt++] = 163; + break; + case 166: + if (curChar == 97) + jjstateSet[jjnewStateCnt++] = 165; + break; + case 167: + if (curChar == 71) + jjstateSet[jjnewStateCnt++] = 166; + break; + case 168: + if (curChar == 120) + jjstateSet[jjnewStateCnt++] = 167; + break; + case 169: + if (curChar == 97) + jjstateSet[jjnewStateCnt++] = 168; + break; + case 170: + if (curChar == 104 && kind > 38) + kind = 38; + break; + case 171: + case 177: + if (curChar == 116) + { jjCheckNAdd(170); } + break; + case 172: + if (curChar == 100) + jjstateSet[jjnewStateCnt++] = 171; + break; + case 173: + if (curChar == 105) + jjstateSet[jjnewStateCnt++] = 172; + break; + case 174: + if (curChar == 119) + jjstateSet[jjnewStateCnt++] = 173; + break; + case 175: + if (curChar == 120) + jjstateSet[jjnewStateCnt++] = 174; + break; + case 176: + if (curChar == 97) + jjstateSet[jjnewStateCnt++] = 175; + break; + case 178: + if (curChar == 100) + jjstateSet[jjnewStateCnt++] = 177; + break; + case 179: + if (curChar == 105) + jjstateSet[jjnewStateCnt++] = 178; + break; + case 180: + if (curChar == 87) + jjstateSet[jjnewStateCnt++] = 179; + break; + case 181: + if (curChar == 120) + jjstateSet[jjnewStateCnt++] = 180; + break; + case 182: + if (curChar == 97) + jjstateSet[jjnewStateCnt++] = 181; + break; + default : break; + } + } while(i != startsAt); + } + else + { + int hiByte = (curChar >> 8); + int i1 = hiByte >> 6; + long l1 = 1L << (hiByte & 077); + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + do + { + switch(jjstateSet[--i]) + { + case 0: + if (jjCanMove_0(hiByte, i1, i2, l1, l2) && kind > 7) + kind = 7; + break; + default : if (i1 == 0 || l1 == 0 || i2 == 0 || l2 == 0) break; else break; + } + } while(i != startsAt); + } + if (kind != 0x7fffffff) + { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + if ((i = jjnewStateCnt) == (startsAt = 183 - (jjnewStateCnt = startsAt))) + return curPos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return curPos; } + } +} +private final int jjStopStringLiteralDfa_1(int pos, long active0){ + switch (pos) + { + case 0: + if ((active0 & 0x4000000000000L) != 0L) + { + jjmatchedKind = 54; + return 6; + } + return -1; + default : + return -1; + } +} +private final int jjStartNfa_1(int pos, long active0){ + return jjMoveNfa_1(jjStopStringLiteralDfa_1(pos, active0), pos + 1); +} +private int jjMoveStringLiteralDfa0_1(){ + switch(curChar) + { + case 84: + return jjMoveStringLiteralDfa1_1(0x4000000000000L); + case 93: + return jjStopAtPos(0, 51); + case 125: + return jjStopAtPos(0, 52); + default : + return jjMoveNfa_1(0, 0); + } +} +private int jjMoveStringLiteralDfa1_1(long active0){ + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_1(0, active0); + return 1; + } + switch(curChar) + { + case 79: + if ((active0 & 0x4000000000000L) != 0L) + return jjStartNfaWithStates_1(1, 50, 6); + break; + default : + break; + } + return jjStartNfa_1(0, active0); +} +private int jjStartNfaWithStates_1(int pos, int kind, int state) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return pos + 1; } + return jjMoveNfa_1(state, pos + 1); +} +private int jjMoveNfa_1(int startState, int curPos) { int startsAt = 0; jjnewStateCnt = 7; @@ -510,8 +1790,8 @@ private int jjMoveNfa_0(int startState, int curPos) case 0: if ((0xfffffffeffffffffL & l) != 0L) { - if (kind > 33) - kind = 33; + if (kind > 54) + kind = 54; { jjCheckNAdd(6); } } if ((0x100002600L & l) != 0L) @@ -528,21 +1808,21 @@ private int jjMoveNfa_0(int startState, int curPos) break; case 2: if ((0xfffffffbffffffffL & l) != 0L) - { jjCheckNAddStates(6, 8); } + { jjCheckNAddStates(24, 26); } break; case 3: if (curChar == 34) - { jjCheckNAddStates(6, 8); } + { jjCheckNAddStates(24, 26); } break; case 5: - if (curChar == 34 && kind > 32) - kind = 32; + if (curChar == 34 && kind > 53) + kind = 53; break; case 6: if ((0xfffffffeffffffffL & l) == 0L) break; - if (kind > 33) - kind = 33; + if (kind > 54) + kind = 54; { jjCheckNAdd(6); } break; default : break; @@ -560,12 +1840,12 @@ private int jjMoveNfa_0(int startState, int curPos) case 6: if ((0xdfffffffdfffffffL & l) == 0L) break; - if (kind > 33) - kind = 33; + if (kind > 54) + kind = 54; { jjCheckNAdd(6); } break; case 2: - { jjAddStates(6, 8); } + { jjAddStates(24, 26); } break; case 4: if (curChar == 92) @@ -594,20 +1874,20 @@ private int jjMoveNfa_0(int startState, int curPos) } if (jjCanMove_1(hiByte, i1, i2, l1, l2)) { - if (kind > 33) - kind = 33; + if (kind > 54) + kind = 54; { jjCheckNAdd(6); } } break; case 2: if (jjCanMove_1(hiByte, i1, i2, l1, l2)) - { jjAddStates(6, 8); } + { jjAddStates(24, 26); } break; case 6: if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) break; - if (kind > 33) - kind = 33; + if (kind > 54) + kind = 54; { jjCheckNAdd(6); } break; default : if (i1 == 0 || l1 == 0 || i2 == 0 || l2 == 0) break; else break; @@ -630,9 +1910,13 @@ private int jjMoveNfa_0(int startState, int curPos) /** Token literal values. */ public static final String[] jjstrLiteralImages = { -"", null, null, null, null, null, null, null, null, null, null, "\53", "\55", -"\50", "\51", "\72", "\75", "\74", "\74\75", "\76", "\76\75", "\136", "\176", null, -null, null, null, "\133", "\173", "\124\117", "\135", "\175", null, null, }; +"", null, null, null, null, null, null, null, null, null, null, "\146\156\72", +"\53", "\55", "\51", "\72", "\75", "\74", "\74\75", "\76", "\76\75", "\136", "\176", +null, null, null, null, "\133", "\173", "\50", null, "\141\146\164\145\162", +"\142\145\146\157\162\145", null, "\143\157\156\164\141\151\156\151\156\147", "\145\170\164\145\156\144", +"\157\162", null, null, null, null, null, null, "\157\162\144\145\162\145\144", +"\157\166\145\162\154\141\160\160\151\156\147", "\160\150\162\141\163\145", "\165\156\157\162\144\145\162\145\144", null, +"\167\151\154\144\143\141\162\144", "\167\151\164\150\151\156", "\124\117", "\135", "\175", null, null, "\100", }; protected Token jjFillToken() { final Token t; @@ -659,7 +1943,8 @@ protected Token jjFillToken() return t; } static final int[] jjnextStates = { - 28, 30, 31, 15, 16, 18, 2, 4, 5, + 28, 30, 31, 15, 16, 18, 164, 169, 176, 182, 82, 94, 107, 119, 131, 142, + 150, 157, 63, 68, 47, 56, 19, 36, 2, 4, 5, }; private static final boolean jjCanMove_0(int hiByte, int i1, int i2, long l1, long l2) { @@ -698,8 +1983,8 @@ private static final boolean jjCanMove_2(int hiByte, int i1, int i2, long l1, lo } } -int curLexState = 1; -int defaultLexState = 1; +int curLexState = 2; +int defaultLexState = 2; int jjnewStateCnt; int jjround; int jjmatchedPos; @@ -738,6 +2023,11 @@ public Token getNextToken() jjmatchedPos = 0; curPos = jjMoveStringLiteralDfa0_1(); break; + case 2: + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_2(); + break; } if (jjmatchedKind != 0x7fffffff) { @@ -863,7 +2153,7 @@ private void jjCheckNAddStates(int start, int end) { int i; jjround = 0x80000001; - for (i = 32; i-- > 0;) + for (i = 183; i-- > 0;) jjrounds[i] = 0x80000000; } @@ -878,7 +2168,7 @@ private void jjCheckNAddStates(int start, int end) /** Switch to specified lex state. */ public void SwitchTo(int lexState) { - if (lexState >= 2 || lexState < 0) + if (lexState >= 3 || lexState < 0) throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", TokenMgrError.INVALID_LEXICAL_STATE); else curLexState = lexState; @@ -887,17 +2177,19 @@ private void jjCheckNAddStates(int start, int end) /** Lexer state names. */ public static final String[] lexStateNames = { + "Function", "Range", "DEFAULT", }; /** Lex State array. */ public static final int[] jjnewLexState = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 0, 0, -1, 1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 2, 2, -1, -1, -1, }; static final long[] jjtoToken = { - 0x3ffffff01L, + 0xffffffffffff01L, }; static final long[] jjtoSkip = { 0x80L, @@ -910,8 +2202,8 @@ static final long[] jjtoMore = { }; protected CharStream input_stream; - private final int[] jjrounds = new int[32]; - private final int[] jjstateSet = new int[2 * 32]; + private final int[] jjrounds = new int[183]; + private final int[] jjstateSet = new int[2 * 183]; private final StringBuilder jjimage = new StringBuilder(); private StringBuilder image = jjimage; private int jjimageLen; diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/processors/IntervalQueryNodeProcessor.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/processors/IntervalQueryNodeProcessor.java new file mode 100644 index 00000000000..b4cf2caf7e8 --- /dev/null +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/processors/IntervalQueryNodeProcessor.java @@ -0,0 +1,68 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard.processors; + +import java.util.List; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.queryparser.flexible.core.QueryNodeException; +import org.apache.lucene.queryparser.flexible.core.config.QueryConfigHandler; +import org.apache.lucene.queryparser.flexible.core.messages.QueryParserMessages; +import org.apache.lucene.queryparser.flexible.core.nodes.QueryNode; +import org.apache.lucene.queryparser.flexible.core.processors.QueryNodeProcessorImpl; +import org.apache.lucene.queryparser.flexible.messages.MessageImpl; +import org.apache.lucene.queryparser.flexible.standard.config.StandardQueryConfigHandler.ConfigurationKeys; +import org.apache.lucene.queryparser.flexible.standard.nodes.IntervalQueryNode; + +/** + * This processor makes sure that {@link ConfigurationKeys#ANALYZER} is defined in the {@link + * QueryConfigHandler} and injects this analyzer into {@link + * org.apache.lucene.queryparser.flexible.standard.nodes.IntervalQueryNode}s. + * + * @see ConfigurationKeys#ANALYZER + */ +public class IntervalQueryNodeProcessor extends QueryNodeProcessorImpl { + private Analyzer analyzer; + + @Override + public QueryNode process(QueryNode queryTree) throws QueryNodeException { + this.analyzer = getQueryConfigHandler().get(ConfigurationKeys.ANALYZER); + return super.process(queryTree); + } + + @Override + protected QueryNode preProcessNode(QueryNode node) throws QueryNodeException { + if (node instanceof IntervalQueryNode) { + var intervalQueryNode = (IntervalQueryNode) node; + if (this.analyzer == null) { + throw new QueryNodeException( + new MessageImpl(QueryParserMessages.ANALYZER_REQUIRED, intervalQueryNode.toString())); + } + intervalQueryNode.setAnalyzer(this.analyzer); + } + return node; + } + + @Override + protected QueryNode postProcessNode(QueryNode node) throws QueryNodeException { + return node; + } + + @Override + protected List setChildrenOrder(List children) throws QueryNodeException { + return children; + } +} diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/processors/StandardQueryNodeProcessorPipeline.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/processors/StandardQueryNodeProcessorPipeline.java index 2df1f8fcdc5..abe3ebfca60 100644 --- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/processors/StandardQueryNodeProcessorPipeline.java +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/processors/StandardQueryNodeProcessorPipeline.java @@ -66,5 +66,6 @@ public class StandardQueryNodeProcessorPipeline extends QueryNodeProcessorPipeli add(new DefaultPhraseSlopQueryNodeProcessor()); add(new BoostQueryNodeProcessor()); add(new MultiTermRewriteMethodProcessor()); + add(new IntervalQueryNodeProcessor()); } } diff --git a/lucene/queryparser/src/java/overview.html b/lucene/queryparser/src/java/overview.html index e419dd89317..2b6f8a446af 100644 --- a/lucene/queryparser/src/java/overview.html +++ b/lucene/queryparser/src/java/overview.html @@ -15,151 +15,29 @@ limitations under the License. --> - - - QueryParsers - - - -

Apache Lucene QueryParsers.

-

- This module provides a number of queryparsers: -

-
-

Classic

- A Simple Lucene QueryParser implemented with JavaCC. -

Analyzing

- QueryParser that passes Fuzzy-, Prefix-, Range-, and WildcardQuerys through the given analyzer. -

Complex Phrase

- QueryParser which permits complex phrase query syntax eg "(john jon jonathan~) peters*" -

Extendable

- Extendable QueryParser provides a simple and flexible extension mechanism by overloading query field names. -

Flexible

-

-This project contains the new Lucene query parser implementation, which matches the syntax of the core QueryParser but offers a more modular architecture to enable customization. -

+ + + QueryParsers + + + + +

Apache Lucene QueryParsers.

-It's currently divided in 2 main packages: + This module provides a number of query parsers:

    -
  • {@link org.apache.lucene.queryparser.flexible.core}: it contains the query parser API classes, which should be extended by query parser implementations.
  • -
  • {@link org.apache.lucene.queryparser.flexible.standard}: it contains the current Lucene query parser implementation using the new query parser API.
  • +
  • {@linkplain org.apache.lucene.queryparser.flexible flexible query parser} +
  • {@linkplain org.apache.lucene.queryparser.classic classic query parser} +
  • {@linkplain org.apache.lucene.queryparser.complexPhrase complex phrase query parser} +
  • {@linkplain org.apache.lucene.queryparser.ext extendable query parser} +
  • {@linkplain org.apache.lucene.queryparser.surround surround query parser (span queries)} +
  • {@linkplain org.apache.lucene.queryparser.xml query parser building Query objects from XML}
-

Features

- -
    -
  1. Full support for boolean logic (not enabled)
  2. -
  3. QueryNode Trees - support for several syntaxes, - that can be converted into similar syntax QueryNode trees.
  4. -
  5. QueryNode Processors - Optimize, validate, rewrite the - QueryNode trees
  6. -
  7. Processors Pipelines - Select your favorite Processor - and build a processor pipeline, to implement the features you need
  8. -
  9. Config Interfaces - Allow the consumer of the Query Parser to implement - a diff Config Handler Objects to suite their needs.
  10. -
  11. Standard Builders - convert QueryNode's into several lucene - representations. Supported conversion is using a 2.4 compatible logic
  12. -
  13. QueryNode tree's can be converted to a lucene 2.4 syntax string, using toQueryString
  14. -
- -

Design

-This new query parser was designed to have very generic -architecture, so that it can be easily used for different -products with varying query syntaxes. This code is much more -flexible and extensible than the Lucene query parser in 2.4.X. -

-

-The new query parser goal is to separate syntax and semantics of a query. E.g. 'a AND -b', '+a +b', 'AND(a,b)' could be different syntaxes for the same query. -It distinguishes the semantics of the different query components, e.g. -whether and how to tokenize/lemmatize/normalize the different terms or -which Query objects to create for the terms. It allows to -write a parser with a new syntax, while reusing the underlying -semantics, as quickly as possible. -

-

-The query parser has three layers and its core is what we call the -QueryNode tree. It is a tree that initially represents the syntax of the -original query, e.g. for 'a AND b': -

-
-      AND
-     /   \
-    A     B
-
-

-The three layers are: -

-
-
QueryParser
-
-This layer is the text parsing layer which simply transforms the -query text string into a {@link org.apache.lucene.queryparser.flexible.core.nodes.QueryNode} tree. Every text parser -must implement the interface {@link org.apache.lucene.queryparser.flexible.core.parser.SyntaxParser}. -Lucene default implementations implements it using JavaCC. -
+ If you're new to query parsers, the {@linkplain org.apache.lucene.queryparser.flexible flexible query parser}'s + {@link org.apache.lucene.queryparser.flexible.standard.StandardQueryParser} is probably a good place to start. -
QueryNodeProcessor
-
The query node processors do most of the work. It is in fact a -configurable chain of processors. Each processors can walk the tree and -modify nodes or even the tree's structure. That makes it possible to -e.g. do query optimization before the query is executed or to tokenize -terms. -
- -
QueryBuilder
-
-The third layer is a configurable map of builders, which map {@link org.apache.lucene.queryparser.flexible.core.nodes.QueryNode} types to its specific -builder that will transform the QueryNode into Lucene Query object. -
- -
- -

-Furthermore, the query parser uses flexible configuration objects. It also uses message classes that -allow to attach resource bundles. This makes it possible to translate -messages, which is an important feature of a query parser. -

-

-This design allows to develop different query syntaxes very quickly. -

- -

StandardQueryParser and QueryParserWrapper

- -

-The classic Lucene query parser is located under -{@link org.apache.lucene.queryparser.classic}. -

-To make it simpler to use the new query parser -the class {@link org.apache.lucene.queryparser.flexible.standard.StandardQueryParser} may be helpful, -specially for people that do not want to extend the Query Parser. -It uses the default Lucene query processors, text parser and builders, so -you don't need to worry about dealing with those. - -{@link org.apache.lucene.queryparser.flexible.standard.StandardQueryParser} usage: - -

-      StandardQueryParser qpHelper = new StandardQueryParser();
-      StandardQueryConfigHandler config =  qpHelper.getQueryConfigHandler();
-      config.setAllowLeadingWildcard(true);
-      config.setAnalyzer(new WhitespaceAnalyzer());
-      Query query = qpHelper.parse("apache AND lucene", "defaultField");
-
-

Surround

-

-A QueryParser that supports the Span family of queries as well as pre and infix notation. -

-

XML

-A QueryParser that produces Lucene Query objects from XML streams. - + diff --git a/lucene/queryparser/src/resources/org/apache/lucene/queryparser/flexible/core/messages/QueryParserMessages.properties b/lucene/queryparser/src/resources/org/apache/lucene/queryparser/flexible/core/messages/QueryParserMessages.properties index 54861f27fa9..ef3b8336be6 100644 --- a/lucene/queryparser/src/resources/org/apache/lucene/queryparser/flexible/core/messages/QueryParserMessages.properties +++ b/lucene/queryparser/src/resources/org/apache/lucene/queryparser/flexible/core/messages/QueryParserMessages.properties @@ -58,3 +58,6 @@ UNSUPPORTED_NUMERIC_DATA_TYPE = Unsupported NumericField.DataType: {0} #Apache Lucene Community NUMERIC_CANNOT_BE_EMPTY = Field "{0}" is numeric and cannot have an empty value. + +#Apache Lucene Community +ANALYZER_REQUIRED = An analyzer is required to parse interval sub-query "{0}" \ No newline at end of file diff --git a/lucene/queryparser/src/test/org/apache/lucene/queryparser/flexible/standard/TestStandardQPEnhancements.java b/lucene/queryparser/src/test/org/apache/lucene/queryparser/flexible/standard/TestStandardQPEnhancements.java new file mode 100644 index 00000000000..de26e7c2724 --- /dev/null +++ b/lucene/queryparser/src/test/org/apache/lucene/queryparser/flexible/standard/TestStandardQPEnhancements.java @@ -0,0 +1,206 @@ +/* + * 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. + */ +package org.apache.lucene.queryparser.flexible.standard; + +import com.carrotsearch.randomizedtesting.RandomizedTest; +import java.io.StringReader; +import java.util.Locale; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.MockTokenizer; +import org.apache.lucene.queryparser.charstream.FastCharStream; +import org.apache.lucene.queryparser.flexible.core.nodes.QueryNode; +import org.apache.lucene.queryparser.flexible.standard.config.StandardQueryConfigHandler; +import org.apache.lucene.queryparser.flexible.standard.nodes.IntervalQueryNode; +import org.apache.lucene.queryparser.flexible.standard.parser.StandardSyntaxParser; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.util.LuceneTestCase; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Test; + +/** Test interval sub-query support in {@link StandardQueryParser}. */ +public class TestStandardQPEnhancements extends LuceneTestCase { + protected static final String FLD_DEFAULT = "defaultField"; + protected static final String FLD_WHITESPACE = "whitespaceField"; + + final StandardQueryParser getQueryParser() { + var analyzer = + new Analyzer() { + @Override + protected TokenStreamComponents createComponents(String fieldName) { + return new TokenStreamComponents(new MockTokenizer(MockTokenizer.WHITESPACE, true)); + } + }; + + var qp = new StandardQueryParser(analyzer); + qp.setDefaultOperator(StandardQueryConfigHandler.Operator.AND); + qp.setMultiFields(new String[] {}); + return qp; + } + + @Test + public void testMinShouldMatchOperator() throws Exception { + Query parsed = + parsedQuery( + String.format( + Locale.ROOT, + "(%s:foo OR %s:bar OR %s:baz)@2", + FLD_WHITESPACE, + FLD_WHITESPACE, + FLD_WHITESPACE)); + + MatcherAssert.assertThat( + ((BooleanQuery) parsed).getMinimumNumberShouldMatch(), Matchers.equalTo(2)); + } + + @Test + public void testAtLeast() throws Exception { + checkIntervalQueryNode("fn:atleast(3 FOO BAR baz)"); + } + + @Test + public void testMaxWidth() throws Exception { + checkIntervalQueryNode("fn:maxwidth(3 fn:atleast(2 foo bar baz))"); + } + + @Test + public void testQuotedTerm() throws Exception { + checkIntervalQueryNode("fn:atleast(2 \"foo\" \"BAR baz\")"); + } + + @Test + public void testMaxGaps() throws Exception { + checkIntervalQueryNode("fn:maxgaps(2 fn:unordered(foo BAR baz))"); + } + + @Test + public void testOrdered() throws Exception { + checkIntervalQueryNode("fn:ordered(foo BAR baz)"); + } + + @Test + public void testUnordered() throws Exception { + checkIntervalQueryNode("fn:unordered(foo BAR baz)"); + } + + @Test + public void testOr() throws Exception { + checkIntervalQueryNode("fn:or(foo baz)"); + } + + @Test + public void testWildcard() throws Exception { + checkIntervalQueryNode("fn:wildcard(foo*)"); + } + + @Test + public void testPhrase() throws Exception { + checkIntervalQueryNode("fn:phrase(abc def fn:or(baz boo))"); + } + + @Test + public void testBefore() throws Exception { + checkIntervalQueryNode("fn:before(abc fn:ordered(foo bar))"); + } + + @Test + public void testAfter() throws Exception { + checkIntervalQueryNode("fn:after(abc fn:ordered(foo bar))"); + } + + @Test + public void testContaining() throws Exception { + checkIntervalQueryNode("fn:containing(big small)"); + } + + @Test + public void testContainedBy() throws Exception { + checkIntervalQueryNode("fn:containedBy(small big)"); + } + + @Test + public void testNotContaining() throws Exception { + checkIntervalQueryNode("fn:notContaining(minuend subtrahend)"); + } + + @Test + public void testNotContainedBy() throws Exception { + checkIntervalQueryNode("fn:notContainedBy(small big)"); + } + + @Test + public void testWithin() throws Exception { + checkIntervalQueryNode("fn:within(small 2 fn:ordered(big foo))"); + } + + @Test + public void testNotWithin() throws Exception { + checkIntervalQueryNode("fn:notWithin(small 2 fn:ordered(big foo))"); + } + + @Test + public void testOverlapping() throws Exception { + checkIntervalQueryNode("fn:overlapping(fn:ordered(big foo) small)"); + } + + @Test + public void testNonOverlapping() throws Exception { + checkIntervalQueryNode("fn:nonOverlapping(fn:ordered(big foo) small)"); + } + + @Test + public void testUnorderedNoOverlaps() throws Exception { + checkIntervalQueryNode("fn:unorderedNoOverlaps(fn:ordered(big foo) small)"); + } + + @Test + public void testExtend() throws Exception { + checkIntervalQueryNode("fn:extend(fn:ordered(big foo) 2 5)"); + } + + protected void checkIntervalQueryNode(String query) throws Exception { + // Check raw parser first. + var syntaxParser = new StandardSyntaxParser(new FastCharStream(new StringReader(query))); + QueryNode queryNode = syntaxParser.TopLevelQuery(FLD_DEFAULT); + MatcherAssert.assertThat(queryNode, Matchers.instanceOf(IntervalQueryNode.class)); + + var queryParser = getQueryParser(); + Query parsedQuery; + if (RandomizedTest.randomBoolean()) { + queryParser.setMultiFields(new String[] {FLD_DEFAULT}); + parsedQuery = queryParser.parse(query, null); + } else { + parsedQuery = queryParser.parse(query, FLD_DEFAULT); + } + MatcherAssert.assertThat(parsedQuery, Matchers.notNullValue()); + + // Emit toString() for visual diagnostics. + IntervalQueryNode intervalQueryNode = (IntervalQueryNode) queryNode; + intervalQueryNode.setAnalyzer(queryParser.getAnalyzer()); + System.out.printf( + Locale.ROOT, "query: %s%n node: %s%n query: %s%n", query, queryNode, parsedQuery); + } + + protected String parsed(String query) throws Exception { + return parsedQuery(query).toString(""); + } + + protected Query parsedQuery(String query) throws Exception { + return getQueryParser().parse(query, /* no default field. */ null); + } +} diff --git a/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java b/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java index 975a0ea911a..a4132917947 100644 --- a/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java +++ b/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java @@ -2854,6 +2854,12 @@ public abstract class LuceneTestCase extends Assert { void run() throws Throwable; } + /** A {@link java.util.function.Consumer} that can throw any checked exception. */ + @FunctionalInterface + public interface ThrowingConsumer { + void accept(T t) throws Exception; + } + /** Checks a specific exception class is thrown by the given runnable, and returns it. */ public static T expectThrows( Class expectedType, ThrowingRunnable runnable) {