You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by dw...@apache.org on 2021/02/08 21:44:36 UTC

[lucene-solr] branch branch_8x updated: LUCENE-9744: NPE on a degenerate query in MinimumShouldMatchIntervalsSource$MinimumMatchesIterator.getSubMatches() (#2323) (#2324)

This is an automated email from the ASF dual-hosted git repository.

dweiss pushed a commit to branch branch_8x
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git


The following commit(s) were added to refs/heads/branch_8x by this push:
     new 190c172  LUCENE-9744: NPE on a degenerate query in MinimumShouldMatchIntervalsSource$MinimumMatchesIterator.getSubMatches() (#2323) (#2324)
190c172 is described below

commit 190c1728989f1e51924bd7e633e3489278fc7480
Author: Dawid Weiss <da...@carrotsearch.com>
AuthorDate: Mon Feb 8 22:43:08 2021 +0100

    LUCENE-9744: NPE on a degenerate query in MinimumShouldMatchIntervalsSource$MinimumMatchesIterator.getSubMatches() (#2323) (#2324)
---
 lucene/CHANGES.txt                                 |  4 +-
 .../apache/lucene/queries/intervals/Intervals.java | 10 +++
 .../MinimumShouldMatchIntervalsSource.java         |  1 +
 .../queries/intervals/NoMatchIntervalsSource.java  | 75 ++++++++++++++++++++++
 .../lucene/queries/intervals/TestIntervals.java    | 21 ++++++
 .../queries/intervals/TestSimplifications.java     |  8 +++
 6 files changed, 118 insertions(+), 1 deletion(-)

diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 535528b..155270f 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -39,7 +39,9 @@ Optimizations
 
 Bug Fixes
 ---------------------
-(No changes)
+
+* LUCENE-9744: NPE on a degenerate query in MinimumShouldMatchIntervalsSource
+  $MinimumMatchesIterator.getSubMatches(). (Alan Woodward)
 
 Other
 ---------------------
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 5d835bd..fcdcf4f 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
@@ -442,6 +442,16 @@ public final class Intervals {
    * Return intervals that span combinations of intervals from {@code minShouldMatch} of the sources
    */
   public static IntervalsSource atLeast(int minShouldMatch, IntervalsSource... sources) {
+    if (minShouldMatch == sources.length) {
+      return unordered(sources);
+    }
+    if (minShouldMatch > sources.length) {
+      return new NoMatchIntervalsSource(
+          "Too few sources to match minimum of ["
+              + minShouldMatch
+              + "]: "
+              + Arrays.toString(sources));
+    }
     return new MinimumShouldMatchIntervalsSource(sources, minShouldMatch);
   }
 
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/intervals/MinimumShouldMatchIntervalsSource.java b/lucene/queries/src/java/org/apache/lucene/queries/intervals/MinimumShouldMatchIntervalsSource.java
index ce3a6de..4cc2cfb 100644
--- a/lucene/queries/src/java/org/apache/lucene/queries/intervals/MinimumShouldMatchIntervalsSource.java
+++ b/lucene/queries/src/java/org/apache/lucene/queries/intervals/MinimumShouldMatchIntervalsSource.java
@@ -43,6 +43,7 @@ class MinimumShouldMatchIntervalsSource extends IntervalsSource {
   private final int minShouldMatch;
 
   MinimumShouldMatchIntervalsSource(IntervalsSource[] sources, int minShouldMatch) {
+    assert minShouldMatch < sources.length;
     this.sources = sources;
     this.minShouldMatch = minShouldMatch;
   }
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/intervals/NoMatchIntervalsSource.java b/lucene/queries/src/java/org/apache/lucene/queries/intervals/NoMatchIntervalsSource.java
new file mode 100644
index 0000000..cfa7364
--- /dev/null
+++ b/lucene/queries/src/java/org/apache/lucene/queries/intervals/NoMatchIntervalsSource.java
@@ -0,0 +1,75 @@
+/*
+ * 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.queries.intervals;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.search.QueryVisitor;
+
+/** A source returning no matches */
+class NoMatchIntervalsSource extends IntervalsSource {
+  final String reason;
+
+  NoMatchIntervalsSource(String reason) {
+    this.reason = reason;
+  }
+
+  @Override
+  public IntervalIterator intervals(String field, LeafReaderContext ctx) throws IOException {
+    return null;
+  }
+
+  @Override
+  public IntervalMatchesIterator matches(String field, LeafReaderContext ctx, int doc)
+      throws IOException {
+    return null;
+  }
+
+  @Override
+  public void visit(String field, QueryVisitor visitor) {}
+
+  @Override
+  public int minExtent() {
+    return 0;
+  }
+
+  @Override
+  public Collection<IntervalsSource> pullUpDisjunctions() {
+    return Collections.singleton(this);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    NoMatchIntervalsSource that = (NoMatchIntervalsSource) o;
+    return Objects.equals(reason, that.reason);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(reason);
+  }
+
+  @Override
+  public String toString() {
+    return "NOMATCH(" + reason + ")";
+  }
+}
diff --git a/lucene/queries/src/test/org/apache/lucene/queries/intervals/TestIntervals.java b/lucene/queries/src/test/org/apache/lucene/queries/intervals/TestIntervals.java
index 5444b66..2329ea8 100644
--- a/lucene/queries/src/test/org/apache/lucene/queries/intervals/TestIntervals.java
+++ b/lucene/queries/src/test/org/apache/lucene/queries/intervals/TestIntervals.java
@@ -761,6 +761,27 @@ public class TestIntervals extends LuceneTestCase {
 
   }
 
+  public void testDegenerateMinShouldMatch() throws IOException {
+    IntervalsSource source =
+        Intervals.ordered(
+            Intervals.atLeast(1, Intervals.term("interest")),
+            Intervals.atLeast(1, Intervals.term("anyone")));
+
+    MatchesIterator mi = getMatches(source, 0, "field1");
+    assertMatch(mi, 2, 4, 11, 29);
+    MatchesIterator subs = mi.getSubMatches();
+    assertNotNull(subs);
+    assertMatch(subs, 2, 2, 11, 19);
+    assertMatch(subs, 4, 4, 23, 29);
+    assertFalse(subs.next());
+    assertFalse(mi.next());
+  }
+
+  public void testNoMatchMinShouldMatch() throws IOException {
+    IntervalsSource source = Intervals.atLeast(4, Intervals.term("a"), Intervals.term("b"));
+    checkIntervals(source, "field", 0, new int[][] {});
+  }
+
   public void testDefinedGaps() throws IOException {
     IntervalsSource source = Intervals.phrase(
         Intervals.term("pease"),
diff --git a/lucene/queries/src/test/org/apache/lucene/queries/intervals/TestSimplifications.java b/lucene/queries/src/test/org/apache/lucene/queries/intervals/TestSimplifications.java
index 76a4857..8706c12 100644
--- a/lucene/queries/src/test/org/apache/lucene/queries/intervals/TestSimplifications.java
+++ b/lucene/queries/src/test/org/apache/lucene/queries/intervals/TestSimplifications.java
@@ -93,4 +93,12 @@ public class TestSimplifications extends LuceneTestCase {
     assertEquals(Intervals.or(Intervals.term("a"), Intervals.term("b"), Intervals.term("c"), Intervals.term("d")), actual);
   }
 
+  public void testMinShouldMatchSimplifications() {
+    IntervalsSource expected = Intervals.unordered(Intervals.term("a"), Intervals.term("b"));
+    assertEquals(expected, Intervals.atLeast(2, Intervals.term("a"), Intervals.term("b")));
+
+    assertEquals(
+        "NOMATCH(Too few sources to match minimum of [3]: [a, b])",
+        Intervals.atLeast(3, Intervals.term("a"), Intervals.term("b")).toString());
+  }
 }