From bfcd156d488df1d8ce82bd575cf637328a214693 Mon Sep 17 00:00:00 2001 From: Henning Baldersheim Date: Wed, 14 Feb 2024 11:11:33 +0100 Subject: - Add a resolver for virtual sources. - Move sourceref tests out of separate test package. --- .../search/federation/FederationSearcher.java | 18 ++- .../federation/sourceref/SearchChainResolver.java | 4 - .../sourceref/VirtualSourceResolver.java | 65 +++++++++ .../sourceref/SearchChainResolverTestCase.java | 148 ++++++++++++++++++++ .../sourceref/SourceRefResolverTestCase.java | 111 +++++++++++++++ .../sourceref/VirtualSourceResolverTestCase.java | 29 ++++ .../test/SearchChainResolverTestCase.java | 150 --------------------- .../sourceref/test/SourceRefResolverTestCase.java | 113 ---------------- 8 files changed, 365 insertions(+), 273 deletions(-) create mode 100644 container-search/src/main/java/com/yahoo/search/federation/sourceref/VirtualSourceResolver.java create mode 100644 container-search/src/test/java/com/yahoo/search/federation/sourceref/SearchChainResolverTestCase.java create mode 100644 container-search/src/test/java/com/yahoo/search/federation/sourceref/SourceRefResolverTestCase.java create mode 100644 container-search/src/test/java/com/yahoo/search/federation/sourceref/VirtualSourceResolverTestCase.java delete mode 100644 container-search/src/test/java/com/yahoo/search/federation/sourceref/test/SearchChainResolverTestCase.java delete mode 100644 container-search/src/test/java/com/yahoo/search/federation/sourceref/test/SourceRefResolverTestCase.java 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 e92fd692cac..1f8f8757ebc 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 @@ -25,6 +25,7 @@ import com.yahoo.search.federation.sourceref.SingleTarget; import com.yahoo.search.federation.sourceref.SourceRefResolver; import com.yahoo.search.federation.sourceref.SourcesTarget; import com.yahoo.search.federation.sourceref.UnresolvedSearchChainException; +import com.yahoo.search.federation.sourceref.VirtualSourceResolver; import com.yahoo.search.query.Properties; import com.yahoo.search.result.ErrorMessage; import com.yahoo.search.result.Hit; @@ -77,25 +78,28 @@ public class FederationSearcher extends ForkingSearcher { private final SearchChainResolver searchChainResolver; private final SourceRefResolver sourceRefResolver; + private final VirtualSourceResolver virtualSourceResolver; private final TargetSelector targetSelector; private final Clock clock = Clock.systemUTC(); @Inject public FederationSearcher(FederationConfig config, ComponentRegistry targetSelectors) { - this(createResolver(config), resolveSelector(config.targetSelector(), targetSelectors)); + this(createResolver(config), VirtualSourceResolver.of(config), resolveSelector(config.targetSelector(), targetSelectors)); } // for testing public FederationSearcher(ComponentId id, SearchChainResolver searchChainResolver) { - this(searchChainResolver, null); + this(searchChainResolver, VirtualSourceResolver.of(), null); } private FederationSearcher(SearchChainResolver searchChainResolver, + VirtualSourceResolver virtualSourceResolver, TargetSelector targetSelector) { this.searchChainResolver = searchChainResolver; sourceRefResolver = new SourceRefResolver(searchChainResolver); this.targetSelector = targetSelector; + this.virtualSourceResolver = virtualSourceResolver; } private static TargetSelector resolveSelector(String selectorId, @@ -370,9 +374,9 @@ public class FederationSearcher extends ForkingSearcher { else { // fill timed out: Remove these hits as they are incomplete and may cause a race when accessed later result.hits().addError(futureFilledResult.getSecond().createTimeoutError()); for (Iterator i = futureFilledResult.getFirst().hits().unorderedDeepIterator(); i.hasNext(); ) { - // Note that some of these hits may be filled, but as the fill thread may still be working on them - // and we do not synchronize with it we need to discard all - Hit removed = result.hits().remove(i.next().getId()); + // Note that some of these hits may be filled, but as the fill thread may still be working on them, + // and we do not synchronize with it, we need to discard all. + result.hits().remove(i.next().getId()); } } } @@ -427,8 +431,10 @@ public class FederationSearcher extends ForkingSearcher { resolveSources(sources, properties, indexFacts); } - private Results resolveSources(Set sources, Properties properties, IndexFacts indexFacts) { + + private Results resolveSources(Set sourcesInQuery, Properties properties, IndexFacts indexFacts) { Results.Builder result = new Builder<>(); + Set sources = virtualSourceResolver.resolve(sourcesInQuery); for (String source : sources) { try { diff --git a/container-search/src/main/java/com/yahoo/search/federation/sourceref/SearchChainResolver.java b/container-search/src/main/java/com/yahoo/search/federation/sourceref/SearchChainResolver.java index a2bc12ddbd0..df91b968750 100644 --- a/container-search/src/main/java/com/yahoo/search/federation/sourceref/SearchChainResolver.java +++ b/container-search/src/main/java/com/yahoo/search/federation/sourceref/SearchChainResolver.java @@ -56,10 +56,6 @@ public class SearchChainResolver { } }; - public Builder addSearchChain(ComponentId searchChainId) { - return addSearchChain(searchChainId, List.of()); - } - public Builder addSearchChain(ComponentId searchChainId, FederationOptions federationOptions) { return addSearchChain(searchChainId, federationOptions, List.of()); } diff --git a/container-search/src/main/java/com/yahoo/search/federation/sourceref/VirtualSourceResolver.java b/container-search/src/main/java/com/yahoo/search/federation/sourceref/VirtualSourceResolver.java new file mode 100644 index 00000000000..fc07d12d429 --- /dev/null +++ b/container-search/src/main/java/com/yahoo/search/federation/sourceref/VirtualSourceResolver.java @@ -0,0 +1,65 @@ +// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.search.federation.sourceref; + +import com.yahoo.search.federation.FederationConfig; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Multiple sources like contentcluster.schema1, contencluster.schema2 needs to respond + * when source is the prefix contentcluster. This is done by generating map from virtual source + * to the fully qualified ones, and resolving from there. + * + * @author baldersheim + */ +public class VirtualSourceResolver { + private final Map> virtualSources; + private VirtualSourceResolver(Map> virtualSources) { + this.virtualSources = virtualSources; + } + public static VirtualSourceResolver of() { + return new VirtualSourceResolver(Map.of()); + } + public static VirtualSourceResolver of(Set targets) { + return new VirtualSourceResolver(createVirtualSources(targets)); + } + private static Map> createVirtualSources(Set targets) { + Set virtualSources = targets.stream() + .filter(id -> id.contains(".")) + .map(id -> id.substring(0, id.indexOf('.'))) + .collect(Collectors.toUnmodifiableSet()); + if (virtualSources.isEmpty()) return Map.of(); + Map> virtualSourceMap = new HashMap<>(); + for (String virtualSource : virtualSources) { + String prefix = virtualSource + "."; + Set sources = targets.stream() + .filter(id -> id.startsWith(prefix)) + .collect(Collectors.toUnmodifiableSet()); + virtualSourceMap.put(virtualSource, sources); + } + return virtualSourceMap; + } + public static VirtualSourceResolver of(FederationConfig config) { + return of(config.target().stream().map(FederationConfig.Target::id).collect(Collectors.toUnmodifiableSet())); + } + public Set resolve(Set sourcesInQuery) { + boolean hasMapping = sourcesInQuery.stream().anyMatch(virtualSources::containsKey); + if (hasMapping) { + Set resolved = new HashSet<>(); + for (String source : sourcesInQuery) { + var subSources = virtualSources.get(source); + if (subSources != null) { + resolved.addAll(subSources); + } else { + resolved.add(source); + } + } + return resolved; + } + return sourcesInQuery; + } +} diff --git a/container-search/src/test/java/com/yahoo/search/federation/sourceref/SearchChainResolverTestCase.java b/container-search/src/test/java/com/yahoo/search/federation/sourceref/SearchChainResolverTestCase.java new file mode 100644 index 00000000000..d9046075f38 --- /dev/null +++ b/container-search/src/test/java/com/yahoo/search/federation/sourceref/SearchChainResolverTestCase.java @@ -0,0 +1,148 @@ +// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.search.federation.sourceref; + +import com.yahoo.component.ComponentId; +import com.yahoo.component.ComponentSpecification; +import com.yahoo.processing.request.properties.PropertyMap; +import com.yahoo.processing.request.Properties; +import com.yahoo.search.searchchain.model.federation.FederationOptions; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.Iterator; +import java.util.SortedSet; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.fail; + +/** + * @author Tony Vaagenes + */ +public class SearchChainResolverTestCase { + + private static final FederationOptions federationOptions = + new FederationOptions().setTimeoutInMilliseconds(3000).setOptional(true); + + private static final ComponentId searchChainId = ComponentId.fromString("search-chain"); + private static final ComponentId providerId = ComponentId.fromString("provider"); + private static final ComponentId provider2Id = ComponentId.fromString("provider2"); + + private static final ComponentId sourceId = ComponentId.fromString("source"); + private static final ComponentId sourceChainInProviderId = + ComponentId.fromString("source-chain").nestInNamespace(providerId); + private static final ComponentId sourceChainInProvider2Id = + ComponentId.fromString("source-chain").nestInNamespace(provider2Id); + + private static final SearchChainResolver searchChainResolver; + + static { + SearchChainResolver.Builder builder = new SearchChainResolver.Builder(); + builder.addSearchChain(searchChainId, federationOptions.setUseByDefault(true), Collections.emptyList()); + builder.addSearchChain(providerId, federationOptions.setUseByDefault(false), Collections.emptyList()); + builder.addSourceForProvider(sourceId, providerId, sourceChainInProviderId, true, + federationOptions.setUseByDefault(true), Collections.emptyList()); + builder.addSourceForProvider(sourceId, provider2Id, sourceChainInProvider2Id, false, + federationOptions.setUseByDefault(false), Collections.emptyList()); + + searchChainResolver = builder.build(); + } + + @Test + void check_default_search_chains() { + assertEquals(2, searchChainResolver.defaultTargets().size()); + + Iterator iterator = searchChainResolver.defaultTargets().iterator(); + assertEquals(searchChainId.toString(), iterator.next().searchRefDescription()); + assertEquals(sourceChainInProviderId.toString(), iterator.next().searchRefDescription()); + } + + @Test + void require_error_message_for_invalid_source() { + try { + resolve("no-such-source"); + fail("Expected exception."); + } catch (UnresolvedSearchChainException e) { + assertEquals("Could not resolve source ref 'no-such-source'.", e.getMessage()); + } + } + + @Test + void lookup_search_chain() throws Exception { + SearchChainInvocationSpec res = resolve(searchChainId.getName()); + assertEquals(searchChainId, res.searchChainId); + } + + //TODO: TVT: @Test() + public void lookup_provider() throws Exception { + SearchChainInvocationSpec res = resolve(providerId.getName()); + assertEquals(providerId, res.provider); + assertNull(res.source); + assertEquals(providerId, res.searchChainId); + } + + @Test + void lookup_source() throws Exception { + SearchChainInvocationSpec res = resolve(sourceId.getName()); + assertIsSourceInProvider(res); + } + + @Test + void lookup_source_search_chain_directly() throws Exception { + SearchChainInvocationSpec res = resolve(sourceChainInProviderId.stringValue()); + assertIsSourceInProvider(res); + } + + private void assertIsSourceInProvider(SearchChainInvocationSpec res) { + assertEquals(providerId, res.provider); + assertEquals(sourceId, res.source); + assertEquals(sourceChainInProviderId, res.searchChainId); + } + + @Test + void lookup_source_for_provider2() throws Exception { + SearchChainInvocationSpec res = resolve(sourceId.getName(), provider2Id.getName()); + assertEquals(provider2Id, res.provider); + assertEquals(sourceId, res.source); + assertEquals(sourceChainInProvider2Id, res.searchChainId); + } + + @Test + void lists_source_ref_description_for_top_level_targets() { + SortedSet topLevelTargets = searchChainResolver.allTopLevelTargets(); + assertEquals(3, topLevelTargets.size()); + + Iterator i = topLevelTargets.iterator(); + assertSearchRefDescriptionIs(i.next(), providerId.toString()); + assertSearchRefDescriptionIs(i.next(), searchChainId.toString()); + assertSearchRefDescriptionIs(i.next(), "source[provider = provider, provider2]"); + } + + private void assertSearchRefDescriptionIs(Target target, String expected) { + assertEquals(expected, target.searchRefDescription()); + } + + static Properties emptySourceToProviderMap() { + return new PropertyMap(); + } + + private SearchChainInvocationSpec resolve(String sourceSpecification) throws UnresolvedSearchChainException { + return resolve(sourceSpecification, emptySourceToProviderMap()); + } + + private SearchChainInvocationSpec resolve(String sourceSpecification, String providerSpecification) + throws UnresolvedSearchChainException { + Properties sourceToProviderMap = emptySourceToProviderMap(); + sourceToProviderMap.set("source." + sourceSpecification + ".provider", providerSpecification); + return resolve(sourceSpecification, sourceToProviderMap); + } + + private SearchChainInvocationSpec resolve(String sourceSpecification, Properties sourceToProviderMap) + throws UnresolvedSearchChainException { + SearchChainInvocationSpec res = searchChainResolver.resolve( + ComponentSpecification.fromString(sourceSpecification), sourceToProviderMap); + assertEquals(federationOptions, res.federationOptions); + return res; + } + +} diff --git a/container-search/src/test/java/com/yahoo/search/federation/sourceref/SourceRefResolverTestCase.java b/container-search/src/test/java/com/yahoo/search/federation/sourceref/SourceRefResolverTestCase.java new file mode 100644 index 00000000000..9badd4a1ab6 --- /dev/null +++ b/container-search/src/test/java/com/yahoo/search/federation/sourceref/SourceRefResolverTestCase.java @@ -0,0 +1,111 @@ +// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.search.federation.sourceref; + +import com.yahoo.component.ComponentId; +import com.yahoo.component.ComponentSpecification; +import com.yahoo.prelude.IndexFacts; +import com.yahoo.prelude.IndexModel; +import com.yahoo.search.searchchain.model.federation.FederationOptions; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.TreeMap; + +import static com.yahoo.search.federation.sourceref.SearchChainResolverTestCase.emptySourceToProviderMap; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +/** + * Test for SourceRefResolver. + * + * @author Tony Vaagenes + */ +public class SourceRefResolverTestCase { + + private static final String cluster1 = "cluster1"; + private static final String cluster2 = "cluster2"; + private static final String cluster3 = "cluster3"; + private static IndexFacts indexFacts; + + private static final SourceRefResolver sourceRefResolver = createSourceRefResolver(); + + static { + setupIndexFacts(); + } + + private static SourceRefResolver createSourceRefResolver() { + SearchChainResolver.Builder builder = new SearchChainResolver.Builder(); + builder.addSearchChain(ComponentId.fromString(cluster1), new FederationOptions().setUseByDefault(true), + Collections.emptyList()); + builder.addSearchChain(ComponentId.fromString(cluster2), new FederationOptions().setUseByDefault(true), + Collections.emptyList()); + + return new SourceRefResolver(builder.build()); + } + + private static void setupIndexFacts() { + TreeMap> masterClusters = new TreeMap<>(); + masterClusters.put(cluster1, List.of("document1", "document2")); + masterClusters.put(cluster2, List.of("document1")); + masterClusters.put(cluster3, List.of("document3")); + indexFacts = new IndexFacts(new IndexModel(masterClusters, Collections.emptyList())); + } + + @Test + void check_test_assumptions() { + assertTrue(indexFacts.clustersHavingSearchDefinition("document1").containsAll(List.of("cluster1", "cluster2"))); + } + + @Test + void lookup_search_chain() throws Exception { + Set searchChains = resolve(cluster1); + assertEquals(1, searchChains.size()); + assertTrue(searchChainIds(searchChains).contains(cluster1)); + } + + @Test + void lookup_search_chains_for_document1() throws Exception { + Set searchChains = resolve("document1"); + assertEquals(2, searchChains.size()); + assertTrue(searchChainIds(searchChains).containsAll(List.of(cluster1, cluster2))); + } + + @Test + void error_when_document_gives_cluster_without_matching_search_chain() { + try { + resolve("document3"); + fail("Expected exception"); + } catch (UnresolvedSearchChainException e) { + assertEquals("Failed to resolve cluster search chain 'cluster3' " + + "when using source ref 'document3' as a document name.", + e.getMessage()); + } + } + + @Test + void error_when_no_document_or_search_chain() { + try { + resolve("document4"); + fail("Expected exception"); + } catch (UnresolvedSearchChainException e) { + assertEquals("Could not resolve source ref 'document4'.", e.getMessage()); + } + } + + private List searchChainIds(Set searchChains) { + List names = new ArrayList<>(); + for (SearchChainInvocationSpec searchChain : searchChains) { + names.add(searchChain.searchChainId.stringValue()); + } + return names; + } + + private Set resolve(String documentName) throws UnresolvedSearchChainException { + return sourceRefResolver.resolve(ComponentSpecification.fromString(documentName), emptySourceToProviderMap(), indexFacts); + } + +} diff --git a/container-search/src/test/java/com/yahoo/search/federation/sourceref/VirtualSourceResolverTestCase.java b/container-search/src/test/java/com/yahoo/search/federation/sourceref/VirtualSourceResolverTestCase.java new file mode 100644 index 00000000000..bf6605a43b2 --- /dev/null +++ b/container-search/src/test/java/com/yahoo/search/federation/sourceref/VirtualSourceResolverTestCase.java @@ -0,0 +1,29 @@ +// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.search.federation.sourceref; + +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; + +/** + * Test of VirtualSourceResolver + * + * @author baldersheim + */ +public class VirtualSourceResolverTestCase { + @Test + void testThatOriginalIsReturnedIfNoMapping() { + var input = Set.of("a","b", "b.c"); + assertSame(input, VirtualSourceResolver.of().resolve(input)); + assertSame(input, VirtualSourceResolver.of(Set.of("x.a","x.b")).resolve(input)); + } + @Test + void testResolution() { + var input = Set.of("a","b", "b.c"); + assertEquals(Set.of("a.x", "a.y", "b.c", "b.x"), + VirtualSourceResolver.of(Set.of("a.x","a.y", "b.x")).resolve(input)); + } +} diff --git a/container-search/src/test/java/com/yahoo/search/federation/sourceref/test/SearchChainResolverTestCase.java b/container-search/src/test/java/com/yahoo/search/federation/sourceref/test/SearchChainResolverTestCase.java deleted file mode 100644 index d575be603c1..00000000000 --- a/container-search/src/test/java/com/yahoo/search/federation/sourceref/test/SearchChainResolverTestCase.java +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.search.federation.sourceref.test; - -import com.yahoo.component.ComponentId; -import com.yahoo.component.ComponentSpecification; -import com.yahoo.processing.request.properties.PropertyMap; -import com.yahoo.processing.request.Properties; -import com.yahoo.search.federation.sourceref.SearchChainInvocationSpec; -import com.yahoo.search.federation.sourceref.SearchChainResolver; -import com.yahoo.search.federation.sourceref.Target; -import com.yahoo.search.federation.sourceref.UnresolvedSearchChainException; -import com.yahoo.search.searchchain.model.federation.FederationOptions; -import org.junit.jupiter.api.Test; - -import java.util.Collections; -import java.util.Iterator; -import java.util.SortedSet; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * @author Tony Vaagenes - */ -public class SearchChainResolverTestCase { - - private static final FederationOptions federationOptions = - new FederationOptions().setTimeoutInMilliseconds(3000).setOptional(true); - - private static final ComponentId searchChainId = ComponentId.fromString("search-chain"); - private static final ComponentId providerId = ComponentId.fromString("provider"); - private static final ComponentId provider2Id = ComponentId.fromString("provider2"); - - private static final ComponentId sourceId = ComponentId.fromString("source"); - private static final ComponentId sourceChainInProviderId = - ComponentId.fromString("source-chain").nestInNamespace(providerId); - private static final ComponentId sourceChainInProvider2Id = - ComponentId.fromString("source-chain").nestInNamespace(provider2Id); - - private static final SearchChainResolver searchChainResolver; - - static { - SearchChainResolver.Builder builder = new SearchChainResolver.Builder(); - builder.addSearchChain(searchChainId, federationOptions.setUseByDefault(true), Collections.emptyList()); - builder.addSearchChain(providerId, federationOptions.setUseByDefault(false), Collections.emptyList()); - builder.addSourceForProvider(sourceId, providerId, sourceChainInProviderId, true, - federationOptions.setUseByDefault(true), Collections.emptyList()); - builder.addSourceForProvider(sourceId, provider2Id, sourceChainInProvider2Id, false, - federationOptions.setUseByDefault(false), Collections.emptyList()); - - searchChainResolver = builder.build(); - } - - @Test - void check_default_search_chains() { - assertEquals(2, searchChainResolver.defaultTargets().size()); - - Iterator iterator = searchChainResolver.defaultTargets().iterator(); - assertEquals(searchChainId.toString(), iterator.next().searchRefDescription()); - assertEquals(sourceChainInProviderId.toString(), iterator.next().searchRefDescription()); - } - - @Test - void require_error_message_for_invalid_source() { - try { - resolve("no-such-source"); - fail("Expected exception."); - } catch (UnresolvedSearchChainException e) { - assertEquals("Could not resolve source ref 'no-such-source'.", e.getMessage()); - } - } - - @Test - void lookup_search_chain() throws Exception { - SearchChainInvocationSpec res = resolve(searchChainId.getName()); - assertEquals(searchChainId, res.searchChainId); - } - - //TODO: TVT: @Test() - public void lookup_provider() throws Exception { - SearchChainInvocationSpec res = resolve(providerId.getName()); - assertEquals(providerId, res.provider); - assertNull(res.source); - assertEquals(providerId, res.searchChainId); - } - - @Test - void lookup_source() throws Exception { - SearchChainInvocationSpec res = resolve(sourceId.getName()); - assertIsSourceInProvider(res); - } - - @Test - void lookup_source_search_chain_directly() throws Exception { - SearchChainInvocationSpec res = resolve(sourceChainInProviderId.stringValue()); - assertIsSourceInProvider(res); - } - - private void assertIsSourceInProvider(SearchChainInvocationSpec res) { - assertEquals(providerId, res.provider); - assertEquals(sourceId, res.source); - assertEquals(sourceChainInProviderId, res.searchChainId); - } - - @Test - void lookup_source_for_provider2() throws Exception { - SearchChainInvocationSpec res = resolve(sourceId.getName(), provider2Id.getName()); - assertEquals(provider2Id, res.provider); - assertEquals(sourceId, res.source); - assertEquals(sourceChainInProvider2Id, res.searchChainId); - } - - @Test - void lists_source_ref_description_for_top_level_targets() { - SortedSet topLevelTargets = searchChainResolver.allTopLevelTargets(); - assertEquals(3, topLevelTargets.size()); - - Iterator i = topLevelTargets.iterator(); - assertSearchRefDescriptionIs(i.next(), providerId.toString()); - assertSearchRefDescriptionIs(i.next(), searchChainId.toString()); - assertSearchRefDescriptionIs(i.next(), "source[provider = provider, provider2]"); - } - - private void assertSearchRefDescriptionIs(Target target, String expected) { - assertEquals(expected, target.searchRefDescription()); - } - - static Properties emptySourceToProviderMap() { - return new PropertyMap(); - } - - private SearchChainInvocationSpec resolve(String sourceSpecification) throws UnresolvedSearchChainException { - return resolve(sourceSpecification, emptySourceToProviderMap()); - } - - private SearchChainInvocationSpec resolve(String sourceSpecification, String providerSpecification) - throws UnresolvedSearchChainException { - Properties sourceToProviderMap = emptySourceToProviderMap(); - sourceToProviderMap.set("source." + sourceSpecification + ".provider", providerSpecification); - return resolve(sourceSpecification, sourceToProviderMap); - } - - private SearchChainInvocationSpec resolve(String sourceSpecification, Properties sourceToProviderMap) - throws UnresolvedSearchChainException { - SearchChainInvocationSpec res = searchChainResolver.resolve( - ComponentSpecification.fromString(sourceSpecification), sourceToProviderMap); - assertEquals(federationOptions, res.federationOptions); - return res; - } - -} diff --git a/container-search/src/test/java/com/yahoo/search/federation/sourceref/test/SourceRefResolverTestCase.java b/container-search/src/test/java/com/yahoo/search/federation/sourceref/test/SourceRefResolverTestCase.java deleted file mode 100644 index 1b3baebac6f..00000000000 --- a/container-search/src/test/java/com/yahoo/search/federation/sourceref/test/SourceRefResolverTestCase.java +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.search.federation.sourceref.test; - -import com.yahoo.component.ComponentId; -import com.yahoo.component.ComponentSpecification; -import com.yahoo.prelude.IndexFacts; -import com.yahoo.prelude.IndexModel; -import com.yahoo.search.federation.sourceref.SearchChainInvocationSpec; -import com.yahoo.search.federation.sourceref.SearchChainResolver; -import com.yahoo.search.federation.sourceref.SourceRefResolver; -import com.yahoo.search.federation.sourceref.UnresolvedSearchChainException; -import com.yahoo.search.searchchain.model.federation.FederationOptions; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.TreeMap; - -import static com.yahoo.search.federation.sourceref.test.SearchChainResolverTestCase.emptySourceToProviderMap; -import static org.junit.jupiter.api.Assertions.*; - -/** - * Test for SourceRefResolver. - * - * @author Tony Vaagenes - */ -public class SourceRefResolverTestCase { - - private static final String cluster1 = "cluster1"; - private static final String cluster2 = "cluster2"; - private static final String cluster3 = "cluster3"; - private static IndexFacts indexFacts; - - private static final SourceRefResolver sourceRefResolver = createSourceRefResolver(); - - static { - setupIndexFacts(); - } - - private static SourceRefResolver createSourceRefResolver() { - SearchChainResolver.Builder builder = new SearchChainResolver.Builder(); - builder.addSearchChain(ComponentId.fromString(cluster1), new FederationOptions().setUseByDefault(true), - Collections.emptyList()); - builder.addSearchChain(ComponentId.fromString(cluster2), new FederationOptions().setUseByDefault(true), - Collections.emptyList()); - - return new SourceRefResolver(builder.build()); - } - - private static void setupIndexFacts() { - TreeMap> masterClusters = new TreeMap<>(); - masterClusters.put(cluster1, List.of("document1", "document2")); - masterClusters.put(cluster2, List.of("document1")); - masterClusters.put(cluster3, List.of("document3")); - indexFacts = new IndexFacts(new IndexModel(masterClusters, Collections.emptyList())); - } - - @Test - void check_test_assumptions() { - assertTrue(indexFacts.clustersHavingSearchDefinition("document1").containsAll(List.of("cluster1", "cluster2"))); - } - - @Test - void lookup_search_chain() throws Exception { - Set searchChains = resolve(cluster1); - assertEquals(1, searchChains.size()); - assertTrue(searchChainIds(searchChains).contains(cluster1)); - } - - @Test - void lookup_search_chains_for_document1() throws Exception { - Set searchChains = resolve("document1"); - assertEquals(2, searchChains.size()); - assertTrue(searchChainIds(searchChains).containsAll(List.of(cluster1, cluster2))); - } - - @Test - void error_when_document_gives_cluster_without_matching_search_chain() { - try { - resolve("document3"); - fail("Expected exception"); - } catch (UnresolvedSearchChainException e) { - assertEquals("Failed to resolve cluster search chain 'cluster3' " + - "when using source ref 'document3' as a document name.", - e.getMessage()); - } - } - - @Test - void error_when_no_document_or_search_chain() { - try { - resolve("document4"); - fail("Expected exception"); - } catch (UnresolvedSearchChainException e) { - assertEquals("Could not resolve source ref 'document4'.", e.getMessage()); - } - } - - private List searchChainIds(Set searchChains) { - List names = new ArrayList<>(); - for (SearchChainInvocationSpec searchChain : searchChains) { - names.add(searchChain.searchChainId.stringValue()); - } - return names; - } - - private Set resolve(String documentName) throws UnresolvedSearchChainException { - return sourceRefResolver.resolve(ComponentSpecification.fromString(documentName), emptySourceToProviderMap(), indexFacts); - } - -} -- cgit v1.2.3