diff options
author | Jon Bratseth <bratseth@yahoo-inc.com> | 2017-02-08 11:20:48 +0100 |
---|---|---|
committer | Jon Bratseth <bratseth@yahoo-inc.com> | 2017-02-08 11:20:48 +0100 |
commit | 1d7eb2e69efb61f24d7dbec98d7f99316be798cb (patch) | |
tree | 3ec1364cd89d4b3c13541ddde4dfe2e1704be8cb /container-search | |
parent | 3abb7bc8bc8d6845284c86cd4b6869536e0daee9 (diff) | |
parent | 1d2fda3acf3035d628aa71bcb82bbcb88f09d5ad (diff) |
Merge branch 'master' into bratseth/add-filter-test
Diffstat (limited to 'container-search')
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(); + } } } |