Skip to content

Commit aa62135

Browse files
committed
Add tests for wildcard + regexp match-all cases
Signed-off-by: Michael Froh <froh@amazon.com>
1 parent 3ee80ee commit aa62135

File tree

3 files changed

+88
-13
lines changed

3 files changed

+88
-13
lines changed

rest-api-spec/src/main/resources/rest-api-spec/test/search/270_wildcard_fieldtype_queries.yml

+29
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ setup:
5050
id: 5
5151
body:
5252
my_field: "AbCd"
53+
- do:
54+
index:
55+
index: test
56+
id: 6
57+
body:
58+
other_field: "test"
5359
- do:
5460
indices.refresh: {}
5561

@@ -198,3 +204,26 @@ setup:
198204
my_field:
199205
value: ".*06-08.*Cluster-Manager Node.*"
200206
- match: { hits.total.value: 0 }
207+
208+
---
209+
"wildcard match-all works":
210+
- do:
211+
search:
212+
index: test
213+
body:
214+
query:
215+
wildcard:
216+
my_field:
217+
value: "*"
218+
- match: { hits.total.value: 5 }
219+
---
220+
"regexp match-all works":
221+
- do:
222+
search:
223+
index: test
224+
body:
225+
query:
226+
regexp:
227+
my_field:
228+
value: ".*"
229+
- match: { hits.total.value: 5 }

server/src/main/java/org/opensearch/index/mapper/WildcardFieldMapper.java

+35-13
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.apache.lucene.search.DocIdSetIterator;
2424
import org.apache.lucene.search.IndexSearcher;
2525
import org.apache.lucene.search.MatchAllDocsQuery;
26+
import org.apache.lucene.search.MatchNoDocsQuery;
2627
import org.apache.lucene.search.MultiTermQuery;
2728
import org.apache.lucene.search.Query;
2829
import org.apache.lucene.search.QueryVisitor;
@@ -448,14 +449,20 @@ public Query wildcardQuery(String value, MultiTermQuery.RewriteMethod method, bo
448449
};
449450
}
450451

451-
return new WildcardMatchingQuery(
452-
name(),
453-
matchAllTermsQuery(name(), getRequiredNGrams(finalValue)),
454-
matchPredicate,
455-
value,
456-
context,
457-
this
458-
);
452+
Set<String> requiredNGrams = getRequiredNGrams(finalValue);
453+
Query approximation;
454+
if (requiredNGrams.isEmpty()) {
455+
// This only happens when all characters are wildcard characters (* or ?),
456+
// or it's the empty string.
457+
if (value.length() == 0 || value.contains("?")) {
458+
approximation = this.existsQuery(context);
459+
} else {
460+
return existsQuery(context);
461+
}
462+
} else {
463+
approximation = matchAllTermsQuery(name(), requiredNGrams);
464+
}
465+
return new WildcardMatchingQuery(name(), approximation, matchPredicate, value, context, this);
459466
}
460467

461468
// Package-private for testing
@@ -540,10 +547,23 @@ public Query regexpQuery(
540547
Automaton automaton = regExp.toAutomaton(maxDeterminizedStates);
541548
CompiledAutomaton compiledAutomaton = new CompiledAutomaton(automaton);
542549

543-
return new WildcardMatchingQuery(name(), regexpToQuery(name(), regExp), s -> {
544-
BytesRef valueBytes = BytesRefs.toBytesRef(s);
545-
return compiledAutomaton.runAutomaton.run(valueBytes.bytes, valueBytes.offset, valueBytes.length);
546-
}, "/" + value + "/", context, this);
550+
Predicate<String> regexpPredicate;
551+
if (compiledAutomaton.type == CompiledAutomaton.AUTOMATON_TYPE.ALL) {
552+
return existsQuery(context);
553+
} else if (compiledAutomaton.type == CompiledAutomaton.AUTOMATON_TYPE.NONE) {
554+
return new MatchNoDocsQuery("Regular expression matches nothing");
555+
} else {
556+
regexpPredicate = s -> {
557+
BytesRef valueBytes = BytesRefs.toBytesRef(s);
558+
return compiledAutomaton.runAutomaton.run(valueBytes.bytes, valueBytes.offset, valueBytes.length);
559+
};
560+
}
561+
562+
Query approximation = regexpToQuery(name(), regExp);
563+
if (approximation instanceof MatchAllDocsQuery) {
564+
approximation = existsQuery(context);
565+
}
566+
return new WildcardMatchingQuery(name(), approximation, regexpPredicate, "/" + value + "/", context, this);
547567
}
548568

549569
/**
@@ -602,6 +622,8 @@ private static Query regexpToQuery(String fieldName, RegExp regExp) {
602622
}
603623
if (query.clauses().size() == 1) {
604624
return query.iterator().next().getQuery();
625+
} else if (query.clauses().size() == 0) {
626+
return new MatchAllDocsQuery();
605627
}
606628
return query;
607629
}
@@ -704,7 +726,7 @@ private WildcardMatchingQuery(
704726

705727
@Override
706728
public String toString(String s) {
707-
return "WildcardMatchingQuery(" + fieldName + "\"" + patternString + "\")";
729+
return "WildcardMatchingQuery(" + fieldName + ":\"" + patternString + "\")";
708730
}
709731

710732
@Override

server/src/test/java/org/opensearch/index/mapper/WildcardFieldTypeTests.java

+24
Original file line numberDiff line numberDiff line change
@@ -149,4 +149,28 @@ public void testRegexpQuery() {
149149
assertTrue(actualMatchingQuery.getSecondPhaseMatcher().test("abcdjk"));
150150
assertTrue(actualMatchingQuery.getSecondPhaseMatcher().test("abefqwertyhi"));
151151
}
152+
153+
public void testWildcardMatchAll() {
154+
String pattern = "???";
155+
MappedFieldType ft = new WildcardFieldMapper.WildcardFieldType("field");
156+
Query actual = ft.wildcardQuery(pattern, null, null);
157+
assertEquals(new WildcardFieldMapper.WildcardMatchingQuery("field", ft.existsQuery(null), "???"), actual);
158+
159+
pattern = "*";
160+
actual = ft.wildcardQuery(pattern, null, null);
161+
assertEquals(ft.existsQuery(null), actual);
162+
}
163+
164+
public void testRegexpMatchAll() {
165+
// The following matches any string of length exactly 3. We do need to evaluate the predicate.
166+
String pattern = "...";
167+
MappedFieldType ft = new WildcardFieldMapper.WildcardFieldType("field");
168+
Query actual = ft.regexpQuery(pattern, 0, 0, 1000, null, null);
169+
assertEquals(new WildcardFieldMapper.WildcardMatchingQuery("field", ft.existsQuery(null), "/.../"), actual);
170+
171+
// The following pattern has a predicate that matches everything. We can just return the field exists query.
172+
pattern = ".*";
173+
actual = ft.regexpQuery(pattern, 0, 0, 1000, null, null);
174+
assertEquals(ft.existsQuery(null), actual);
175+
}
152176
}

0 commit comments

Comments
 (0)