Skip to content

Commit

Permalink
Add tests for multi_match phrase prefix query across multiple fields (e…
Browse files Browse the repository at this point in the history
…lastic#95772)

This adds unit test coverage for a bug that was recently found in
Lucene. We would have caught it earlier if we were testing the
underlying lucene query being generated.

Closes elastic#95738
  • Loading branch information
javanna committed May 10, 2023
1 parent 4c43eb8 commit e1d98c6
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,8 @@
import org.apache.lucene.search.PrefixQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.lucene.search.MultiPhrasePrefixQuery;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.core.Strings;
import org.elasticsearch.index.query.MultiMatchQueryBuilder.Type;
Expand All @@ -43,7 +41,6 @@
import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.hasItems;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;

Expand Down Expand Up @@ -453,27 +450,6 @@ public void testNegativeFieldBoost() {
assertThat(exc.getMessage(), containsString("negative [boost]"));
}

private static IndexMetadata newIndexMeta(String name, Settings oldIndexSettings, Settings indexSettings) {
Settings build = Settings.builder().put(oldIndexSettings).put(indexSettings).build();
return IndexMetadata.builder(name).settings(build).build();
}

private void assertQueryWithAllFieldsWildcard(Query query) {
assertEquals(DisjunctionMaxQuery.class, query.getClass());
DisjunctionMaxQuery disjunctionMaxQuery = (DisjunctionMaxQuery) query;
int noMatchNoDocsQueries = 0;
for (Query q : disjunctionMaxQuery.getDisjuncts()) {
if (q.getClass() == MatchNoDocsQuery.class) {
noMatchNoDocsQueries++;
}
}
assertEquals(9, noMatchNoDocsQueries);
assertThat(
disjunctionMaxQuery.getDisjuncts(),
hasItems(new TermQuery(new Term(TEXT_FIELD_NAME, "hello")), new TermQuery(new Term(KEYWORD_FIELD_NAME, "hello")))
);
}

/**
* "now" on date fields should make the query non-cachable.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,15 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static java.util.Collections.emptyMap;
import static org.elasticsearch.index.query.QueryBuilders.multiMatchQuery;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;

public class MultiMatchQueryParserTests extends ESSingleNodeTestCase {

Expand All @@ -73,15 +76,27 @@ public void setup() throws IOException {
"properties": {
"first": {
"type": "text",
"analyzer": "standard"
"analyzer": "standard",
"index_prefixes": {
"min_chars" : 1,
"max_chars" : 19
}
},
"last": {
"type": "text",
"analyzer": "standard"
"analyzer": "standard",
"index_prefixes": {
"min_chars" : 1,
"max_chars" : 19
}
},
"nickname": {
"type": "text",
"analyzer": "whitespace"
"analyzer": "whitespace",
"index_prefixes": {
"min_chars" : 1,
"max_chars" : 19
}
}
}
}
Expand All @@ -93,6 +108,33 @@ public void setup() throws IOException {
this.indexService = indexService;
}

public void testToQueryPhrasePrefix() throws IOException {
SearchExecutionContext searchExecutionContext = indexService.newSearchExecutionContext(randomInt(20), 0, null, () -> {
throw new UnsupportedOperationException();
}, null, emptyMap());
searchExecutionContext.setAllowUnmappedFields(true);
MultiMatchQueryBuilder multiMatchQueryBuilder = new MultiMatchQueryBuilder("Har", "name.first", "name.last", "name.nickname");
multiMatchQueryBuilder.type(MultiMatchQueryBuilder.Type.PHRASE_PREFIX);
Query query = multiMatchQueryBuilder.toQuery(searchExecutionContext);
assertThat(query, instanceOf(DisjunctionMaxQuery.class));
DisjunctionMaxQuery disjunctionMaxQuery = (DisjunctionMaxQuery) query;
Set<Term> expectedTerms = new HashSet<>(
Arrays.asList(
new Term("name.first._index_prefix", "har"),
new Term("name.last._index_prefix", "har"),
new Term("name.nickname._index_prefix", "Har")
)
);
for (Query disjunct : disjunctionMaxQuery.getDisjuncts()) {
assertThat(disjunct, instanceOf(SynonymQuery.class));
SynonymQuery synonymQuery = (SynonymQuery) disjunct;
assertEquals(1, synonymQuery.getTerms().size());
Term term = synonymQuery.getTerms().get(0);
assertTrue("Unexpected term " + term, expectedTerms.remove(term));
}
assertEquals("Expected terms not found in the query: " + expectedTerms, 0, expectedTerms.size());
}

public void testCrossFieldMultiMatchQuery() throws IOException {
SearchExecutionContext searchExecutionContext = indexService.newSearchExecutionContext(randomInt(20), 0, null, () -> {
throw new UnsupportedOperationException();
Expand Down

0 comments on commit e1d98c6

Please sign in to comment.