summaryrefslogtreecommitdiffstats
path: root/container-search
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@yahoo-inc.com>2017-02-08 11:20:48 +0100
committerJon Bratseth <bratseth@yahoo-inc.com>2017-02-08 11:20:48 +0100
commit1d7eb2e69efb61f24d7dbec98d7f99316be798cb (patch)
tree3ec1364cd89d4b3c13541ddde4dfe2e1704be8cb /container-search
parent3abb7bc8bc8d6845284c86cd4b6869536e0daee9 (diff)
parent1d2fda3acf3035d628aa71bcb82bbcb88f09d5ad (diff)
Merge branch 'master' into bratseth/add-filter-test
Diffstat (limited to 'container-search')
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/cluster/ClusterMonitor.java3
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/cluster/Hasher.java49
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/cluster/NodeMonitor.java26
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/fastsearch/VespaBackEndSearcher.java4
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/Ranking.java11
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/properties/QueryProperties.java19
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/ranking/Matching.java123
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/ranking/SoftTimeout.java (renamed from container-search/src/main/java/com/yahoo/search/query/SoftTimeout.java)4
-rw-r--r--container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java11
-rw-r--r--container-search/src/main/java/com/yahoo/search/result/Coverage.java14
-rw-r--r--container-search/src/test/java/com/yahoo/search/query/MatchingTestCase.java54
-rw-r--r--container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java55
12 files changed, 320 insertions, 53 deletions
diff --git a/container-search/src/main/java/com/yahoo/prelude/cluster/ClusterMonitor.java b/container-search/src/main/java/com/yahoo/prelude/cluster/ClusterMonitor.java
index 871ecc37ea5..bb5accd9022 100644
--- a/container-search/src/main/java/com/yahoo/prelude/cluster/ClusterMonitor.java
+++ b/container-search/src/main/java/com/yahoo/prelude/cluster/ClusterMonitor.java
@@ -67,6 +67,7 @@ public class ClusterMonitor implements Runnable, Freezable {
if (isFrozen())
throw new IllegalStateException("Can not add new nodes after ClusterMonitor has been frozen.");
nodeMonitors.put(node, new NodeMonitor(node));
+ updateVipStatus();
}
/** Called from ClusterSearcher/NodeManager when a node failed */
@@ -88,7 +89,7 @@ public class ClusterMonitor implements Runnable, Freezable {
monitor.responded(hasSearchNodesOnline);
if (wasFailing && monitor.isWorking()) {
log.info("Failed node '" + node + "' started working again.");
- nodeManager.working(monitor.getNode());
+ nodeManager.working(node);
}
updateVipStatus();
}
diff --git a/container-search/src/main/java/com/yahoo/prelude/cluster/Hasher.java b/container-search/src/main/java/com/yahoo/prelude/cluster/Hasher.java
index eefa90f63f0..0c0da390616 100644
--- a/container-search/src/main/java/com/yahoo/prelude/cluster/Hasher.java
+++ b/container-search/src/main/java/com/yahoo/prelude/cluster/Hasher.java
@@ -39,6 +39,10 @@ public class Hasher {
}
static private VespaBackEndSearcher[] addNode(VespaBackEndSearcher node, VespaBackEndSearcher[] oldNodes) {
+ assert node != null;
+ for (VespaBackEndSearcher n : oldNodes) {
+ if (n == node) return oldNodes; // already present
+ }
VespaBackEndSearcher[] newNodes = new VespaBackEndSearcher[oldNodes.length + 1];
System.arraycopy(oldNodes, 0, newNodes, 0, oldNodes.length);
newNodes[oldNodes.length] = node;
@@ -56,25 +60,25 @@ public class Hasher {
}
}
- private VespaBackEndSearcher[] removeNode(VespaBackEndSearcher node, VespaBackEndSearcher[] nodes) {
- VespaBackEndSearcher[] newNodes = null;
- for (VespaBackEndSearcher n : nodes) {
+ private VespaBackEndSearcher[] removeNode(VespaBackEndSearcher node, VespaBackEndSearcher[] oldNodes) {
+ int newLen = oldNodes.length;
+ for (VespaBackEndSearcher n : oldNodes) {
if (n == node) {
- newNodes = new VespaBackEndSearcher[nodes.length - 1];
- break;
+ --newLen;
}
}
- if (newNodes != null) {
- int numToKeep = 0;
-
- for (VespaBackEndSearcher n : nodes) {
- if (n != node) {
- newNodes[numToKeep++] = n;
- }
+ if (newLen == oldNodes.length) {
+ return oldNodes;
+ }
+ VespaBackEndSearcher[] newNodes = new VespaBackEndSearcher[newLen];
+ int idx = 0;
+ for (VespaBackEndSearcher n : oldNodes) {
+ if (n != node) {
+ newNodes[idx++] = n;
}
- return newNodes;
}
- return nodes;
+ assert idx == newLen;
+ return newNodes;
}
/** Removes a node */
@@ -113,18 +117,19 @@ public class Hasher {
*/
public VespaBackEndSearcher select(int trynum) {
VespaBackEndSearcher[] nodes = allNodes;
- if (nodes.length == 0) return null;
if (localNodes.length > 0) {
nodes = localNodes;
- if (localNodes.length == 1) {
- return nodes[0];
- } else {
- return nodes[Math.abs(avoidAllQrsHitSameTld.incrementAndGet() % nodes.length)];
- }
- } else {
- return nodes[Math.abs(avoidAllQrsHitSameTld.incrementAndGet() % nodes.length)];
}
+ if (nodes.length == 0) {
+ return null;
+ }
+ int idx = 0;
+ if (nodes.length > 1) {
+ idx = Math.abs(avoidAllQrsHitSameTld.incrementAndGet() % nodes.length);
+ }
+ assert nodes[idx] != null;
+ return nodes[idx];
}
}
diff --git a/container-search/src/main/java/com/yahoo/prelude/cluster/NodeMonitor.java b/container-search/src/main/java/com/yahoo/prelude/cluster/NodeMonitor.java
index c06b7fe04ba..ba2c693a0fe 100644
--- a/container-search/src/main/java/com/yahoo/prelude/cluster/NodeMonitor.java
+++ b/container-search/src/main/java/com/yahoo/prelude/cluster/NodeMonitor.java
@@ -15,6 +15,7 @@ import com.yahoo.search.result.ErrorMessage;
* <ul>
* <li>A node is taken out of operation if it gives no response in 10 s</li>
* <li>A node is put back in operation when it responds correctly again</li>
+ * <li>A node is initially considered not in operation until we have some data from it</li>
* </ul>
*
* @author bratseth
@@ -33,7 +34,7 @@ public class NodeMonitor {
private long succeededAt = 0;
/** Whether it is assumed the node has documents available to serve */
- private boolean searchNodesOnline = true;
+ private boolean searchNodesOnline = false;
/**
* Creates a new node monitor for a node
@@ -66,10 +67,12 @@ public class NodeMonitor {
long respondedAt = System.currentTimeMillis();
if (error.getCode() == BACKEND_COMMUNICATION_ERROR.code
- || error.getCode() == NO_ANSWER_WHEN_PINGING_NODE.code) {
+ || error.getCode() == NO_ANSWER_WHEN_PINGING_NODE.code)
+ {
// Only count not being able to talk to backend at all
// as errors we care about
if ((respondedAt - succeededAt) > 10000) {
+ this.searchNodesOnline = false;
setWorking(false, "Not working for 10 s: " + error.toString());
}
} else {
@@ -83,26 +86,19 @@ public class NodeMonitor {
public void responded(boolean searchNodesOnline) {
succeededAt = System.currentTimeMillis();
this.searchNodesOnline = searchNodesOnline;
- atStartUp = false;
-
- if ( ! isWorking)
+ if (! isWorking)
setWorking(true, "Responds correctly");
+ atStartUp = false;
}
/** Changes the state of this node if required */
private void setWorking(boolean working, String explanation) {
if (isWorking == working) return; // Old news
- String explanationToLog;
- if (explanation == null)
- explanationToLog = "";
- else
- explanationToLog = ": " + explanation;
-
- if (working)
- log.info("Putting " + node + " in service" + explanationToLog);
- else if ( ! atStartUp)
- log.info("Taking " + node + " out of service" + explanationToLog);
+ if (working && ! atStartUp)
+ log.info("Putting " + node + " in service: " + explanation);
+ else if (! atStartUp)
+ log.info("Taking " + node + " out of service: " + explanation);
isWorking = working;
}
diff --git a/container-search/src/main/java/com/yahoo/prelude/fastsearch/VespaBackEndSearcher.java b/container-search/src/main/java/com/yahoo/prelude/fastsearch/VespaBackEndSearcher.java
index 872ed5ac11c..1b44d60cce9 100644
--- a/container-search/src/main/java/com/yahoo/prelude/fastsearch/VespaBackEndSearcher.java
+++ b/container-search/src/main/java/com/yahoo/prelude/fastsearch/VespaBackEndSearcher.java
@@ -417,7 +417,9 @@ public abstract class VespaBackEndSearcher extends PingableSearcher {
}
if (resultPacket.getCoverageFeature()) {
- result.setCoverage(new Coverage(resultPacket.getCoverageDocs(), resultPacket.getActiveDocs()).setSoonActive(resultPacket.getSoonActiveDocs()).setDegradedReason(resultPacket.getDegradedReason()));
+ result.setCoverage(new Coverage(resultPacket.getCoverageDocs(), resultPacket.getActiveDocs())
+ .setSoonActive(resultPacket.getSoonActiveDocs())
+ .setDegradedReason(resultPacket.getDegradedReason()));
}
}
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 addd759cd29..17ec32b200a 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
@@ -8,8 +8,10 @@ import 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.MatchPhase;
+import com.yahoo.search.query.ranking.Matching;
import com.yahoo.search.query.ranking.RankFeatures;
import com.yahoo.search.query.ranking.RankProperties;
+import com.yahoo.search.query.ranking.SoftTimeout;
import com.yahoo.search.result.ErrorMessage;
/**
@@ -38,6 +40,7 @@ public class Ranking implements Cloneable {
public static final String MATCH_PHASE = "matchPhase";
public static final String DIVERSITY = "diversity";
public static final String SOFTTIMEOUT = "softtimeout";
+ public static final String MATCHING = "matching";
public static final String FEATURES = "features";
public static final String PROPERTIES = "properties";
@@ -83,6 +86,8 @@ public class Ranking implements Cloneable {
private MatchPhase matchPhase = new MatchPhase();
+ private Matching matching = new Matching();
+
private SoftTimeout softTimeout = new SoftTimeout();
public Ranking(Query parent) {
@@ -172,6 +177,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 matching settings of this. This is never null. */
+ public Matching getMatching() { return matching; }
+
/** Returns the soft timeout settings of this. This is never null. */
public SoftTimeout getSoftTimeout() { return softTimeout; }
@@ -185,6 +193,7 @@ public class Ranking implements Cloneable {
clone.rankProperties = this.rankProperties.clone();
clone.rankFeatures = this.rankFeatures.clone();
clone.matchPhase = this.matchPhase.clone();
+ clone.matching = this.matching.clone();
clone.softTimeout = this.softTimeout.clone();
return clone;
}
@@ -216,6 +225,7 @@ public class Ranking implements Cloneable {
hash += 13 * rankProperties.hashCode();
hash += 17 * matchPhase.hashCode();
hash += 19 * softTimeout.hashCode();
+ hash += 23 * matching.hashCode();
return Ranking.class.hashCode() + QueryHelper.combineHash(sorting,location,profile,hash);
}
@@ -240,6 +250,7 @@ public class Ranking implements Cloneable {
public void prepare() {
rankFeatures.prepare(rankProperties);
matchPhase.prepare(rankProperties);
+ matching.prepare(rankProperties);
softTimeout.prepare(rankProperties);
prepareNow(freshness);
}
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 30154b223a5..dbbb9977e05 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
@@ -1,16 +1,16 @@
// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.search.query.properties;
-import com.yahoo.component.ComponentId;
import com.yahoo.processing.request.CompoundName;
import com.yahoo.search.Query;
import com.yahoo.search.query.*;
import com.yahoo.search.query.profile.compiled.CompiledQueryProfileRegistry;
import com.yahoo.search.query.profile.types.FieldDescription;
import com.yahoo.search.query.profile.types.QueryProfileType;
-import com.yahoo.search.query.profile.types.QueryProfileTypeRegistry;
import com.yahoo.search.query.ranking.Diversity;
import com.yahoo.search.query.ranking.MatchPhase;
+import com.yahoo.search.query.ranking.Matching;
+import com.yahoo.search.query.ranking.SoftTimeout;
import com.yahoo.tensor.Tensor;
import java.util.Map;
@@ -119,6 +119,14 @@ public class QueryProperties extends Properties {
if (key.last().equals(SoftTimeout.FACTOR)) return soft.getFactor();
if (key.last().equals(SoftTimeout.TAILCOST)) return soft.getTailcost();
}
+ else if (key.size() == 3 && key.get(1).equals(Ranking.MATCHING)) {
+ Matching matching = ranking.getMatching();
+ if (key.last().equals(Matching.TERMWISELIMIT)) return matching.getTermwiseLimit();
+ if (key.last().equals(Matching.NUMTHREADSPERSEARCH)) return matching.getNumThreadsPerSearch();
+ if (key.last().equals(Matching.NUMSEARCHPARTITIIONS)) return matching.getNumSearchPartitions();
+ if (key.last().equals(Matching.MINHITSPERTHREAD)) return matching.getMinHitsPerThread();
+
+ }
else if (key.size()>2) {
// pass the portion after "ranking.features/properties" down
if (key.get(1).equals(Ranking.FEATURES)) return ranking.getFeatures().getObject(key.rest().rest().toString());
@@ -228,6 +236,13 @@ public class QueryProperties extends Properties {
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() == 3 && key.get(1).equals(Ranking.MATCHING)) {
+ Matching matching = ranking.getMatching();
+ if (key.last().equals(Matching.TERMWISELIMIT)) matching.setTermwiselimit(asDouble(value, 1.0));
+ if (key.last().equals(Matching.NUMTHREADSPERSEARCH)) matching.setNumThreadsPerSearch(asInteger(value, 1));
+ if (key.last().equals(Matching.NUMSEARCHPARTITIIONS)) matching.setNumSearchPartitions(asInteger(value, 1));
+ if (key.last().equals(Matching.MINHITSPERTHREAD)) matching.setMinHitsPerThread(asInteger(value, 0));
+ }
else if (key.size()>2) {
String restKey = key.rest().rest().toString();
if (key.get(1).equals(Ranking.FEATURES))
diff --git a/container-search/src/main/java/com/yahoo/search/query/ranking/Matching.java b/container-search/src/main/java/com/yahoo/search/query/ranking/Matching.java
new file mode 100644
index 00000000000..514a0f36031
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/query/ranking/Matching.java
@@ -0,0 +1,123 @@
+package com.yahoo.search.query.ranking;
+
+import com.yahoo.search.query.Ranking;
+import com.yahoo.search.query.profile.types.FieldDescription;
+import com.yahoo.search.query.profile.types.QueryProfileType;
+
+import java.util.Objects;
+
+/**
+ * Holds the settings for the matching feature.
+ *
+ * @author balder
+ */
+public class Matching implements Cloneable {
+ /** The type representing the property arguments consumed by this */
+ private static final QueryProfileType argumentType;
+
+ public static final String TERMWISELIMIT = "termwiselimit";
+ public static final String NUMTHREADSPERSEARCH = "numthreadspersearch";
+ public static final String NUMSEARCHPARTITIIONS = "numsearchpartitions";
+ public static final String MINHITSPERTHREAD = "minhitsperthread";
+
+
+ static {
+ argumentType =new QueryProfileType(Ranking.MATCHING);
+ argumentType.setStrict(true);
+ argumentType.setBuiltin(true);
+ argumentType.addField(new FieldDescription(TERMWISELIMIT, "double"));
+ argumentType.addField(new FieldDescription(NUMTHREADSPERSEARCH, "integer"));
+ argumentType.addField(new FieldDescription(NUMSEARCHPARTITIIONS, "integer"));
+ argumentType.addField(new FieldDescription(MINHITSPERTHREAD, "integer"));
+ argumentType.freeze();
+ }
+ public static QueryProfileType getArgumentType() { return argumentType; }
+
+ public Double termwiseLimit = null;
+ private Integer numThreadsPerSearch = null;
+ private Integer numSearchPartitions = null;
+ private Integer minHitsPerThread = null;
+
+ public Integer getNumSearchPartitions() {
+ return numSearchPartitions;
+ }
+
+ public void setNumSearchPartitions(int numSearchPartitions) {
+ this.numSearchPartitions = numSearchPartitions;
+ }
+
+ public Integer getMinHitsPerThread() {
+ return minHitsPerThread;
+ }
+
+ public void setMinHitsPerThread(int minHitsPerThread) {
+ this.minHitsPerThread = minHitsPerThread;
+ }
+
+ public void setTermwiselimit(double value) {
+ if ((value < 0.0) || (value > 1.0)) {
+ throw new IllegalArgumentException("termwiselimit must be in the range [0.0, 1.0]. It is " + value);
+ }
+ termwiseLimit = value;
+ }
+
+ public Double getTermwiseLimit() { return termwiseLimit; }
+
+ public void setNumThreadsPerSearch(int value) {
+ numThreadsPerSearch = value;
+ }
+ public Integer getNumThreadsPerSearch() { return numThreadsPerSearch; }
+
+
+ /** Internal operation - DO NOT USE */
+ public void prepare(RankProperties rankProperties) {
+
+ if (termwiseLimit != null) {
+ rankProperties.put("vespa.matching.termwise_limit", String.valueOf(termwiseLimit));
+ }
+ if (numThreadsPerSearch != null) {
+ rankProperties.put("vespa.matching.numthreadspersearch", String.valueOf(numThreadsPerSearch));
+ }
+ if (numSearchPartitions != null) {
+ rankProperties.put("vespa.matching.numsearchpartitions", String.valueOf(numSearchPartitions));
+ }
+ if (minHitsPerThread != null) {
+ rankProperties.put("vespa.matching.minhitsperthread", String.valueOf(minHitsPerThread));
+ }
+ }
+
+ @Override
+ public Matching clone() {
+ try {
+ return (Matching) super.clone();
+ }
+ catch (CloneNotSupportedException e) {
+ throw new RuntimeException("Won't happen", e);
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 0;
+ if (termwiseLimit != null) hash += 11 * termwiseLimit.hashCode();
+ if (numThreadsPerSearch != null) hash += 13 * numThreadsPerSearch.hashCode();
+ if (numSearchPartitions != null) hash += 17 * numSearchPartitions.hashCode();
+ if (minHitsPerThread != null) hash += 19 * minHitsPerThread.hashCode();
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if ( ! (o instanceof Matching)) return false;
+
+ Matching other = (Matching) o;
+ if ( ! Objects.equals(this.termwiseLimit, other.termwiseLimit)) return false;
+ if ( ! Objects.equals(this.numThreadsPerSearch, other.numThreadsPerSearch)) return false;
+ if ( ! Objects.equals(this.numSearchPartitions, other.numSearchPartitions)) return false;
+ if ( ! Objects.equals(this.minHitsPerThread, other.minHitsPerThread)) return false;
+ return true;
+ }
+
+}
+
diff --git a/container-search/src/main/java/com/yahoo/search/query/SoftTimeout.java b/container-search/src/main/java/com/yahoo/search/query/ranking/SoftTimeout.java
index 2db1235f72c..950cb861da6 100644
--- a/container-search/src/main/java/com/yahoo/search/query/SoftTimeout.java
+++ b/container-search/src/main/java/com/yahoo/search/query/ranking/SoftTimeout.java
@@ -1,8 +1,8 @@
-package com.yahoo.search.query;
+package com.yahoo.search.query.ranking;
+import com.yahoo.search.query.Ranking;
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;
diff --git a/container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java b/container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java
index e91349a5842..0fd152e4718 100644
--- a/container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java
+++ b/container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java
@@ -96,13 +96,11 @@ public class JsonRenderer extends AsynchronousSectionedRenderer<Result> {
private static final String COVERAGE = "coverage";
private static final String COVERAGE_COVERAGE = "coverage";
private static final String COVERAGE_DOCUMENTS = "documents";
- private static final String COVERAGE_ACTIVE = "active";
- private static final String COVERAGE_SOON_ACTIVE = "soon-active";
private static final String COVERAGE_DEGRADE = "degraded";
private static final String COVERAGE_DEGRADE_MATCHPHASE = "match-phase";
private static final String COVERAGE_DEGRADE_TIMEOUT = "timeout";
private static final String COVERAGE_DEGRADE_ADAPTIVE_TIMEOUT = "adaptive-timeout";
- private static final String COVERAGE_NON_IDEAL_STATE = "non-ideal-state";
+ private static final String COVERAGE_DEGRADED_NON_IDEAL_STATE = "non-ideal-state";
private static final String COVERAGE_FULL = "full";
private static final String COVERAGE_NODES = "nodes";
private static final String COVERAGE_RESULTS = "results";
@@ -451,14 +449,9 @@ public class JsonRenderer extends AsynchronousSectionedRenderer<Result> {
generator.writeBooleanField(COVERAGE_DEGRADE_MATCHPHASE, c.isDegradedByMatchPhase());
generator.writeBooleanField(COVERAGE_DEGRADE_TIMEOUT, c.isDegradedByTimeout());
generator.writeBooleanField(COVERAGE_DEGRADE_ADAPTIVE_TIMEOUT, c.isDegradedByAdapativeTimeout());
- generator.writeEndObject();
- } else if (c.getResultPercentage() != 100) {
- generator.writeObjectFieldStart(COVERAGE_NON_IDEAL_STATE);
- generator.writeNumberField(COVERAGE_ACTIVE, c.getActive());
- generator.writeNumberField(COVERAGE_SOON_ACTIVE, c.getSoonActive());
+ generator.writeBooleanField(COVERAGE_DEGRADED_NON_IDEAL_STATE, c.isDegradedByNonIdealState());
generator.writeEndObject();
}
- generator.writeNumberField(COVERAGE_DOCUMENTS, c.getDocs());
generator.writeBooleanField(COVERAGE_FULL, c.getFull());
generator.writeNumberField(COVERAGE_NODES, c.getNodes());
generator.writeNumberField(COVERAGE_RESULTS, c.getResultSets());
diff --git a/container-search/src/main/java/com/yahoo/search/result/Coverage.java b/container-search/src/main/java/com/yahoo/search/result/Coverage.java
index 13deaf36db7..ebb91a64723 100644
--- a/container-search/src/main/java/com/yahoo/search/result/Coverage.java
+++ b/container-search/src/main/java/com/yahoo/search/result/Coverage.java
@@ -1,6 +1,8 @@
// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.search.result;
+import com.google.common.annotations.Beta;
+
/**
* The coverage report for a result set.
*
@@ -21,7 +23,19 @@ public class Coverage extends com.yahoo.container.handler.Coverage {
super(docs, nodes, full, resultSets);
}
+ /**
+ * Will set number of documents present in ideal state
+ * @param soonActive
+ * @return self for chaining
+ */
+ @Beta
public Coverage setSoonActive(long soonActive) { this.soonActive = soonActive; return this; }
+
+ /**
+ * Will set the reasons for degraded coverage as reported by vespa backend.
+ * @param degradedReason
+ * @return self for chaining
+ */
public Coverage setDegradedReason(int degradedReason) { this.degradedReason = degradedReason; return this; }
}
diff --git a/container-search/src/test/java/com/yahoo/search/query/MatchingTestCase.java b/container-search/src/test/java/com/yahoo/search/query/MatchingTestCase.java
new file mode 100644
index 00000000000..49e632b00bd
--- /dev/null
+++ b/container-search/src/test/java/com/yahoo/search/query/MatchingTestCase.java
@@ -0,0 +1,54 @@
+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.*;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author baldersheim
+ */
+public class MatchingTestCase {
+ @Test
+ public void testDefaultsInQuery() {
+ Query query=new Query("?query=test");
+ assertNull(query.getRanking().getMatching().getTermwiseLimit());
+ assertNull(query.getRanking().getMatching().getNumThreadsPerSearch());
+ assertNull(query.getRanking().getMatching().getNumSearchPartitions());
+ assertNull(query.getRanking().getMatching().getMinHitsPerThread());
+
+ }
+
+ @Test
+ public void testQueryOverride() {
+ Query query=new Query("?query=test&ranking.matching.termwiselimit=0.7&ranking.matching.numthreadspersearch=17&ranking.matching.numsearchpartitions=13&ranking.matching.minhitsperthread=3");
+ assertEquals(Double.valueOf(0.7), query.getRanking().getMatching().getTermwiseLimit());
+ assertEquals(Integer.valueOf(17), query.getRanking().getMatching().getNumThreadsPerSearch());
+ assertEquals(Integer.valueOf(13), query.getRanking().getMatching().getNumSearchPartitions());
+ assertEquals(Integer.valueOf(3), query.getRanking().getMatching().getMinHitsPerThread());
+
+ query.prepare();
+ assertEquals("0.7", query.getRanking().getProperties().get("vespa.matching.termwise_limit").get(0));
+ assertEquals("17", query.getRanking().getProperties().get("vespa.matching.numthreadspersearch").get(0));
+ assertEquals("13", query.getRanking().getProperties().get("vespa.matching.numsearchpartitions").get(0));
+ assertEquals("3", query.getRanking().getProperties().get("vespa.matching.minhitsperthread").get(0));
+ }
+
+ private void verifyException(String key, String value) {
+ try {
+ new Query("?query=test&ranking.matching."+key+"="+value);
+ assertFalse(true);
+ } catch (QueryException e) {
+ assertEquals("Invalid request parameter", e.getMessage());
+ assertEquals("Could not set 'ranking.matching." + 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("termwiselimit", "-0.1");
+ verifyException("termwiselimit", "1.1");
+ }
+}
diff --git a/container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java b/container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java
index c0b06bf7fb4..3f22b22a7cf 100644
--- a/container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java
@@ -599,7 +599,40 @@ public class JsonRendererTestCase {
r.hits().add(gg);
r.hits().addError(ErrorMessage.createInternalServerError("boom"));
String summary = render(execution, r);
- // System.out.println(summary);
+ assertEqualJson(expected, summary);
+ }
+
+ @Test
+ public void testCoverage() throws InterruptedException, ExecutionException, IOException {
+ String expected = "{\n"
+ + " \"root\": {\n"
+ + " \"coverage\": {\n"
+ + " \"coverage\": 83,\n"
+ + " \"documents\": 500,\n"
+ + " \"degraded\" : {\n"
+ + " \"match-phase\" : true,\n"
+ + " \"timeout\" : false,\n"
+ + " \"adaptive-timeout\" : true,\n"
+ + " \"non-ideal-state\" : false"
+ + " },\n"
+ + " \"full\": false,\n"
+ + " \"nodes\": 0,\n"
+ + " \"results\": 1,\n"
+ + " \"resultsFull\": 0\n"
+ + " },\n"
+ + " \"fields\": {\n"
+ + " \"totalCount\": 0\n"
+ + " },\n"
+ + " \"id\": \"toplevel\",\n"
+ + " \"relevance\": 1.0\n"
+ + " }\n"
+ + "}";
+ Query q = new Query("/?query=a&tracelevel=5&reportCoverage=true");
+ Execution execution = new Execution(Execution.Context.createContextStub());
+ Result r = new Result(q);
+ r.setCoverage(new Coverage(500, 600).setDegradedReason(5));
+
+ String summary = render(execution, r);
assertEqualJson(expected, summary);
}
@@ -1122,6 +1155,16 @@ public class JsonRendererTestCase {
assertEquals(");", jsonCallbackEnd);
}
+ @Test
+ public void testThatTheJsonValidatorCanCatchErrors() {
+ String json = "{"
+ + " \"root\": {"
+ + " \"duplicate\": 1,"
+ + " \"duplicate\": 2"
+ + " }"
+ + "}";
+ assertEquals("Duplicate key \"duplicate\"", validateJSON(json));
+ }
private String render(Result r) throws InterruptedException, ExecutionException {
Execution execution = new Execution(Execution.Context.createContextStub());
return render(execution, r);
@@ -1141,6 +1184,16 @@ public class JsonRendererTestCase {
Map<String, Object> exp = m.readValue(expected, Map.class);
Map<String, Object> gen = m.readValue(generated, Map.class);
assertEquals(exp, gen);
+ assertEquals("", validateJSON(expected));
+ assertEquals("", validateJSON(generated));
+ }
+ private String validateJSON(String presumablyValidJson) {
+ try {
+ new JSONObject(presumablyValidJson);
+ return "";
+ } catch (JSONException e) {
+ return e.getMessage();
+ }
}
}