diff --git a/lucene/core/src/java/org/apache/lucene/search/positions/OrderedConjunctionQuery.java b/lucene/core/src/java/org/apache/lucene/search/positions/OrderedConjunctionQuery.java new file mode 100644 index 0000000..527a97f --- /dev/null +++ b/lucene/core/src/java/org/apache/lucene/search/positions/OrderedConjunctionQuery.java @@ -0,0 +1,38 @@ +package org.apache.lucene.search.positions; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.Query; + +public class OrderedConjunctionQuery extends PositionFilterQuery { + + public OrderedConjunctionQuery(int slop, Query... queries) { + super(buildBooleanQuery(queries), new WithinOrderedFilter(slop + queries.length - 1)); + } + + private static BooleanQuery buildBooleanQuery(Query... queries) { + BooleanQuery bq = new BooleanQuery(); + for (Query q : queries) { + bq.add(q, BooleanClause.Occur.MUST); + } + return bq; + } + +} diff --git a/lucene/core/src/java/org/apache/lucene/search/positions/PositionFilterQuery.java b/lucene/core/src/java/org/apache/lucene/search/positions/PositionFilterQuery.java index d548f11..a6180d2 100644 --- a/lucene/core/src/java/org/apache/lucene/search/positions/PositionFilterQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/positions/PositionFilterQuery.java @@ -80,7 +80,15 @@ public class PositionFilterQuery extends Query implements Cloneable { @Override public Explanation explain(AtomicReaderContext context, int doc) throws IOException { - return other.explain(context, doc); + Scorer scorer = scorer(context, true, false, context.reader().getLiveDocs()); + if (scorer != null) { + int newDoc = scorer.advance(doc); + if (newDoc == doc) { + return other.explain(context, doc); + } + } + return new ComplexExplanation(false, 0.0f, "No matching term within position filter"); + } @Override @@ -169,7 +177,7 @@ public class PositionFilterQuery extends Query implements Cloneable { @Override public String toString(String field) { - return inner.toString(); + return filter.toString() + "(" + inner.toString() + ")"; } private final class FilteredPositions extends PositionIntervalIterator { diff --git a/lucene/core/src/java/org/apache/lucene/search/positions/WithinOrderedFilter.java b/lucene/core/src/java/org/apache/lucene/search/positions/WithinOrderedFilter.java new file mode 100644 index 0000000..a6fb133 --- /dev/null +++ b/lucene/core/src/java/org/apache/lucene/search/positions/WithinOrderedFilter.java @@ -0,0 +1,39 @@ +package org.apache.lucene.search.positions; + +/* + * 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. + */ + +public class WithinOrderedFilter implements PositionIntervalIterator.PositionIntervalFilter { + + private int slop; + + public WithinOrderedFilter(int slop) { + this.slop = slop; + } + + @Override + public PositionIntervalIterator filter(PositionIntervalIterator iter) { + return new WithinPositionIterator(slop, + new OrderedConjunctionPositionIterator(false, iter)); + } + + @Override + public String toString() { + return "WithinOrderedFilter[" + slop + "]"; + } + +} diff --git a/lucene/core/src/test/org/apache/lucene/search/positions/TestPositionFilterQueries.java b/lucene/core/src/test/org/apache/lucene/search/positions/TestPositionFilterQueries.java new file mode 100644 index 0000000..60ae8c9 --- /dev/null +++ b/lucene/core/src/test/org/apache/lucene/search/positions/TestPositionFilterQueries.java @@ -0,0 +1,101 @@ +package org.apache.lucene.search.positions; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.apache.lucene.analysis.MockAnalyzer; +import org.apache.lucene.document.Document; +import org.apache.lucene.document.TextField; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.RandomIndexWriter; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.*; +import org.apache.lucene.store.Directory; +import org.apache.lucene.util.LuceneTestCase; + +import java.io.IOException; + +public class TestPositionFilterQueries extends LuceneTestCase { + + private IndexSearcher searcher; + private IndexReader reader; + private Directory directory; + + public static final String field = "field"; + + @Override + public void setUp() throws Exception { + super.setUp(); + directory = newDirectory(); + RandomIndexWriter writer = new RandomIndexWriter(random(), directory, + newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random())) + .setMergePolicy(newLogMergePolicy())); + for (int i = 0; i < docFields.length; i++) { + Document doc = new Document(); + doc.add(newField(field, docFields[i], TextField.TYPE_STORED)); + writer.addDocument(doc); + } + writer.forceMerge(1); + reader = writer.getReader(); + writer.close(); + searcher = newSearcher(reader); + } + + @Override + public void tearDown() throws Exception { + reader.close(); + directory.close(); + super.tearDown(); + } + + private String[] docFields = { + "w1 w2 w3 w4 w5 w6 w7 w8 w9 w10 w11 w12", //0 + "w1 w3 w4 w5 w6 w7 w8", //1 + "w1 w3 w10 w4 w5 w6 w7 w8", //2 + "w1 w3 w2 w4 w5 w6 w7 w8", //3 + }; + + public TermQuery makeTermQuery(String text) { + return new TermQuery(new Term(field, text)); + } + + private void checkHits(Query query, int[] results) throws IOException { + CheckHits.checkHits(random(), query, field, searcher, results); + } + + // or(w1 pre/2 w2, w1 pre/3 w10) + public void testOrNearNearQuery() throws IOException { + Query near1 = new OrderedConjunctionQuery(2, makeTermQuery("w1"), makeTermQuery("w2")); + Query near2 = new OrderedConjunctionQuery(3, makeTermQuery("w1"), makeTermQuery("w10")); + BooleanQuery bq = new BooleanQuery(); + bq.add(near1, BooleanClause.Occur.SHOULD); + bq.add(near2, BooleanClause.Occur.SHOULD); + + checkHits(bq, new int[] { 0, 2, 3 }); + } + + // (a pre/2 b) pre/6 (c pre/2 d) + public void testNearNearNearQuery() throws IOException { + Query near1 = new OrderedConjunctionQuery(2, makeTermQuery("w1"), makeTermQuery("w4")); + Query near2 = new OrderedConjunctionQuery(2, makeTermQuery("w10"), makeTermQuery("w12")); + Query near3 = new OrderedConjunctionQuery(11, near1, near2); + + //TODO: Subinterval slops - should this work with a slop of 6 rather than 11? + + checkHits(near3, new int[] { 0 }); + } +} diff --git a/lucene/core/src/test/org/apache/lucene/search/positions/TestSimplePositions.java b/lucene/core/src/test/org/apache/lucene/search/positions/TestSimplePositions.java index 6b3330c..a2ebb35 100644 --- a/lucene/core/src/test/org/apache/lucene/search/positions/TestSimplePositions.java +++ b/lucene/core/src/test/org/apache/lucene/search/positions/TestSimplePositions.java @@ -17,8 +17,6 @@ package org.apache.lucene.search.positions; * limitations under the License. */ -import java.io.IOException; - import org.apache.lucene.analysis.MockAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.TextField; @@ -26,20 +24,15 @@ import org.apache.lucene.index.AtomicReader; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.RandomIndexWriter; import org.apache.lucene.index.Term; -import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.*; import org.apache.lucene.search.BooleanClause.Occur; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.CheckHits; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.Weight; import org.apache.lucene.search.positions.PositionIntervalIterator.PositionInterval; import org.apache.lucene.search.positions.PositionIntervalIterator.PositionIntervalFilter; import org.apache.lucene.store.Directory; import org.apache.lucene.util.LuceneTestCase; +import java.io.IOException; + public class TestSimplePositions extends LuceneTestCase { private IndexSearcher searcher; private IndexReader reader; @@ -211,22 +204,7 @@ public class TestSimplePositions extends LuceneTestCase { assertNull("third range", positions.next()); } - public static class WithinOrderedFilter implements PositionIntervalFilter { - - private int slop; - - public WithinOrderedFilter(int slop) { - this.slop = slop; - } - - @Override - public PositionIntervalIterator filter(PositionIntervalIterator iter) { - return new WithinPositionIterator(slop, - new OrderedConjunctionPositionIterator(false, iter)); - } - - } - + public static class BlockPositionIteratorFilter implements PositionIntervalFilter { @Override