diff options
author | Henning Baldersheim <balder@yahoo-inc.com> | 2016-11-25 14:32:21 +0100 |
---|---|---|
committer | Henning Baldersheim <balder@yahoo-inc.com> | 2017-01-25 18:55:21 +0100 |
commit | c19fb20b2bdb12ed6e1c1aa5cc7b9e7efb033b84 (patch) | |
tree | 538ad4eba247a9d74ac5c53befc80fa4898e967e /container-search/src | |
parent | 29ee9162845ffaeb01f22a242d2fc7ab9593530e (diff) |
Add the softtimeout query control.
Diffstat (limited to 'container-search/src')
5 files changed, 209 insertions, 37 deletions
diff --git a/container-search/src/main/java/com/yahoo/search/query/Ranking.java b/container-search/src/main/java/com/yahoo/search/query/Ranking.java index e543589f74d..addd759cd29 100644 --- a/container-search/src/main/java/com/yahoo/search/query/Ranking.java +++ b/container-search/src/main/java/com/yahoo/search/query/Ranking.java @@ -37,6 +37,7 @@ public class Ranking implements Cloneable { public static final String QUERYCACHE = "queryCache"; public static final String MATCH_PHASE = "matchPhase"; public static final String DIVERSITY = "diversity"; + public static final String SOFTTIMEOUT = "softtimeout"; public static final String FEATURES = "features"; public static final String PROPERTIES = "properties"; @@ -82,6 +83,8 @@ public class Ranking implements Cloneable { private MatchPhase matchPhase = new MatchPhase(); + private SoftTimeout softTimeout = new SoftTimeout(); + public Ranking(Query parent) { this.parent = parent; } @@ -169,6 +172,9 @@ public class Ranking implements Cloneable { /** Returns the match phase rank settings of this. This is never null. */ public MatchPhase getMatchPhase() { return matchPhase; } + /** Returns the soft timeout settings of this. This is never null. */ + public SoftTimeout getSoftTimeout() { return softTimeout; } + @Override public Object clone() { try { @@ -179,6 +185,7 @@ public class Ranking implements Cloneable { clone.rankProperties = this.rankProperties.clone(); clone.rankFeatures = this.rankFeatures.clone(); clone.matchPhase = this.matchPhase.clone(); + clone.softTimeout = this.softTimeout.clone(); return clone; } catch (CloneNotSupportedException e) { @@ -208,6 +215,7 @@ public class Ranking implements Cloneable { hash += 11 * rankFeatures.hashCode(); hash += 13 * rankProperties.hashCode(); hash += 17 * matchPhase.hashCode(); + hash += 19 * softTimeout.hashCode(); return Ranking.class.hashCode() + QueryHelper.combineHash(sorting,location,profile,hash); } @@ -232,6 +240,7 @@ public class Ranking implements Cloneable { public void prepare() { rankFeatures.prepare(rankProperties); matchPhase.prepare(rankProperties); + softTimeout.prepare(rankProperties); prepareNow(freshness); } diff --git a/container-search/src/main/java/com/yahoo/search/query/SoftTimeout.java b/container-search/src/main/java/com/yahoo/search/query/SoftTimeout.java new file mode 100644 index 00000000000..01cc460fbeb --- /dev/null +++ b/container-search/src/main/java/com/yahoo/search/query/SoftTimeout.java @@ -0,0 +1,101 @@ +package com.yahoo.search.query; + +import com.yahoo.search.query.profile.types.FieldDescription; +import com.yahoo.search.query.profile.types.QueryProfileType; +import com.yahoo.search.query.ranking.RankProperties; + +import java.util.Objects; + +/** + * Holds the settings for the soft-timeout feature. + * + * @author balder + */ +public class SoftTimeout implements Cloneable { + /** The type representing the property arguments consumed by this */ + private static final QueryProfileType argumentType; + + public static final String ENABLE = "enable"; + public static final String FACTOR = "factor"; + public static final String TAILCOST = "tailcost"; + + + static { + argumentType =new QueryProfileType(Ranking.SOFTTIMEOUT); + argumentType.setStrict(true); + argumentType.setBuiltin(true); + argumentType.addField(new FieldDescription(TAILCOST, "double")); + argumentType.addField(new FieldDescription(ENABLE, "boolean")); + argumentType.freeze(); + } + public static QueryProfileType getArgumentType() { return argumentType; } + + public Boolean enable = null; + private Double factor = null; + private Double tailcost = null; + + public void setEnable(boolean enable) { this.enable = enable; } + + public Boolean getEnable() { return enable; } + + public void setFactor(double factor) { + if ((factor < 0.0) || (factor > 1.0)) { + throw new IllegalArgumentException("factor must be in the range [0.0, 1.0]. It is " + factor); + } + this.factor = factor; + } + public Double getFactor() { return factor; } + public void setTailcost(double tailcost) { + if ((tailcost < 0.0) || (tailcost > 1.0)) { + throw new IllegalArgumentException("tailcost must be in the range [0.0, 1.0]. It is " + tailcost); + } + this.tailcost = tailcost; + } + public Double getTailcost() { return tailcost; } + + /** Internal operation - DO NOT USE */ + public void prepare(RankProperties rankProperties) { + + if (enable != null) { + rankProperties.put("vespa.softtimeout.enable", String.valueOf(enable)); + } + if (factor != null) { + rankProperties.put("vespa.softtimeout.factor", String.valueOf(factor)); + } + if (tailcost != null) { + rankProperties.put("vespa.softtimeout.tailcost", String.valueOf(tailcost)); + } + } + + @Override + public SoftTimeout clone() { + try { + return (SoftTimeout) super.clone(); + } + catch (CloneNotSupportedException e) { + throw new RuntimeException("Won't happen", e); + } + } + + @Override + public int hashCode() { + int hash = 0; + if (enable != null) hash += 11 * enable.hashCode(); + if (factor != null) hash += 13 * factor.hashCode(); + if (tailcost != null) hash += 17 * tailcost.hashCode(); + return hash; + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if ( ! (o instanceof SoftTimeout)) return false; + + SoftTimeout other = (SoftTimeout)o; + if ( ! Objects.equals(this.enable, other.enable)) return false; + if ( ! Objects.equals(this.factor, other.factor)) return false; + if ( ! Objects.equals(this.tailcost, other.tailcost)) return false; + return true; + } + +} diff --git a/container-search/src/main/java/com/yahoo/search/query/properties/QueryProperties.java b/container-search/src/main/java/com/yahoo/search/query/properties/QueryProperties.java index 5abc6663063..30154b223a5 100644 --- a/container-search/src/main/java/com/yahoo/search/query/properties/QueryProperties.java +++ b/container-search/src/main/java/com/yahoo/search/query/properties/QueryProperties.java @@ -74,34 +74,36 @@ public class QueryProperties extends Properties { public Object get(CompoundName key, Map<String,String> context, com.yahoo.processing.request.Properties substitution) { if (key.size()==2 && key.first().equals(Model.MODEL)) { - if (key.last().equals(Model.QUERY_STRING)) return query.getModel().getQueryString(); - if (key.last().equals(Model.TYPE)) return query.getModel().getType(); - if (key.last().equals(Model.FILTER)) return query.getModel().getFilter(); - if (key.last().equals(Model.DEFAULT_INDEX)) return query.getModel().getDefaultIndex(); - if (key.last().equals(Model.LANGUAGE)) return query.getModel().getLanguage(); - if (key.last().equals(Model.ENCODING)) return query.getModel().getEncoding(); - if (key.last().equals(Model.SOURCES)) return query.getModel().getSources(); - if (key.last().equals(Model.SEARCH_PATH)) return query.getModel().getSearchPath(); - if (key.last().equals(Model.RESTRICT)) return query.getModel().getRestrict(); + Model model = query.getModel(); + if (key.last().equals(Model.QUERY_STRING)) return model.getQueryString(); + if (key.last().equals(Model.TYPE)) return model.getType(); + if (key.last().equals(Model.FILTER)) return model.getFilter(); + if (key.last().equals(Model.DEFAULT_INDEX)) return model.getDefaultIndex(); + if (key.last().equals(Model.LANGUAGE)) return model.getLanguage(); + if (key.last().equals(Model.ENCODING)) return model.getEncoding(); + if (key.last().equals(Model.SOURCES)) return model.getSources(); + if (key.last().equals(Model.SEARCH_PATH)) return model.getSearchPath(); + if (key.last().equals(Model.RESTRICT)) return model.getRestrict(); } else if (key.first().equals(Ranking.RANKING)) { + Ranking ranking = query.getRanking(); if (key.size()==2) { - if (key.last().equals(Ranking.LOCATION)) return query.getRanking().getLocation(); - if (key.last().equals(Ranking.PROFILE)) return query.getRanking().getProfile(); - if (key.last().equals(Ranking.SORTING)) return query.getRanking().getSorting(); - if (key.last().equals(Ranking.FRESHNESS)) return query.getRanking().getFreshness(); - if (key.last().equals(Ranking.QUERYCACHE)) return query.getRanking().getQueryCache(); - if (key.last().equals(Ranking.LIST_FEATURES)) return query.getRanking().getListFeatures(); + if (key.last().equals(Ranking.LOCATION)) return ranking.getLocation(); + if (key.last().equals(Ranking.PROFILE)) return ranking.getProfile(); + if (key.last().equals(Ranking.SORTING)) return ranking.getSorting(); + if (key.last().equals(Ranking.FRESHNESS)) return ranking.getFreshness(); + if (key.last().equals(Ranking.QUERYCACHE)) return ranking.getQueryCache(); + if (key.last().equals(Ranking.LIST_FEATURES)) return ranking.getListFeatures(); } else if (key.size()>=3 && key.get(1).equals(Ranking.MATCH_PHASE)) { if (key.size() == 3) { - MatchPhase matchPhase = query.getRanking().getMatchPhase(); + MatchPhase matchPhase = ranking.getMatchPhase(); if (key.last().equals(MatchPhase.ATTRIBUTE)) return matchPhase.getAttribute(); if (key.last().equals(MatchPhase.ASCENDING)) return matchPhase.getAscending(); if (key.last().equals(MatchPhase.MAX_HITS)) return matchPhase.getMaxHits(); if (key.last().equals(MatchPhase.MAX_FILTER_COVERAGE)) return matchPhase.getMaxFilterCoverage(); } else if (key.size() >= 4 && key.get(2).equals(Ranking.DIVERSITY)) { - Diversity diversity = query.getRanking().getMatchPhase().getDiversity(); + Diversity diversity = ranking.getMatchPhase().getDiversity(); if (key.size() == 4) { if (key.last().equals(Diversity.ATTRIBUTE)) return diversity.getAttribute(); if (key.last().equals(Diversity.MINGROUPS)) return diversity.getMinGroups(); @@ -111,10 +113,16 @@ public class QueryProperties extends Properties { } } } + else if (key.size() == 3 && key.get(1).equals(Ranking.SOFTTIMEOUT)) { + SoftTimeout soft = ranking.getSoftTimeout(); + if (key.last().equals(SoftTimeout.ENABLE)) return soft.getEnable(); + if (key.last().equals(SoftTimeout.FACTOR)) return soft.getFactor(); + if (key.last().equals(SoftTimeout.TAILCOST)) return soft.getTailcost(); + } else if (key.size()>2) { // pass the portion after "ranking.features/properties" down - if (key.get(1).equals(Ranking.FEATURES)) return query.getRanking().getFeatures().getObject(key.rest().rest().toString()); - if (key.get(1).equals(Ranking.PROPERTIES)) return query.getRanking().getProperties().get(key.rest().rest().toString()); + if (key.get(1).equals(Ranking.FEATURES)) return ranking.getFeatures().getObject(key.rest().rest().toString()); + if (key.get(1).equals(Ranking.PROPERTIES)) return ranking.getProperties().get(key.rest().rest().toString()); } } else if (key.size()==2 && key.first().equals(Presentation.PRESENTATION)) { @@ -149,45 +157,47 @@ public class QueryProperties extends Properties { // Note: The defaults here are never used try { if (key.size()==2 && key.first().equals(Model.MODEL)) { + Model model = query.getModel(); if (key.last().equals(Model.QUERY_STRING)) - query.getModel().setQueryString(asString(value, "")); + model.setQueryString(asString(value, "")); else if (key.last().equals(Model.TYPE)) - query.getModel().setType(asString(value, "ANY")); + model.setType(asString(value, "ANY")); else if (key.last().equals(Model.FILTER)) - query.getModel().setFilter(asString(value, "")); + model.setFilter(asString(value, "")); else if (key.last().equals(Model.DEFAULT_INDEX)) - query.getModel().setDefaultIndex(asString(value, "")); + model.setDefaultIndex(asString(value, "")); else if (key.last().equals(Model.LANGUAGE)) - query.getModel().setLanguage(asString(value, "")); + model.setLanguage(asString(value, "")); else if (key.last().equals(Model.ENCODING)) - query.getModel().setEncoding(asString(value,"")); + model.setEncoding(asString(value,"")); else if (key.last().equals(Model.SEARCH_PATH)) - query.getModel().setSearchPath(asString(value,"")); + model.setSearchPath(asString(value,"")); else if (key.last().equals(Model.SOURCES)) - query.getModel().setSources(asString(value,"")); + model.setSources(asString(value,"")); else if (key.last().equals(Model.RESTRICT)) - query.getModel().setRestrict(asString(value,"")); + model.setRestrict(asString(value,"")); else throwIllegalParameter(key.last(),Model.MODEL); } else if (key.first().equals(Ranking.RANKING)) { + Ranking ranking = query.getRanking(); if (key.size()==2) { if (key.last().equals(Ranking.LOCATION)) - query.getRanking().setLocation(asString(value,"")); + ranking.setLocation(asString(value,"")); else if (key.last().equals(Ranking.PROFILE)) - query.getRanking().setProfile(asString(value,"")); + ranking.setProfile(asString(value,"")); else if (key.last().equals(Ranking.SORTING)) - query.getRanking().setSorting(asString(value,"")); + ranking.setSorting(asString(value,"")); else if (key.last().equals(Ranking.FRESHNESS)) - query.getRanking().setFreshness(asString(value, "")); + ranking.setFreshness(asString(value, "")); else if (key.last().equals(Ranking.QUERYCACHE)) - query.getRanking().setQueryCache(asBoolean(value, false)); + ranking.setQueryCache(asBoolean(value, false)); else if (key.last().equals(Ranking.LIST_FEATURES)) - query.getRanking().setListFeatures(asBoolean(value,false)); + ranking.setListFeatures(asBoolean(value,false)); } else if (key.size()>=3 && key.get(1).equals(Ranking.MATCH_PHASE)) { if (key.size() == 3) { - MatchPhase matchPhase = query.getRanking().getMatchPhase(); + MatchPhase matchPhase = ranking.getMatchPhase(); if (key.last().equals(MatchPhase.ATTRIBUTE)) { matchPhase.setAttribute(asString(value, null)); } else if (key.last().equals(MatchPhase.ASCENDING)) { @@ -198,7 +208,7 @@ public class QueryProperties extends Properties { matchPhase.setMaxFilterCoverage(asDouble(value, 0.2)); } } else if (key.size() > 3 && key.get(2).equals(Ranking.DIVERSITY)) { - Diversity diversity = query.getRanking().getMatchPhase().getDiversity(); + Diversity diversity = ranking.getMatchPhase().getDiversity(); if (key.last().equals(Diversity.ATTRIBUTE)) { diversity.setAttribute(asString(value, null)); } else if (key.last().equals(Diversity.MINGROUPS)) { @@ -212,12 +222,18 @@ public class QueryProperties extends Properties { } } } + else if (key.size() == 3 && key.get(1).equals(Ranking.SOFTTIMEOUT)) { + SoftTimeout soft = ranking.getSoftTimeout(); + if (key.last().equals(SoftTimeout.ENABLE)) soft.setEnable(asBoolean(value, false)); + if (key.last().equals(SoftTimeout.FACTOR)) soft.setFactor(asDouble(value, 0.50)); + if (key.last().equals(SoftTimeout.TAILCOST)) soft.setTailcost(asDouble(value, 0.10)); + } else if (key.size()>2) { String restKey = key.rest().rest().toString(); if (key.get(1).equals(Ranking.FEATURES)) setRankingFeature(query, restKey, toSpecifiedType(restKey, value, profileRegistry.getTypeRegistry().getComponent("features"))); else if (key.get(1).equals(Ranking.PROPERTIES)) - query.getRanking().getProperties().put(restKey, toSpecifiedType(restKey, value, profileRegistry.getTypeRegistry().getComponent("properties"))); + ranking.getProperties().put(restKey, toSpecifiedType(restKey, value, profileRegistry.getTypeRegistry().getComponent("properties"))); else throwIllegalParameter(key.rest().toString(),Ranking.RANKING); } diff --git a/container-search/src/test/java/com/yahoo/search/query/SoftTimeoutTestCase.java b/container-search/src/test/java/com/yahoo/search/query/SoftTimeoutTestCase.java new file mode 100644 index 00000000000..ccfc71b7922 --- /dev/null +++ b/container-search/src/test/java/com/yahoo/search/query/SoftTimeoutTestCase.java @@ -0,0 +1,45 @@ +package com.yahoo.search.query; + +import com.yahoo.prelude.query.QueryException; +import com.yahoo.search.Query; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * @author baldersheim + */ +public class SoftTimeoutTestCase { + @Test + public void testDefaultsInQuery() { + Query query=new Query("?query=test"); + assertNull(query.getRanking().getSoftTimeout().getEnable()); + assertNull(query.getRanking().getSoftTimeout().getFactor()); + assertNull(query.getRanking().getSoftTimeout().getTailcost()); + } + + @Test + public void testQueryOverride() { + Query query=new Query("?query=test&ranking.softtimeout.enable&ranking.softtimeout.factor=0.7&ranking.softtimeout.tailcost=0.3"); + assertTrue(query.getRanking().getSoftTimeout().getEnable()); + assertEquals(Double.valueOf(0.7), query.getRanking().getSoftTimeout().getFactor()); + assertEquals(Double.valueOf(0.3), query.getRanking().getSoftTimeout().getTailcost()); + } + + private void verifyException(String key, String value) { + try { + new Query("?query=test&ranking.softtimeout."+key+"="+value); + assertFalse(true); + } catch (QueryException e) { + assertEquals("Invalid request parameter", e.getMessage()); + assertEquals("Could not set 'ranking.softtimeout." + key + "' to '" + value +"'", e.getCause().getMessage()); + assertEquals(key + " must be in the range [0.0, 1.0]. It is " + value, e.getCause().getCause().getMessage()); + } + } + @Test + public void testLimits() { + verifyException("factor", "-0.1"); + verifyException("factor", "1.1"); + verifyException("tailcost", "-0.1"); + verifyException("tailcost", "1.1"); + } +} 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 eaaf87bc035..ac1da3ada1c 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 @@ -100,6 +100,7 @@ public class QueryTestCase { assertNotSame(q.getRanking().getProperties(), p.getRanking().getProperties()); assertNotSame(q.getRanking().getMatchPhase(), p.getRanking().getMatchPhase()); assertNotSame(q.getRanking().getMatchPhase().getDiversity(), p.getRanking().getMatchPhase().getDiversity()); + assertNotSame(q.getRanking().getSoftTimeout(), p.getRanking().getSoftTimeout()); assertNotSame(q.getPresentation(), p.getPresentation()); assertNotSame(q.getPresentation().getHighlight(), p.getPresentation().getHighlight()); |