// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.prelude.test; import com.yahoo.config.subscription.ConfigGetter; import com.yahoo.language.process.StemMode; import com.yahoo.prelude.Index; import com.yahoo.prelude.IndexFacts; import com.yahoo.prelude.IndexModel; import com.yahoo.prelude.SearchDefinition; import com.yahoo.search.Query; import com.yahoo.search.config.IndexInfoConfig; import com.yahoo.search.searchchain.Execution; import org.junit.jupiter.api.Test; import java.util.Collection; import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.*; /** * Tests using synthetic index names for IndexFacts class. * * @author Steinar Knutsen */ public class IndexFactsTestCase { private static final String INDEXFACTS_TESTING = "file:src/test/java/com/yahoo/prelude/test/indexfactstesting.cfg"; @SuppressWarnings("deprecation") private IndexFacts createIndexFacts() { ConfigGetter getter = new ConfigGetter<>(IndexInfoConfig.class); IndexInfoConfig config = getter.getConfig(INDEXFACTS_TESTING); return new IndexFacts(new IndexModel(config, createClusters())); } private IndexFacts createIndexFacts(Collection searchDefinitions) { return new IndexFacts(new IndexModel(createClusters(), searchDefinitions)); } private Map> createClusters() { return Map.of("clusterOne", List.of("one", "two"), "clusterTwo", List.of("one", "three")); } @Test void testBasicCases() { // First check default behavior IndexFacts indexFacts = createIndexFacts(); 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()); } @Test void testDefaultPosition() { Index a = new Index("a"); assertFalse(a.isDefaultPosition()); a.addCommand("any"); assertFalse(a.isDefaultPosition()); a.addCommand("default-position"); assertTrue(a.isDefaultPosition()); SearchDefinition sd = new SearchDefinition("sd"); sd.addCommand("b", "any"); assertNull(sd.getDefaultPosition()); sd.addCommand("c", "default-position"); assertEquals(sd.getDefaultPosition(), "c"); SearchDefinition sd2 = new SearchDefinition("sd2"); sd2.addIndex(new Index("b").addCommand("any")); assertNull(sd2.getDefaultPosition()); sd2.addIndex(a); assertEquals(sd2.getDefaultPosition(), "a"); IndexFacts indexFacts = createIndexFacts(List.of(sd, sd2)); assertEquals(indexFacts.getDefaultPosition(null), "a"); assertEquals(indexFacts.getDefaultPosition("sd"), "c"); } @Test void testIndicesInAnyConfigurationAreIndicesInDefault() { IndexFacts.Session indexFacts = createIndexFacts().newSession(new Query()); assertTrue(indexFacts.isIndex("a")); assertTrue(indexFacts.isIndex("b")); assertTrue(indexFacts.isIndex("c")); assertTrue(indexFacts.isIndex("d")); assertFalse(indexFacts.isIndex("anythingelse")); } @Test void testDefaultIsUnionHostIndex() { IndexFacts.Session session = createIndexFacts().newSession(new Query()); assertTrue(session.getIndex("c").isHostIndex()); assertFalse(session.getIndex("a").isHostIndex()); } @Test void testDefaultIsUnionUriIndex() { IndexFacts indexFacts = createIndexFacts(); assertTrue(indexFacts.newSession(new Query()).getIndex("d").isUriIndex()); assertFalse(indexFacts.newSession(new Query()).getIndex("a").isUriIndex()); } @Test void testDefaultIsUnionStemMode() { IndexFacts.Session session = createIndexFacts().newSession(new Query()); assertEquals(StemMode.NONE, session.getIndex("a").getStemMode()); assertEquals(StemMode.NONE, session.getIndex("b").getStemMode()); } private void assertExactIsWorking(String indexName) { SearchDefinition sd = new SearchDefinition("artist"); Index index = new Index(indexName); index.setExact(true,"^^^"); sd.addIndex(index); IndexFacts indexFacts = new IndexFacts(new IndexModel(sd)); Query query = new Query(); query.getModel().getSources().add("artist"); assertTrue(indexFacts.newSession(query).getIndex(indexName).isExact()); Query q = newQuery("?query=" + indexName + ":foo...&search=artist&type=all", indexFacts); assertEquals(indexName + ":foo...", q.getModel().getQueryTree().getRoot().toString()); } @Test void testExactMatching() { assertExactIsWorking("test"); assertExactIsWorking("artist_name_ft_norm1"); Query query = new Query(); query.getModel().getSources().add("three"); IndexFacts.Session threeSession = createIndexFacts().newSession(query); IndexFacts.Session nullSession = createIndexFacts().newSession(new Query()); Index d3 = threeSession.getIndex("d"); assertTrue(d3.isExact()); assertEquals(" ", d3.getExactTerminator()); Index e = nullSession.getIndex("e"); assertTrue(e.isExact()); assertEquals("kj(/&", e.getExactTerminator()); Index a = nullSession.getIndex("a"); assertFalse(a.isExact()); assertNull(a.getExactTerminator()); Index wem = threeSession.getIndex("twewm"); assertTrue(wem.isExact()); assertNull(wem.getExactTerminator()); } @Test void testComplexExactMatching() { SearchDefinition sd = new SearchDefinition("foobar"); String u_name = "foo_bar"; Index u_index = new Index(u_name); u_index.setExact(true, "^^^"); Index b_index = new Index("bar"); sd.addIndex(u_index); sd.addIndex(b_index); IndexFacts indexFacts = new IndexFacts(new IndexModel(sd)); Query query = new Query(); query.getModel().getSources().add("foobar"); IndexFacts.Session session = indexFacts.newSession(query); assertFalse(session.getIndex("bar").isExact()); assertTrue(session.getIndex(u_name).isExact()); Query q = newQuery("?query=" + u_name + ":foo...&search=foobar&type=all", indexFacts); assertEquals(u_name + ":foo...", q.getModel().getQueryTree().getRoot().toString()); } // This is also backed by a system test on cause of complex config @Test void testRestrictLists1() { Query query = new Query(); query.getModel().getSources().add("nalle"); query.getModel().getSources().add("one"); query.getModel().getRestrict().add("two"); IndexFacts.Session indexFacts = createIndexFacts().newSession(List.of("clusterOne"), List.of()); assertTrue(indexFacts.isIndex("a")); assertFalse(indexFacts.isIndex("b")); assertTrue(indexFacts.isIndex("d")); } @Test void testRestrictLists2() { Query query = new Query(); query.getModel().getSources().add("clusterTwo"); query.getModel().getRestrict().add("three"); IndexFacts indexFacts = createIndexFacts(); IndexFacts.Session session = indexFacts.newSession(query); assertFalse(session.getIndex("c").isNull()); assertTrue(session.getIndex("e").isNull()); assertEquals("c", session.getCanonicName("C")); assertTrue(session.getIndex("c").isHostIndex()); assertFalse(session.getIndex("a").isNull()); assertFalse(session.getIndex("a").isHostIndex()); assertEquals(StemMode.SHORTEST, session.getIndex("a").getStemMode()); assertFalse(session.getIndex("b").isNull()); assertFalse(session.getIndex("b").isUriIndex()); assertFalse(session.getIndex("b").isHostIndex()); assertEquals(StemMode.NONE, session.getIndex("b").getStemMode()); } @Test void testRestrictLists3() { Query query = new Query(); query.getModel().getSources().add("clusterOne"); query.getModel().getRestrict().add("two"); IndexFacts indexFacts = createIndexFacts(); IndexFacts.Session session = indexFacts.newSession(query); assertTrue(session.getIndex("a").isNull()); assertFalse(session.getIndex("d").isNull()); assertTrue(session.getIndex("d").isUriIndex()); assertTrue(session.getIndex("e").isExact()); } private Query newQuery(String queryString, IndexFacts indexFacts) { Query query = new Query(queryString); query.getModel().setExecution(new Execution(Execution.Context.createContextStub(indexFacts))); return query; } @Test void testPredicateBounds() { Index index = new Index("a"); assertEquals(Long.MIN_VALUE, index.getPredicateLowerBound()); assertEquals(Long.MAX_VALUE, index.getPredicateUpperBound()); index.addCommand("predicate-bounds [2..300]"); assertEquals(2L, index.getPredicateLowerBound()); assertEquals(300L, index.getPredicateUpperBound()); index.addCommand("predicate-bounds [-20000..30000]"); assertEquals(-20_000L, index.getPredicateLowerBound()); assertEquals(30_000L, index.getPredicateUpperBound()); index.addCommand("predicate-bounds [-40000000000..-300]"); assertEquals(-40_000_000_000L, index.getPredicateLowerBound()); assertEquals(-300L, index.getPredicateUpperBound()); index.addCommand("predicate-bounds [..300]"); assertEquals(Long.MIN_VALUE, index.getPredicateLowerBound()); assertEquals(300L, index.getPredicateUpperBound()); index.addCommand("predicate-bounds [2..]"); assertEquals(2L, index.getPredicateLowerBound()); assertEquals(Long.MAX_VALUE, index.getPredicateUpperBound()); } @Test void testUriIndexAndRestrict() { IndexInfoConfig.Builder b = new IndexInfoConfig.Builder(); IndexInfoConfig.Indexinfo.Builder b1 = new IndexInfoConfig.Indexinfo.Builder(); b1.name("hasUri"); IndexInfoConfig.Indexinfo.Command.Builder bb1 = new IndexInfoConfig.Indexinfo.Command.Builder(); bb1.indexname("url"); bb1.command("fullurl"); b1.command(bb1); b.indexinfo(b1); IndexInfoConfig.Indexinfo.Builder b2 = new IndexInfoConfig.Indexinfo.Builder(); b2.name("hasNotUri1"); b.indexinfo(b2); IndexInfoConfig.Indexinfo.Builder b3 = new IndexInfoConfig.Indexinfo.Builder(); b3.name("hasNotUri2"); b.indexinfo(b3); IndexInfoConfig config = new IndexInfoConfig(b); IndexFacts indexFacts = new IndexFacts(new IndexModel(config, Map.of())); Query query1 = new Query("?query=url:https://foo.bar"); Query query2 = new Query("?query=url:https://foo.bar&restrict=hasUri"); assertEquals(0, query1.getModel().getRestrict().size()); assertEquals(1, query2.getModel().getRestrict().size()); IndexFacts.Session session1 = indexFacts.newSession(query1.getModel().getSources(), query1.getModel().getRestrict()); 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()); } @Test void testConflictingAliases() { SearchDefinition first = new SearchDefinition("first"); Index field1 = new Index("field1"); first.addIndex(field1); SearchDefinition second = new SearchDefinition("second"); Index field2 = new Index("field2"); field2.addAlias("field1"); second.addIndex(field2); // Alias to field1 conflics with field1 in the "union" search definition. // Should not produce an exception (but a log message): new IndexFacts(new IndexModel(Map.of(), List.of(first, second))); } }