summaryrefslogtreecommitdiffstats
path: root/container-search
diff options
context:
space:
mode:
authorHenning Baldersheim <balder@yahoo-inc.com>2016-11-25 14:32:21 +0100
committerHenning Baldersheim <balder@yahoo-inc.com>2017-01-25 18:55:21 +0100
commitc19fb20b2bdb12ed6e1c1aa5cc7b9e7efb033b84 (patch)
tree538ad4eba247a9d74ac5c53befc80fa4898e967e /container-search
parent29ee9162845ffaeb01f22a242d2fc7ab9593530e (diff)
Add the softtimeout query control.
Diffstat (limited to 'container-search')
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/Ranking.java9
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/SoftTimeout.java101
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/properties/QueryProperties.java90
-rw-r--r--container-search/src/test/java/com/yahoo/search/query/SoftTimeoutTestCase.java45
-rw-r--r--container-search/src/test/java/com/yahoo/search/test/QueryTestCase.java1
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());