mirror of https://github.com/apache/lucene.git
LUCENE-9578: TermRangeQuery empty string lower bound edge case (#1976)
Currently a TermRangeQuery with the empty String ("") as lower bound and includeLower=false leads internally constructs an Automaton that doesn't match anything. This is unexpected expecially for open upper bounds where any string should be considered to be "higher" than the empty string. This PR changes "Automata#makeBinaryInterval" so that for an empty string lower bound and an open upper bound, any String should match the query regardless or the includeLower flag.
This commit is contained in:
parent
6990109f9b
commit
48348aefc1
|
@ -85,7 +85,22 @@ final public class Automata {
|
|||
a.finishState();
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a new (deterministic) automaton that accepts all binary terms except
|
||||
* the empty string.
|
||||
*/
|
||||
public static Automaton makeNonEmptyBinary() {
|
||||
Automaton a = new Automaton();
|
||||
int s1 = a.createState();
|
||||
int s2 = a.createState();
|
||||
a.setAccept(s2, true);
|
||||
a.addTransition(s1, s2, 0, 255);
|
||||
a.addTransition(s2, s2, 0, 255);
|
||||
a.finishState();
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new (deterministic) automaton that accepts any single codepoint.
|
||||
*/
|
||||
|
@ -254,8 +269,12 @@ final public class Automata {
|
|||
cmp = min.compareTo(max);
|
||||
} else {
|
||||
cmp = -1;
|
||||
if (min.length == 0 && minInclusive) {
|
||||
return makeAnyBinary();
|
||||
if (min.length == 0) {
|
||||
if (minInclusive) {
|
||||
return makeAnyBinary();
|
||||
} else {
|
||||
return makeNonEmptyBinary();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,7 +285,7 @@ final public class Automata {
|
|||
return makeBinary(min);
|
||||
}
|
||||
} else if (cmp > 0) {
|
||||
// max > min
|
||||
// max < min
|
||||
return makeEmpty();
|
||||
}
|
||||
|
||||
|
|
|
@ -1331,6 +1331,23 @@ public class TestAutomaton extends LuceneTestCase {
|
|||
assertFalse(Operations.run(a, intsRef("baq")));
|
||||
assertTrue(Operations.run(a, intsRef("bara")));
|
||||
}
|
||||
|
||||
public void testMakeBinaryIntervalLowerBoundEmptyString() throws Exception {
|
||||
Automaton a = Automata.makeBinaryInterval(new BytesRef(""), true, new BytesRef("bar"), true);
|
||||
assertTrue(Operations.run(a, intsRef("")));
|
||||
assertTrue(Operations.run(a, intsRef("a")));
|
||||
assertTrue(Operations.run(a, intsRef("bar")));
|
||||
assertFalse(Operations.run(a, intsRef("bara")));
|
||||
assertFalse(Operations.run(a, intsRef("baz")));
|
||||
|
||||
|
||||
a = Automata.makeBinaryInterval(new BytesRef(""), false, new BytesRef("bar"), true);
|
||||
assertFalse(Operations.run(a, intsRef("")));
|
||||
assertTrue(Operations.run(a, intsRef("a")));
|
||||
assertTrue(Operations.run(a, intsRef("bar")));
|
||||
assertFalse(Operations.run(a, intsRef("bara")));
|
||||
assertFalse(Operations.run(a, intsRef("baz")));
|
||||
}
|
||||
|
||||
public void testMakeBinaryIntervalEqual() throws Exception {
|
||||
Automaton a = Automata.makeBinaryInterval(new BytesRef("bar"), true, new BytesRef("bar"), true);
|
||||
|
@ -1352,6 +1369,12 @@ public class TestAutomaton extends LuceneTestCase {
|
|||
assertFalse(Operations.run(a, intsRef("barfoop")));
|
||||
}
|
||||
|
||||
public void testMakeBinaryExceptEmpty() throws Exception {
|
||||
Automaton a = Automata.makeNonEmptyBinary();
|
||||
assertFalse(Operations.run(a, intsRef("")));
|
||||
assertTrue(Operations.run(a, intsRef(TestUtil.randomRealisticUnicodeString(random(), 1, 10))));
|
||||
}
|
||||
|
||||
public void testMakeBinaryIntervalOpenMax() throws Exception {
|
||||
Automaton a = Automata.makeBinaryInterval(new BytesRef("bar"), true, null, true);
|
||||
assertFalse(Operations.run(a, intsRef("bam")));
|
||||
|
@ -1366,6 +1389,19 @@ public class TestAutomaton extends LuceneTestCase {
|
|||
assertTrue(Operations.run(a, intsRef("zzz")));
|
||||
}
|
||||
|
||||
public void testMakeBinaryIntervalOpenMaxZeroLengthMin() throws Exception {
|
||||
// when including min, automaton should accept "a"
|
||||
Automaton a = Automata.makeBinaryInterval(new BytesRef(""), true, null, true);
|
||||
assertTrue(Operations.run(a, intsRef("")));
|
||||
assertTrue(Operations.run(a, intsRef("a")));
|
||||
assertTrue(Operations.run(a, intsRef("aaaaaa")));
|
||||
// excluding min should still accept "a"
|
||||
a = Automata.makeBinaryInterval(new BytesRef(""), false, null, true);
|
||||
assertFalse(Operations.run(a, intsRef("")));
|
||||
assertTrue(Operations.run(a, intsRef("a")));
|
||||
assertTrue(Operations.run(a, intsRef("aaaaaa")));
|
||||
}
|
||||
|
||||
public void testMakeBinaryIntervalOpenMin() throws Exception {
|
||||
Automaton a = Automata.makeBinaryInterval(null, true, new BytesRef("foo"), true);
|
||||
assertFalse(Operations.run(a, intsRef("foz")));
|
||||
|
|
Loading…
Reference in New Issue