diff options
Diffstat (limited to 'container-search/src')
11 files changed, 94 insertions, 80 deletions
diff --git a/container-search/src/main/java/com/yahoo/prelude/query/parser/AllParser.java b/container-search/src/main/java/com/yahoo/prelude/query/parser/AllParser.java index 43bd175b348..692269a1412 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/parser/AllParser.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/parser/AllParser.java @@ -11,6 +11,7 @@ import com.yahoo.prelude.query.OrItem; import com.yahoo.prelude.query.PhraseItem; import com.yahoo.prelude.query.QueryCanonicalizer; import com.yahoo.prelude.query.RankItem; +import com.yahoo.prelude.query.SegmentItem; import com.yahoo.prelude.query.TrueItem; import com.yahoo.prelude.query.WeakAndItem; import com.yahoo.search.query.QueryTree; @@ -98,10 +99,29 @@ public class AllParser extends SimpleParser { return root.getRoot() instanceof NullItem ? null : root.getRoot(); } + private boolean foldIntoAnd(CompositeItem other) { + if (other instanceof AndItem) { + return true; + } + if (weakAnd && other instanceof SegmentItem) { + return true; + } + if (weakAnd && other instanceof PhraseItem phrase) { + return ! phrase.isExplicit(); + } + return false; + } + protected CompositeItem addAnd(Item item, CompositeItem and) { if (and == null) and = createAnd(); - and.addItem(item); + if (item instanceof CompositeItem composite && foldIntoAnd(composite)) { + for (var subItem : composite.items()) { + addAnd(subItem, and); + } + } else { + and.addItem(item); + } return and; } diff --git a/container-search/src/main/java/com/yahoo/search/federation/FederationSearcher.java b/container-search/src/main/java/com/yahoo/search/federation/FederationSearcher.java index f40caac1562..000fef9b14b 100644 --- a/container-search/src/main/java/com/yahoo/search/federation/FederationSearcher.java +++ b/container-search/src/main/java/com/yahoo/search/federation/FederationSearcher.java @@ -42,7 +42,6 @@ import com.yahoo.search.searchchain.model.federation.FederationOptions; import java.time.Clock; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Iterator; @@ -95,7 +94,7 @@ public class FederationSearcher extends ForkingSearcher { } // for testing - public FederationSearcher(ComponentId id, SearchChainResolver searchChainResolver, + public FederationSearcher(SearchChainResolver searchChainResolver, Map<String, List<String>> schema2Clusters) { this(searchChainResolver, VirtualSourceResolver.of(), null, schema2Clusters); } @@ -151,7 +150,9 @@ public class FederationSearcher extends ForkingSearcher { } private static void addSearchChain(SearchChainResolver.Builder builder, - FederationConfig.Target target, FederationConfig.Target.SearchChain searchChain) { + FederationConfig.Target target, + FederationConfig.Target.SearchChain searchChain) + { if (!target.id().equals(searchChain.searchChainId())) throw new RuntimeException("Invalid federation config, " + target.id() + " != " + searchChain.searchChainId()); @@ -160,7 +161,8 @@ public class FederationSearcher extends ForkingSearcher { } private static void addSourceForProvider(SearchChainResolver.Builder builder, FederationConfig.Target target, - FederationConfig.Target.SearchChain searchChain, boolean isDefaultProvider) { + FederationConfig.Target.SearchChain searchChain, boolean isDefaultProvider) + { builder.addSourceForProvider( ComponentId.fromString(target.id()), ComponentId.fromString(searchChain.providerId()), @@ -181,14 +183,12 @@ public class FederationSearcher extends ForkingSearcher { public Result search(Query query, Execution execution) { Result mergedResults = execution.search(query); - Results<SearchChainInvocationSpec, UnresolvedSearchChainException> targets = - getTargets(query.getModel().getSources(), query.properties()); + var targets = getTargets(query.getModel().getSources(), query.properties()); warnIfUnresolvedSearchChains(targets.errors(), mergedResults.hits()); - Collection<SearchChainInvocationSpec> prunedTargets = - pruneTargetsWithoutDocumentTypes(query.getModel().getRestrict(), targets.data()); + var prunedTargets = pruneTargetsWithoutDocumentTypes(query.getModel().getRestrict(), targets.data()); - Results<Target, ErrorMessage> regularTargetHandlers = resolveSearchChains(prunedTargets, execution.searchChainRegistry()); + var regularTargetHandlers = resolveSearchChains(prunedTargets, execution.searchChainRegistry()); query.errors().addAll(regularTargetHandlers.errors()); Set<Target> targetHandlers = new LinkedHashSet<>(regularTargetHandlers.data()); @@ -359,8 +359,7 @@ public class FederationSearcher extends ForkingSearcher { public void fill(Result result, String summaryClass, Execution execution) { UniqueExecutionsToResults uniqueExecutionsToResults = new UniqueExecutionsToResults(); addResultsToFill(result.hits(), result, summaryClass, uniqueExecutionsToResults); - Set<Entry<Chain<Searcher>, Map<Query, Result>>> resultsForAllChains = - uniqueExecutionsToResults.resultsToFill.entrySet(); + var resultsForAllChains = uniqueExecutionsToResults.resultsToFill.entrySet(); int numberOfCallsToFillNeeded = 0; for (Entry<Chain<Searcher>, Map<Query, Result>> resultsToFillForAChain : resultsForAllChains) { @@ -563,7 +562,7 @@ public class FederationSearcher extends ForkingSearcher { } private static <T> List<Target> getAdditionalTargets(Query query, Execution execution, TargetSelector<T> targetSelector) { - if (targetSelector == null) return Collections.emptyList(); + if (targetSelector == null) return List.of(); ArrayList<Target> result = new ArrayList<>(); for (FederationTarget<T> target: targetSelector.getTargets(query, execution.searchChainRegistry())) @@ -708,12 +707,6 @@ public class FederationSearcher extends ForkingSearcher { private record Window(int hits, int offset) { - public Integer get(CompoundName parameterName) { - if (parameterName.equals(Query.HITS)) return hits; - if (parameterName.equals(Query.OFFSET)) return offset; - return null; - } - public static Window from(Query query) { return new Window(query.getHits(), query.getOffset()); } diff --git a/container-search/src/main/java/com/yahoo/search/federation/selection/TargetSelector.java b/container-search/src/main/java/com/yahoo/search/federation/selection/TargetSelector.java index c23be6bad14..be42ef1ee0e 100644 --- a/container-search/src/main/java/com/yahoo/search/federation/selection/TargetSelector.java +++ b/container-search/src/main/java/com/yahoo/search/federation/selection/TargetSelector.java @@ -5,7 +5,6 @@ import com.yahoo.processing.execution.chain.ChainRegistry; import com.yahoo.search.Query; import com.yahoo.search.Result; import com.yahoo.search.Searcher; -import com.yahoo.search.federation.selection.FederationTarget; import java.util.Collection; @@ -25,7 +24,7 @@ public interface TargetSelector<T> { Collection<FederationTarget<T>> getTargets(Query query, ChainRegistry<Searcher> searcherChainRegistry); /** - * For modifying the query before sending it to a the target + * For modifying the query before sending it to the target */ void modifyTargetQuery(FederationTarget<T> target, Query query); diff --git a/container-search/src/test/java/com/yahoo/prelude/query/parser/test/ExactMatchAndDefaultIndexTestCase.java b/container-search/src/test/java/com/yahoo/prelude/query/parser/test/ExactMatchAndDefaultIndexTestCase.java index adcfc721fc9..b998847efa3 100644 --- a/container-search/src/test/java/com/yahoo/prelude/query/parser/test/ExactMatchAndDefaultIndexTestCase.java +++ b/container-search/src/test/java/com/yahoo/prelude/query/parser/test/ExactMatchAndDefaultIndexTestCase.java @@ -35,7 +35,7 @@ public class ExactMatchAndDefaultIndexTestCase { q.getModel().setExecution(new Execution(Execution.Context.createContextStub(facts))); assertEquals("WEAKAND(100) testexact:a/b testexact:foo.com", q.getModel().getQueryTree().getRoot().toString()); q = new Query("?query=" + enc("a/b foo.com")); - assertEquals("WEAKAND(100) (AND a b) (AND foo com)", q.getModel().getQueryTree().getRoot().toString()); + assertEquals("WEAKAND(100) a b foo com", q.getModel().getQueryTree().getRoot().toString()); } @Test diff --git a/container-search/src/test/java/com/yahoo/prelude/querytransform/test/StemmingSearcherTestCase.java b/container-search/src/test/java/com/yahoo/prelude/querytransform/test/StemmingSearcherTestCase.java index 3db51ad4b8a..70af02102a3 100644 --- a/container-search/src/test/java/com/yahoo/prelude/querytransform/test/StemmingSearcherTestCase.java +++ b/container-search/src/test/java/com/yahoo/prelude/querytransform/test/StemmingSearcherTestCase.java @@ -144,8 +144,8 @@ public class StemmingSearcherTestCase { String emoji1 = "\uD83C\uDF49"; // 🍉 String emoji2 = "\uD83D\uDE00"; // 😀 assertStemmed("WEAKAND(100) " + emoji1, "/search?query=" + emoji1); - assertStemmed("WEAKAND(100) (AND " + emoji1 + " " + emoji2 + ")", "/search?query=" + emoji1 + emoji2); - assertStemmed("WEAKAND(100) (AND " + emoji1 + " foo " + emoji2 + ")", "/search?query=" + emoji1 + "foo" + emoji2); + assertStemmed("WEAKAND(100) " + emoji1 + " " + emoji2, "/search?query=" + emoji1 + emoji2); + assertStemmed("WEAKAND(100) " + emoji1 + " foo " + emoji2, "/search?query=" + emoji1 + "foo" + emoji2); } private Execution.Context newExecutionContext() { diff --git a/container-search/src/test/java/com/yahoo/prelude/test/IndexFactsTestCase.java b/container-search/src/test/java/com/yahoo/prelude/test/IndexFactsTestCase.java index c636550c02f..f53b238c5cd 100644 --- a/container-search/src/test/java/com/yahoo/prelude/test/IndexFactsTestCase.java +++ b/container-search/src/test/java/com/yahoo/prelude/test/IndexFactsTestCase.java @@ -50,7 +50,7 @@ public class IndexFactsTestCase { Query q = newQuery("?query=a:b", indexFacts); assertEquals("WEAKAND(100) a:b", q.getModel().getQueryTree().getRoot().toString()); q = newQuery("?query=notarealindex:b", indexFacts); - assertEquals("WEAKAND(100) (AND notarealindex b)", q.getModel().getQueryTree().getRoot().toString()); + assertEquals("WEAKAND(100) notarealindex b", q.getModel().getQueryTree().getRoot().toString()); } @Test @@ -277,8 +277,8 @@ public class IndexFactsTestCase { IndexFacts.Session session2 = indexFacts.newSession(query2.getModel().getSources(), query2.getModel().getRestrict()); assertTrue(session1.getIndex("url").isUriIndex()); assertTrue(session2.getIndex("url").isUriIndex()); - assertEquals("WEAKAND(100) (AND url:https url:foo url:bar)", query1.getModel().getQueryTree().toString()); - assertEquals("WEAKAND(100) (AND url:https url:foo url:bar)", query2.getModel().getQueryTree().toString()); + assertEquals("WEAKAND(100) url:https url:foo url:bar", query1.getModel().getQueryTree().toString()); + assertEquals("WEAKAND(100) url:https url:foo url:bar", query2.getModel().getQueryTree().toString()); } @Test diff --git a/container-search/src/test/java/com/yahoo/search/federation/DuplicateSourceTestCase.java b/container-search/src/test/java/com/yahoo/search/federation/DuplicateSourceTestCase.java index 514a087bed1..f911e215867 100644 --- a/container-search/src/test/java/com/yahoo/search/federation/DuplicateSourceTestCase.java +++ b/container-search/src/test/java/com/yahoo/search/federation/DuplicateSourceTestCase.java @@ -37,8 +37,7 @@ public class DuplicateSourceTestCase { SearchChainResolver resolver = new SearchChainResolver.Builder() .addSearchChain(new ComponentId(chain1), List.of(schema1, schema2)) .build(); - FederationSearcher searcher = new FederationSearcher(new ComponentId("test"), resolver, - Map.of(schema1, List.of(chain1), schema2, List.of(chain1))); + var searcher = new FederationSearcher(resolver, Map.of(schema1, List.of(chain1), schema2, List.of(chain1))); Result result = searcher.search(new Query("?query=test&sources=doc1%2cdoc2"), new Execution(Execution.Context.createContextStub(searchChains))); diff --git a/container-search/src/test/java/com/yahoo/search/federation/FederationSearcherTestCase.java b/container-search/src/test/java/com/yahoo/search/federation/FederationSearcherTestCase.java index af0e0ef69a8..5d2e19d02a3 100644 --- a/container-search/src/test/java/com/yahoo/search/federation/FederationSearcherTestCase.java +++ b/container-search/src/test/java/com/yahoo/search/federation/FederationSearcherTestCase.java @@ -284,7 +284,7 @@ public class FederationSearcherTestCase { builder.addSourceForProvider(news, provider1, provider1, true, options, List.of()); builder.addSourceForProvider(news, provider2, provider2, false, options, List.of()); - return new FederationSearcher(new ComponentId("federation"), builder.build(), Map.of()); + return new FederationSearcher(builder.build(), Map.of()); } private static class MockProvider extends Searcher { @@ -304,30 +304,4 @@ public class FederationSearcherTestCase { } - private static class QueryCheckSearcher extends Searcher { - - private static final String STATUS = "status"; - public static final String FEDERATION_SEARCHER_HAS_CLONED_THE_QUERY = "FederationSearcher has cloned the query."; - public static final String OK = "Got the correct query."; - private final Query query; - - QueryCheckSearcher(Query query) { - this.query = query; - } - - @Override - public Result search(Query query, Execution execution) { - Result result = new Result(query); - if (query != this.query) { - result.hits().addError(ErrorMessage - .createErrorInPluginSearcher(FEDERATION_SEARCHER_HAS_CLONED_THE_QUERY)); - } else { - final Hit h = new Hit("QueryCheckSearcher status hit"); - h.setField(STATUS, OK); - result.hits().add(h); - } - return result; - } - } - } diff --git a/container-search/src/test/java/com/yahoo/search/federation/FederationTester.java b/container-search/src/test/java/com/yahoo/search/federation/FederationTester.java index ec540e43dfd..6016e85bc17 100644 --- a/container-search/src/test/java/com/yahoo/search/federation/FederationTester.java +++ b/container-search/src/test/java/com/yahoo/search/federation/FederationTester.java @@ -47,7 +47,7 @@ class FederationTester { } FederationSearcher buildFederationSearcher() { - return new FederationSearcher(ComponentId.fromString("federation"), builder.build(), Map.of()); + return new FederationSearcher(builder.build(), Map.of()); } public Result search() { diff --git a/container-search/src/test/java/com/yahoo/search/searchchain/test/FutureDataTestCase.java b/container-search/src/test/java/com/yahoo/search/searchchain/test/FutureDataTestCase.java index 36c80e852d1..bfcde54d65b 100644 --- a/container-search/src/test/java/com/yahoo/search/searchchain/test/FutureDataTestCase.java +++ b/container-search/src/test/java/com/yahoo/search/searchchain/test/FutureDataTestCase.java @@ -39,11 +39,11 @@ public class FutureDataTestCase { Searcher syncProviderSearcher = new SyncProviderSearcher(); Chain<Searcher> asyncSource = new Chain<>(new ComponentId("async"), asyncProviderSearcher); Chain<Searcher> syncSource = new Chain<>(new ComponentId("sync"), syncProviderSearcher); - SearchChainResolver searchChainResolver = - new SearchChainResolver.Builder().addSearchChain(new ComponentId("sync"), new FederationOptions().setUseByDefault(true)). - addSearchChain(new ComponentId("async"), new FederationOptions().setUseByDefault(true)). - build(); - Chain<Searcher> main = new Chain<>(new FederationSearcher(new ComponentId("federator"), searchChainResolver, Map.of())); + var searchChainResolver = new SearchChainResolver.Builder() + .addSearchChain(new ComponentId("sync"), new FederationOptions().setUseByDefault(true)) + .addSearchChain(new ComponentId("async"), new FederationOptions().setUseByDefault(true)) + .build(); + Chain<Searcher> main = new Chain<>(new FederationSearcher(searchChainResolver, Map.of())); SearchChainRegistry searchChainRegistry = new SearchChainRegistry(); searchChainRegistry.register(main); searchChainRegistry.register(syncSource); diff --git a/container-search/src/test/java/com/yahoo/search/test/QueryTestCase.java b/container-search/src/test/java/com/yahoo/search/test/QueryTestCase.java index 6a310180eab..08cd653c1fc 100644 --- a/container-search/src/test/java/com/yahoo/search/test/QueryTestCase.java +++ b/container-search/src/test/java/com/yahoo/search/test/QueryTestCase.java @@ -1015,7 +1015,9 @@ public class QueryTestCase { @Test void testImplicitPhraseIsDefault() { Query query = new Query(httpEncode("?query=it's fine")); - assertEquals("WEAKAND(100) (SAND it s) fine", query.getModel().getQueryTree().toString()); + assertEquals("WEAKAND(100) it s fine", query.getModel().getQueryTree().toString()); + query = new Query(httpEncode("?query=\"it s\" fine")); + assertEquals("WEAKAND(100) \"it s\" fine", query.getModel().getQueryTree().toString()); } @Test @@ -1034,7 +1036,7 @@ public class QueryTestCase { } @Test - void testImplicitAnd() { + void testImplicitConnectivityInsideWeakAnd() { Query query = new Query(httpEncode("?query=myfield:it's myfield:a.b myfield:c")); SearchDefinition test = new SearchDefinition("test"); @@ -1045,9 +1047,38 @@ public class QueryTestCase { IndexModel indexModel = new IndexModel(test); query.getModel().setExecution(new Execution(Execution.Context.createContextStub(new IndexFacts(indexModel)))); - assertEquals("WEAKAND(100) (SAND myfield:it myfield:s) (AND myfield:a myfield:b) myfield:c", query.getModel().getQueryTree().toString()); + assertEquals("WEAKAND(100) myfield:it myfield:s myfield:a myfield:b myfield:c", query.getModel().getQueryTree().toString()); // 'it' and 's' should have connectivity 1 WeakAndItem root = (WeakAndItem) query.getModel().getQueryTree().getRoot(); + WordItem it = (WordItem) root.getItem(0); + assertEquals("it", it.getWord()); + WordItem s = (WordItem) root.getItem(1); + assertEquals("s", s.getWord()); + assertEquals(s, it.getConnectedItem()); + assertEquals(1.0, it.getConnectivity(), 0.00000001); + assertEquals(0.0, s.getConnectivity(), 0.00000001); + WordItem a = (WordItem) root.getItem(2); + assertEquals(1.0, a.getConnectivity(), 0.00000001); + WordItem b = (WordItem) root.getItem(3); + assertEquals(0.0, b.getConnectivity(), 0.00000001); + WordItem c = (WordItem) root.getItem(4); + } + + @Test + void testImplicitAnd() { + Query query = new Query(httpEncode("?query=myfield:it's myfield:a.b myfield:c&type=all")); + + SearchDefinition test = new SearchDefinition("test"); + Index myField = new Index("myfield"); + myField.addCommand("phrase-segmenting false"); + assertFalse(myField.getPhraseSegmenting()); + test.addIndex(myField); + IndexModel indexModel = new IndexModel(test); + query.getModel().setExecution(new Execution(Execution.Context.createContextStub(new IndexFacts(indexModel)))); + + assertEquals("AND (SAND myfield:it myfield:s) myfield:a myfield:b myfield:c", query.getModel().getQueryTree().toString()); + // 'it' and 's' should have connectivity 1 + AndItem root = (AndItem) query.getModel().getQueryTree().getRoot(); AndSegmentItem sand = (AndSegmentItem) root.getItem(0); WordItem it = (WordItem) sand.getItem(0); assertEquals("it", it.getWord()); @@ -1066,14 +1097,13 @@ public class QueryTestCase { IndexModel indexModel = new IndexModel(test); { - Query query = new Query(httpEncode("?query=myfield:b.c.d")); + Query query = new Query(httpEncode("?query=myfield:b.c.d&type=all")); query.getModel().setExecution(new Execution(Execution.Context.createContextStub(new IndexFacts(indexModel)))); - assertEquals("WEAKAND(100) (AND myfield:b myfield:c myfield:d)", query.getModel().getQueryTree().toString()); - WeakAndItem root = (WeakAndItem) query.getModel().getQueryTree().getRoot(); - AndItem and = (AndItem) root.getItem(0); - WordItem b = (WordItem) and.getItem(0); - WordItem c = (WordItem) and.getItem(1); - WordItem d = (WordItem) and.getItem(2); + assertEquals("AND myfield:b myfield:c myfield:d", query.getModel().getQueryTree().toString()); + AndItem root = (AndItem) query.getModel().getQueryTree().getRoot(); + WordItem b = (WordItem) root.getItem(0); + WordItem c = (WordItem) root.getItem(1); + WordItem d = (WordItem) root.getItem(2); assertEquals(c, b.getConnectedItem()); assertEquals(1.0, b.getConnectivity(), 0.00000001); assertEquals(d, c.getConnectedItem()); @@ -1081,16 +1111,15 @@ public class QueryTestCase { } { - Query query = new Query(httpEncode("?query=myfield:a myfield:b.c.d myfield:e")); + Query query = new Query(httpEncode("?query=myfield:a myfield:b.c.d myfield:e&type=all")); query.getModel().setExecution(new Execution(Execution.Context.createContextStub(new IndexFacts(indexModel)))); - assertEquals("WEAKAND(100) myfield:a (AND myfield:b myfield:c myfield:d) myfield:e", query.getModel().getQueryTree().toString()); - WeakAndItem root = (WeakAndItem) query.getModel().getQueryTree().getRoot(); + assertEquals("AND myfield:a myfield:b myfield:c myfield:d myfield:e", query.getModel().getQueryTree().toString()); + AndItem root = (AndItem) query.getModel().getQueryTree().getRoot(); WordItem a = (WordItem) root.getItem(0); - AndItem and = (AndItem) root.getItem(1); - WordItem b = (WordItem) and.getItem(0); - WordItem c = (WordItem) and.getItem(1); - WordItem d = (WordItem) and.getItem(2); - WordItem e = (WordItem) root.getItem(2); + WordItem b = (WordItem) root.getItem(1); + WordItem c = (WordItem) root.getItem(2); + WordItem d = (WordItem) root.getItem(3); + WordItem e = (WordItem) root.getItem(4); assertNull(a.getConnectedItem()); assertEquals(c, b.getConnectedItem()); assertEquals(1.0, b.getConnectivity(), 0.00000001); |