From 984bb738376faa202a26c79c421e7b42b54a64ef Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Wed, 3 May 2023 11:46:20 +0200 Subject: [PATCH] Add tests for multi_match phrase prefix query across multiple fields 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 #95738 --- .../query/MultiMatchQueryBuilderTests.java | 22 --------- .../search/MultiMatchQueryParserTests.java | 47 +++++++++++++++++-- 2 files changed, 44 insertions(+), 25 deletions(-) diff --git a/server/src/test/java/org/elasticsearch/index/query/MultiMatchQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/MultiMatchQueryBuilderTests.java index aba90ec5fda43..b710320052882 100644 --- a/server/src/test/java/org/elasticsearch/index/query/MultiMatchQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/MultiMatchQueryBuilderTests.java @@ -452,28 +452,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. */ diff --git a/server/src/test/java/org/elasticsearch/index/search/MultiMatchQueryParserTests.java b/server/src/test/java/org/elasticsearch/index/search/MultiMatchQueryParserTests.java index 84135f7c55ad4..ce49ff74ae87d 100644 --- a/server/src/test/java/org/elasticsearch/index/search/MultiMatchQueryParserTests.java +++ b/server/src/test/java/org/elasticsearch/index/search/MultiMatchQueryParserTests.java @@ -20,8 +20,10 @@ import org.apache.lucene.search.TermQuery; import org.apache.lucene.tests.analysis.MockSynonymAnalyzer; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.Version; import org.elasticsearch.common.Strings; import org.elasticsearch.common.compress.CompressedXContent; +import org.elasticsearch.common.lucene.Lucene; import org.elasticsearch.common.lucene.search.Queries; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.IndexService; @@ -44,12 +46,16 @@ 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.containsInAnyOrder; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; public class MultiMatchQueryParserTests extends ESSingleNodeTestCase { @@ -73,15 +79,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 + } } } } @@ -93,6 +111,29 @@ 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 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();