summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHenning Baldersheim <balder@yahoo-inc.com>2019-03-07 17:02:14 +0100
committerGitHub <noreply@github.com>2019-03-07 17:02:14 +0100
commitd33933d74fa5a5a01529a6b74f1a1a1901904449 (patch)
tree5e1180aa105c8e8215270b2e740873b59e21e15b
parent882e4276c0fd191db0ba62f4da5e3e64ab7059ea (diff)
parent9e410bcd856284367889d288ef8ee17e0d90a569 (diff)
Merge pull request #8681 from vespa-engine/balder/trace-backend
Balder/trace backend
-rw-r--r--container-search/abi-spec.json2
-rw-r--r--container-search/src/main/java/com/yahoo/fs4/FS4Properties.java5
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/fastsearch/VespaBackEndSearcher.java21
-rw-r--r--container-search/src/main/java/com/yahoo/search/Query.java26
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/context/QueryContext.java5
-rw-r--r--container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java25
-rw-r--r--container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java1224
-rw-r--r--documentapi/abi-spec.json2
-rw-r--r--processing/abi-spec.json1
-rw-r--r--processing/src/main/java/com/yahoo/processing/execution/Execution.java3
-rw-r--r--searchcore/src/vespa/searchcore/proton/matchengine/matchengine.cpp23
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/match_tools.h1
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/matcher.cpp14
-rw-r--r--searchlib/src/tests/engine/searchapi/searchapi_test.cpp78
-rw-r--r--searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp67
-rw-r--r--searchlib/src/vespa/searchlib/attribute/imported_attribute_vector.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/attribute/imported_attribute_vector.h9
-rw-r--r--searchlib/src/vespa/searchlib/attribute/imported_attribute_vector_read_guard.cpp7
-rw-r--r--searchlib/src/vespa/searchlib/attribute/imported_attribute_vector_read_guard.h77
-rw-r--r--searchlib/src/vespa/searchlib/engine/CMakeLists.txt1
-rw-r--r--searchlib/src/vespa/searchlib/engine/packetconverter.cpp37
-rw-r--r--searchlib/src/vespa/searchlib/engine/propertiesmap.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/engine/request.cpp6
-rw-r--r--searchlib/src/vespa/searchlib/engine/request.h12
-rw-r--r--searchlib/src/vespa/searchlib/engine/searchrequest.cpp10
-rw-r--r--searchlib/src/vespa/searchlib/engine/trace.cpp31
-rw-r--r--searchlib/src/vespa/searchlib/engine/trace.h39
-rw-r--r--searchlib/src/vespa/searchlib/fef/indexproperties.cpp18
-rw-r--r--searchlib/src/vespa/searchlib/fef/indexproperties.h14
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/blueprint.cpp11
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/blueprint.h7
-rw-r--r--staging_vespalib/src/vespa/vespalib/objects/CMakeLists.txt1
-rw-r--r--staging_vespalib/src/vespa/vespalib/objects/object2slime.cpp76
-rw-r--r--staging_vespalib/src/vespa/vespalib/objects/object2slime.h36
-rw-r--r--staging_vespalib/src/vespa/vespalib/objects/objectdumper.cpp3
-rw-r--r--staging_vespalib/src/vespa/vespalib/objects/objectdumper.h17
36 files changed, 1195 insertions, 719 deletions
diff --git a/container-search/abi-spec.json b/container-search/abi-spec.json
index 24a46602b60..02f82762d57 100644
--- a/container-search/abi-spec.json
+++ b/container-search/abi-spec.json
@@ -1752,6 +1752,7 @@
"public java.lang.String toDetailString()",
"public int encode(java.nio.ByteBuffer)",
"public void trace(java.lang.String, int)",
+ "public void trace(java.lang.Object, int)",
"public void trace(java.lang.String, boolean, int)",
"public varargs void trace(boolean, int, java.lang.Object[])",
"public void attachContext(com.yahoo.search.Query)",
@@ -5370,6 +5371,7 @@
"methods": [
"public void <init>(int, com.yahoo.search.Query)",
"public void trace(java.lang.String, int)",
+ "public void trace(java.lang.Object, int)",
"public void logValue(java.lang.String, java.lang.Object)",
"public java.util.Iterator logValueIterator()",
"public void setProperty(java.lang.String, java.lang.Object)",
diff --git a/container-search/src/main/java/com/yahoo/fs4/FS4Properties.java b/container-search/src/main/java/com/yahoo/fs4/FS4Properties.java
index 9dec31785c3..f5f1fca0801 100644
--- a/container-search/src/main/java/com/yahoo/fs4/FS4Properties.java
+++ b/container-search/src/main/java/com/yahoo/fs4/FS4Properties.java
@@ -10,11 +10,12 @@ public class FS4Properties {
static public class Entry {
public final String key;
- public final String val;
+ private final byte [] val;
public Entry(byte[] k, byte[] v) {
key = Utf8.toString(k);
- val = Utf8.toString(v);
+ val = v;
}
+ public final byte [] getValue() { return val; }
};
private Entry[] entries;
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 d6e87e58fd8..024dc4a6b06 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
@@ -2,8 +2,11 @@
package com.yahoo.prelude.fastsearch;
import com.yahoo.collections.TinyIdentitySet;
+import com.yahoo.data.access.simple.Value;
+import com.yahoo.data.access.slime.SlimeAdapter;
import com.yahoo.fs4.DocsumPacket;
import com.yahoo.fs4.DocumentInfo;
+import com.yahoo.fs4.FS4Properties;
import com.yahoo.fs4.Packet;
import com.yahoo.fs4.QueryPacket;
import com.yahoo.fs4.QueryPacketData;
@@ -29,6 +32,10 @@ import com.yahoo.search.result.Hit;
import com.yahoo.search.result.Relevance;
import com.yahoo.search.searchchain.Execution;
import com.yahoo.searchlib.aggregation.Grouping;
+import com.yahoo.slime.BinaryFormat;
+import com.yahoo.slime.JsonFormat;
+import com.yahoo.slime.Slime;
+import com.yahoo.text.Utf8;
import com.yahoo.vespa.objects.BufferSerializer;
import java.io.IOException;
@@ -410,9 +417,23 @@ public abstract class VespaBackEndSearcher extends PingableSearcher {
}
}
+ private void addBackendTrace(Query query, QueryResultPacket resultPacket) {
+ if (resultPacket.propsArray == null) return;
+ Value.ArrayValue traces = new Value.ArrayValue();
+ for (FS4Properties properties : resultPacket.propsArray) {
+ if ( ! properties.getName().equals("trace")) continue;
+ for (FS4Properties.Entry entry : properties.getEntries()) {
+ traces.add(new SlimeAdapter(BinaryFormat.decode(entry.getValue()).get()));
+ }
+ }
+ query.trace(traces, query.getTraceLevel());
+ }
+
void addMetaInfo(Query query, QueryPacketData queryPacketData, QueryResultPacket resultPacket, Result result) {
result.setTotalHitCount(resultPacket.getTotalDocumentCount());
+ addBackendTrace(query, resultPacket);
+
// Grouping
if (resultPacket.getGroupData() != null) {
byte[] data = resultPacket.getGroupData();
diff --git a/container-search/src/main/java/com/yahoo/search/Query.java b/container-search/src/main/java/com/yahoo/search/Query.java
index 8aacd194a3f..97a8c35bfa3 100644
--- a/container-search/src/main/java/com/yahoo/search/Query.java
+++ b/container-search/src/main/java/com/yahoo/search/Query.java
@@ -678,6 +678,11 @@ public class Query extends com.yahoo.processing.Request implements Cloneable {
trace(message, false, traceLevel);
}
+ public void trace(Object message, int traceLevel) {
+ if ( ! isTraceable(traceLevel)) return;
+ getContext(true).trace(message, 0);
+ }
+
/**
* Adds a trace message to this query
* if the trace level of the query is sufficiently high.
@@ -1027,13 +1032,17 @@ public class Query extends com.yahoo.processing.Request implements Cloneable {
// TODO: Push down
mapCount += ranking.getProperties().encode(buffer, encodeQueryData);
- if (encodeQueryData) mapCount += ranking.getFeatures().encode(buffer);
+ if (encodeQueryData) {
+ mapCount += ranking.getFeatures().encode(buffer);
- // TODO: Push down
- if (encodeQueryData && presentation.getHighlight() != null) mapCount += MapEncoder.encodeStringMultiMap(Highlight.HIGHLIGHTTERMS, presentation.getHighlight().getHighlightTerms(), buffer);
+ // TODO: Push down
+ if (presentation.getHighlight() != null) {
+ mapCount += MapEncoder.encodeStringMultiMap(Highlight.HIGHLIGHTTERMS, presentation.getHighlight().getHighlightTerms(), buffer);
+ }
- // TODO: Push down
- if (encodeQueryData) mapCount += MapEncoder.encodeSingleValue("model", "searchpath", model.getSearchPath(), buffer);
+ // TODO: Push down
+ mapCount += MapEncoder.encodeMap("model", createModelMap(), buffer);
+ }
mapCount += MapEncoder.encodeSingleValue(DocumentDatabase.MATCH_PROPERTY, DocumentDatabase.SEARCH_DOC_TYPE_KEY, model.getDocumentDb(), buffer);
mapCount += MapEncoder.encodeMap("caches", createCacheSettingMap(), buffer);
@@ -1057,6 +1066,13 @@ public class Query extends com.yahoo.processing.Request implements Cloneable {
return Collections.<String,Boolean>emptyMap();
}
+ private Map<String, String> createModelMap() {
+ Map<String, String> m = new HashMap<>();
+ if (model.getSearchPath() != null) m.put("searchpath", model.getSearchPath());
+ if (getTraceLevel() > 0) m.put("tracelevel", String.valueOf(getTraceLevel()));
+ return m;
+ }
+
/**
* Prepares this for binary serialization.
* <p>
diff --git a/container-search/src/main/java/com/yahoo/search/query/context/QueryContext.java b/container-search/src/main/java/com/yahoo/search/query/context/QueryContext.java
index 6b28b166150..1ba30275dc1 100644
--- a/container-search/src/main/java/com/yahoo/search/query/context/QueryContext.java
+++ b/container-search/src/main/java/com/yahoo/search/query/context/QueryContext.java
@@ -31,9 +31,12 @@ public class QueryContext implements Cloneable {
/** Adds a context message to this context */
public void trace(String message, int traceLevel) {
- owner.getModel().getExecution().trace().trace(message,traceLevel);
+ trace((Object)message, traceLevel);
}
+ public void trace(Object message, int traceLevel) {
+ owner.getModel().getExecution().trace().trace(message,traceLevel);
+ }
/**
* Adds a key-value which will be logged to the access log for this query (by doing toString() on the value
* Multiple values may be set to the same key. A value cannot be removed once set.
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 c6e64a32c48..1754b6cc028 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
@@ -18,7 +18,6 @@ import com.yahoo.document.datatypes.StringFieldValue;
import com.yahoo.document.datatypes.TensorFieldValue;
import com.yahoo.document.json.JsonWriter;
import com.yahoo.lang.MutableBoolean;
-import com.yahoo.prelude.hitfield.HitField;
import com.yahoo.processing.Response;
import com.yahoo.processing.execution.Execution.Trace;
import com.yahoo.processing.rendering.AsynchronousSectionedRenderer;
@@ -185,7 +184,8 @@ public class JsonRenderer extends AsynchronousSectionedRenderer<Result> {
header();
generator.writeStartObject();
}
- generator.writeStringField(TRACE_MESSAGE, payload.toString());
+ generator.writeFieldName(TRACE_MESSAGE);
+ fieldConsumer.renderFieldContentsDirect(payload);
dirty = true;
}
if (dirty) {
@@ -763,17 +763,32 @@ public class JsonRenderer extends AsynchronousSectionedRenderer<Result> {
}
private void renderInspector(Inspector data) throws IOException {
- StringBuilder intermediate = new StringBuilder();
Inspector asMap = wrapAsMap(data);
if (asMap != null) {
+ StringBuilder intermediate = new StringBuilder();
JsonRender.render(asMap, intermediate, true);
+ generator.writeRawValue(intermediate.toString());
} else {
- JsonRender.render(data, intermediate, true);
+ renderInspectorDirect(data);
}
+
+ }
+
+ private void renderInspectorDirect(Inspector data) throws IOException {
+ StringBuilder intermediate = new StringBuilder();
+ JsonRender.render(data, intermediate, true);
generator.writeRawValue(intermediate.toString());
}
private void renderFieldContents(Object field) throws IOException {
+ if (field instanceof Inspectable) {
+ renderInspector(((Inspectable)field).inspect());
+ } else {
+ renderFieldContentsDirect(field);
+ }
+ }
+
+ private void renderFieldContentsDirect(Object field) throws IOException {
if (field == null) {
generator.writeNull();
} else if (field instanceof Boolean) {
@@ -785,7 +800,7 @@ public class JsonRenderer extends AsynchronousSectionedRenderer<Result> {
} else if (field instanceof Tensor) {
renderTensor(Optional.of((Tensor)field));
} else if (field instanceof Inspectable) {
- renderInspector(((Inspectable)field).inspect());
+ renderInspectorDirect(((Inspectable)field).inspect());
} else if (field instanceof JsonProducer) {
generator.writeRawValue(((JsonProducer) field).toJson());
} else if (field instanceof StringFieldValue) {
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 f3167cceaaf..9fb2e627e9c 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
@@ -95,24 +95,24 @@ public class JsonRendererTestCase {
@Test
public void testDocumentId() throws IOException, InterruptedException, ExecutionException {
- String expected = "{\n"
- + " \"root\": {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"fields\": {\n"
- + " \"documentid\": \"id:unittest:smoke::whee\"\n"
- + " },\n"
- + " \"id\": \"id:unittest:smoke::whee\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + " ],\n"
- + " \"fields\": {\n"
- + " \"totalCount\": 1\n"
- + " },\n"
- + " \"id\": \"toplevel\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + "}\n";
+ String expected = "{"
+ + " \"root\": {"
+ + " \"children\": ["
+ + " {"
+ + " \"fields\": {"
+ + " \"documentid\": \"id:unittest:smoke::whee\""
+ + " },"
+ + " \"id\": \"id:unittest:smoke::whee\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + " ],"
+ + " \"fields\": {"
+ + " \"totalCount\": 1"
+ + " },"
+ + " \"id\": \"toplevel\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + "}";
Result r = newEmptyResult();
Hit h = new Hit("docIdTest");
h.setField("documentid", new DocumentId("id:unittest:smoke::whee"));
@@ -124,34 +124,34 @@ public class JsonRendererTestCase {
@Test
public void testDataTypes() throws IOException, InterruptedException, ExecutionException, JSONException {
- String expected = "{\n"
- + " \"root\": {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"fields\": {\n"
- + " \"double\": 0.00390625,\n"
- + " \"float\": 14.29,\n"
- + " \"integer\": 1,\n"
- + " \"long\": 4398046511104,\n"
- + " \"bool\": true,\n"
- + " \"object\": \"thingie\",\n"
- + " \"string\": \"stuff\",\n"
- + " \"predicate\": \"a in [b]\",\n"
- + " \"tensor1\": { \"cells\": [ { \"address\": {\"x\": \"a\"}, \"value\":2.0 } ] },\n"
- + " \"tensor2\": { \"cells\": [] },\n"
- + " \"tensor3\": { \"cells\": [ { \"address\": {\"x\": \"a\", \"y\": \"0\"}, \"value\":2.0 }, { \"address\": {\"x\": \"a\", \"y\": \"1\"}, \"value\":-1.0 } ] }\n"
- + " },\n"
- + " \"id\": \"datatypestuff\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + " ],\n"
- + " \"fields\": {\n"
- + " \"totalCount\": 1\n"
- + " },\n"
- + " \"id\": \"toplevel\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + "}\n";
+ String expected = "{"
+ + " \"root\": {"
+ + " \"children\": ["
+ + " {"
+ + " \"fields\": {"
+ + " \"double\": 0.00390625,"
+ + " \"float\": 14.29,"
+ + " \"integer\": 1,"
+ + " \"long\": 4398046511104,"
+ + " \"bool\": true,"
+ + " \"object\": \"thingie\","
+ + " \"string\": \"stuff\","
+ + " \"predicate\": \"a in [b]\","
+ + " \"tensor1\": { \"cells\": [ { \"address\": {\"x\": \"a\"}, \"value\":2.0 } ] },"
+ + " \"tensor2\": { \"cells\": [] },"
+ + " \"tensor3\": { \"cells\": [ { \"address\": {\"x\": \"a\", \"y\": \"0\"}, \"value\":2.0 }, { \"address\": {\"x\": \"a\", \"y\": \"1\"}, \"value\":-1.0 } ] }"
+ + " },"
+ + " \"id\": \"datatypestuff\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + " ],"
+ + " \"fields\": {"
+ + " \"totalCount\": 1"
+ + " },"
+ + " \"id\": \"toplevel\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + "}";
Result r = newEmptyResult();
Hit h = new Hit("datatypestuff");
// the floating point values are chosen to get a deterministic string representation
@@ -176,42 +176,42 @@ public class JsonRendererTestCase {
@Test
public void testTracing() throws IOException, InterruptedException, ExecutionException {
// which clearly shows a trace child is created once too often...
- String expected = "{\n"
- + " \"root\": {\n"
- + " \"fields\": {\n"
- + " \"totalCount\": 0\n"
- + " },\n"
- + " \"id\": \"toplevel\",\n"
- + " \"relevance\": 1.0\n"
- + " },\n"
- + " \"trace\": {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"message\": \"No query profile is used\"\n"
- + " },\n"
- + " {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"message\": \"something\"\n"
- + " },\n"
- + " {\n"
- + " \"message\": \"something else\"\n"
- + " },\n"
- + " {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"message\": \"yellow\"\n"
- + " }\n"
- + " ]\n"
- + " },\n"
- + " {\n"
- + " \"message\": \"marker\"\n"
- + " }\n"
- + " ]\n"
- + " }\n"
- + " ]\n"
- + " }\n"
- + "}\n";
+ String expected = "{"
+ + " \"root\": {"
+ + " \"fields\": {"
+ + " \"totalCount\": 0"
+ + " },"
+ + " \"id\": \"toplevel\","
+ + " \"relevance\": 1.0"
+ + " },"
+ + " \"trace\": {"
+ + " \"children\": ["
+ + " {"
+ + " \"message\": \"No query profile is used\""
+ + " },"
+ + " {"
+ + " \"children\": ["
+ + " {"
+ + " \"message\": \"something\""
+ + " },"
+ + " {"
+ + " \"message\": \"something else\""
+ + " },"
+ + " {"
+ + " \"children\": ["
+ + " {"
+ + " \"message\": \"yellow\""
+ + " }"
+ + " ]"
+ + " },"
+ + " {"
+ + " \"message\": \"marker\""
+ + " }"
+ + " ]"
+ + " }"
+ + " ]"
+ + " }"
+ + "}";
Query q = new Query("/?query=a&tracelevel=1");
Execution execution = new Execution(Execution.Context.createContextStub());
Result r = new Result(q);
@@ -229,16 +229,81 @@ public class JsonRendererTestCase {
}
@Test
+ public void testTracingOfSlime() throws IOException, InterruptedException, ExecutionException {
+ // which clearly shows a trace child is created once too often...
+ String expected = "{"
+ + " \"root\": {"
+ + " \"fields\": {"
+ + " \"totalCount\": 0"
+ + " },"
+ + " \"id\": \"toplevel\","
+ + " \"relevance\": 1.0"
+ + " },"
+ + " \"trace\": {"
+ + " \"children\": ["
+ + " {"
+ + " \"message\": \"No query profile is used\""
+ + " },"
+ + " {"
+ + " \"children\": ["
+ + " {"
+ + " \"message\": \"something\""
+ + " },"
+ + " {"
+ + " \"message\": \"something else\""
+ + " },"
+ + " {"
+ + " \"children\": ["
+ + " {"
+ + " \"message\": ["
+ + " { \"colour\": \"yellow\"},"
+ + " { \"colour\": \"green\"}"
+ + " ]"
+ + " }"
+ + " ]"
+ + " },"
+ + " {"
+ + " \"message\": \"marker\""
+ + " }"
+ + " ]"
+ + " }"
+ + " ]"
+ + " }"
+ + "}";
+ Query q = new Query("/?query=a&tracelevel=1");
+ Execution execution = new Execution(Execution.Context.createContextStub());
+ Result r = new Result(q);
+
+ execution.search(q);
+ q.trace("something", 1);
+ q.trace("something else", 1);
+ Execution e2 = new Execution(new Chain<Searcher>(), execution.context());
+ Query subQuery = new Query("/?query=b&tracelevel=1");
+ e2.search(subQuery);
+ Value.ArrayValue access = new Value.ArrayValue();
+ Slime slime = new Slime();
+ slime.setObject().setString("colour","yellow");
+ access.add(new SlimeAdapter(slime.get()));
+ slime = new Slime();
+ slime.setObject().setString("colour","green");
+ access.add(new SlimeAdapter(slime.get()));
+ subQuery.trace(access, 1);
+ q.trace("marker", 1);
+ String summary = render(execution, r);
+ assertEqualJson(expected, summary);
+ }
+
+ @Test
public void testEmptyTracing() throws IOException, InterruptedException, ExecutionException {
- String expected = "{\n"
- + " \"root\": {\n"
- + " \"fields\": {\n"
- + " \"totalCount\": 0\n"
- + " },\n"
- + " \"id\": \"toplevel\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + "}\n";
+ String expected = "{"
+ + " \"root\": {"
+ + " \"fields\": {"
+ + " \"totalCount\": 0"
+ + " },"
+ + " \"id\": \"toplevel\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + "}";
Query q = new Query("/?query=a&tracelevel=0");
Execution execution = new Execution(Execution.Context.createContextStub());
Result r = new Result(q);
@@ -259,31 +324,31 @@ public class JsonRendererTestCase {
@SuppressWarnings("unchecked")
@Test
public void testTracingWithEmptySubtree() throws IOException, InterruptedException, ExecutionException {
- String expected = "{\n"
- + " \"root\": {\n"
- + " \"fields\": {\n"
- + " \"totalCount\": 0\n"
- + " },\n"
- + " \"id\": \"toplevel\",\n"
- + " \"relevance\": 1.0\n"
- + " },\n"
- + " \"trace\": {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"message\": \"No query profile is used\"\n"
- + " },\n"
- + " {\n"
- + " \"message\": \"Resolved properties:\\ntracelevel=10 (value from request)\\nquery=a (value from request)\\n\"\n"
- + " },\n"
- + " {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"timestamp\": 42\n"
- + " }\n"
- + " ]\n"
- + " }\n"
- + " ]\n"
- + " }\n"
+ String expected = "{"
+ + " \"root\": {"
+ + " \"fields\": {"
+ + " \"totalCount\": 0"
+ + " },"
+ + " \"id\": \"toplevel\","
+ + " \"relevance\": 1.0"
+ + " },"
+ + " \"trace\": {"
+ + " \"children\": ["
+ + " {"
+ + " \"message\": \"No query profile is used\""
+ + " },"
+ + " {"
+ + " \"message\": \"Resolved properties:\\ntracelevel=10 (value from request)\\nquery=a (value from request)\\n\""
+ + " },"
+ + " {"
+ + " \"children\": ["
+ + " {"
+ + " \"timestamp\": 42"
+ + " }"
+ + " ]"
+ + " }"
+ + " ]"
+ + " }"
+ "}";
Query q = new Query("/?query=a&tracelevel=10");
Execution execution = new Execution(Execution.Context.createContextStub());
@@ -314,14 +379,14 @@ public class JsonRendererTestCase {
@Test
public void trace_is_not_included_if_tracelevel_0() throws IOException, ExecutionException, InterruptedException {
String expected =
- "{\n" +
- " \"root\": {\n" +
- " \"id\": \"toplevel\",\n" +
- " \"relevance\": 1.0,\n" +
- " \"fields\": {\n" +
- " \"totalCount\": 0\n" +
- " }\n" +
- " }\n" +
+ "{" +
+ " \"root\": {" +
+ " \"id\": \"toplevel\"," +
+ " \"relevance\": 1.0," +
+ " \"fields\": {" +
+ " \"totalCount\": 0" +
+ " }" +
+ " }" +
"}";
Query q = new Query("/?query=a&tracelevel=0");
Execution execution = new Execution(Execution.Context.createContextStub());
@@ -334,37 +399,37 @@ public class JsonRendererTestCase {
@Test
public void testTracingOfNodesWithBothChildrenAndData() throws IOException, InterruptedException, ExecutionException {
- String expected = "{\n"
- + " \"root\": {\n"
- + " \"fields\": {\n"
- + " \"totalCount\": 0\n"
- + " },\n"
- + " \"id\": \"toplevel\",\n"
- + " \"relevance\": 1.0\n"
- + " },\n"
- + " \"trace\": {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"message\": \"No query profile is used\"\n"
- + " },\n"
- + " {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"message\": \"string payload\",\n"
+ String expected = "{"
+ + " \"root\": {"
+ + " \"fields\": {"
+ + " \"totalCount\": 0"
+ + " },"
+ + " \"id\": \"toplevel\","
+ + " \"relevance\": 1.0"
+ + " },"
+ + " \"trace\": {"
+ + " \"children\": ["
+ + " {"
+ + " \"message\": \"No query profile is used\""
+ + " },"
+ + " {"
+ + " \"children\": ["
+ + " {"
+ + " \"message\": \"string payload\","
+ " \"children\": ["
- + " {\n"
+ + " {"
+ " \"message\": \"leafnode\""
- + " }\n"
- + " ]\n"
- + " },\n"
- + " {\n"
- + " \"message\": \"something\"\n"
- + " }\n"
- + " ]\n"
- + " }\n"
- + " ]\n"
- + " }\n"
- + "}\n";
+ + " }"
+ + " ]"
+ + " },"
+ + " {"
+ + " \"message\": \"something\""
+ + " }"
+ + " ]"
+ + " }"
+ + " ]"
+ + " }"
+ + "}";
Query q = new Query("/?query=a&tracelevel=1");
Execution execution = new Execution(Execution.Context.createContextStub());
Result r = new Result(q);
@@ -380,32 +445,32 @@ public class JsonRendererTestCase {
@Test
public void testTracingOfNodesWithBothChildrenAndDataAndEmptySubnode() throws IOException, InterruptedException, ExecutionException {
- String expected = "{\n"
- + " \"root\": {\n"
- + " \"fields\": {\n"
- + " \"totalCount\": 0\n"
- + " },\n"
- + " \"id\": \"toplevel\",\n"
- + " \"relevance\": 1.0\n"
- + " },\n"
- + " \"trace\": {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"message\": \"No query profile is used\"\n"
- + " },\n"
- + " {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"message\": \"string payload\"\n"
- + " },\n"
- + " {\n"
- + " \"message\": \"something\"\n"
- + " }\n"
- + " ]\n"
- + " }\n"
- + " ]\n"
- + " }\n"
- + "}\n";
+ String expected = "{"
+ + " \"root\": {"
+ + " \"fields\": {"
+ + " \"totalCount\": 0"
+ + " },"
+ + " \"id\": \"toplevel\","
+ + " \"relevance\": 1.0"
+ + " },"
+ + " \"trace\": {"
+ + " \"children\": ["
+ + " {"
+ + " \"message\": \"No query profile is used\""
+ + " },"
+ + " {"
+ + " \"children\": ["
+ + " {"
+ + " \"message\": \"string payload\""
+ + " },"
+ + " {"
+ + " \"message\": \"something\""
+ + " }"
+ + " ]"
+ + " }"
+ + " ]"
+ + " }"
+ + "}";
Query q = new Query("/?query=a&tracelevel=1");
Execution execution = new Execution(
Execution.Context.createContextStub());
@@ -421,38 +486,38 @@ public class JsonRendererTestCase {
@Test
public void testTracingOfNestedNodesWithDataAndSubnodes() throws IOException, InterruptedException, ExecutionException {
- String expected = "{\n"
- + " \"root\": {\n"
- + " \"fields\": {\n"
- + " \"totalCount\": 0\n"
- + " },\n"
- + " \"id\": \"toplevel\",\n"
- + " \"relevance\": 1.0\n"
- + " },\n"
- + " \"trace\": {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"message\": \"No query profile is used\"\n"
- + " },\n"
- + " {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"message\": \"string payload\",\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"message\": \"in OO languages, nesting is for birds\"\n"
- + " }\n"
- + " ]\n"
- + " }\n"
- + " ]\n"
- + " }\n"
- + " ]\n"
- + " }\n"
- + " ]\n"
- + " }\n"
- + "}\n";
+ String expected = "{"
+ + " \"root\": {"
+ + " \"fields\": {"
+ + " \"totalCount\": 0"
+ + " },"
+ + " \"id\": \"toplevel\","
+ + " \"relevance\": 1.0"
+ + " },"
+ + " \"trace\": {"
+ + " \"children\": ["
+ + " {"
+ + " \"message\": \"No query profile is used\""
+ + " },"
+ + " {"
+ + " \"children\": ["
+ + " {"
+ + " \"message\": \"string payload\","
+ + " \"children\": ["
+ + " {"
+ + " \"children\": ["
+ + " {"
+ + " \"message\": \"in OO languages, nesting is for birds\""
+ + " }"
+ + " ]"
+ + " }"
+ + " ]"
+ + " }"
+ + " ]"
+ + " }"
+ + " ]"
+ + " }"
+ + "}";
Query q = new Query("/?query=a&tracelevel=1");
Execution execution = new Execution(
Execution.Context.createContextStub());
@@ -470,67 +535,67 @@ public class JsonRendererTestCase {
@Test
public void test() throws IOException, InterruptedException, ExecutionException {
- String expected = "{\n"
- + " \"root\": {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"fields\": {\n"
- + " \"c\": \"d\"\n"
- + " },\n"
- + " \"id\": \"http://localhost/1\",\n"
- + " \"relevance\": 0.9,\n"
- + " \"types\": [\n"
- + " \"summary\"\n"
- + " ]\n"
- + " }\n"
- + " ],\n"
- + " \"id\": \"usual\",\n"
- + " \"relevance\": 1.0\n"
- + " },\n"
- + " {\n"
- + " \"fields\": {\n"
- + " \"e\": \"f\"\n"
- + " },\n"
- + " \"id\": \"type grouphit\",\n"
- + " \"relevance\": 1.0,\n"
- + " \"types\": [\n"
- + " \"grouphit\"\n"
- + " ]\n"
- + " },\n"
- + " {\n"
- + " \"fields\": {\n"
- + " \"b\": \"foo\"\n"
- + " },\n"
- + " \"id\": \"http://localhost/\",\n"
- + " \"relevance\": 0.95,\n"
- + " \"types\": [\n"
- + " \"summary\"\n"
- + " ]\n"
- + " }\n"
- + " ],\n"
- + " \"coverage\": {\n"
- + " \"coverage\": 100,\n"
- + " \"documents\": 500,\n"
- + " \"full\": true,\n"
- + " \"nodes\": 1,\n"
- + " \"results\": 1,\n"
- + " \"resultsFull\": 1\n"
- + " },\n"
- + " \"errors\": [\n"
- + " {\n"
- + " \"code\": 18,\n"
- + " \"message\": \"boom\",\n"
- + " \"summary\": \"Internal server error.\"\n"
- + " }\n"
- + " ],\n"
- + " \"fields\": {\n"
- + " \"totalCount\": 0\n"
- + " },\n"
- + " \"id\": \"toplevel\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
+ String expected = "{"
+ + " \"root\": {"
+ + " \"children\": ["
+ + " {"
+ + " \"children\": ["
+ + " {"
+ + " \"fields\": {"
+ + " \"c\": \"d\""
+ + " },"
+ + " \"id\": \"http://localhost/1\","
+ + " \"relevance\": 0.9,"
+ + " \"types\": ["
+ + " \"summary\""
+ + " ]"
+ + " }"
+ + " ],"
+ + " \"id\": \"usual\","
+ + " \"relevance\": 1.0"
+ + " },"
+ + " {"
+ + " \"fields\": {"
+ + " \"e\": \"f\""
+ + " },"
+ + " \"id\": \"type grouphit\","
+ + " \"relevance\": 1.0,"
+ + " \"types\": ["
+ + " \"grouphit\""
+ + " ]"
+ + " },"
+ + " {"
+ + " \"fields\": {"
+ + " \"b\": \"foo\""
+ + " },"
+ + " \"id\": \"http://localhost/\","
+ + " \"relevance\": 0.95,"
+ + " \"types\": ["
+ + " \"summary\""
+ + " ]"
+ + " }"
+ + " ],"
+ + " \"coverage\": {"
+ + " \"coverage\": 100,"
+ + " \"documents\": 500,"
+ + " \"full\": true,"
+ + " \"nodes\": 1,"
+ + " \"results\": 1,"
+ + " \"resultsFull\": 1"
+ + " },"
+ + " \"errors\": ["
+ + " {"
+ + " \"code\": 18,"
+ + " \"message\": \"boom\","
+ + " \"summary\": \"Internal server error.\""
+ + " }"
+ + " ],"
+ + " \"fields\": {"
+ + " \"totalCount\": 0"
+ + " },"
+ + " \"id\": \"toplevel\","
+ + " \"relevance\": 1.0"
+ + " }"
+ "}";
Query q = new Query("/?query=a&tracelevel=5");
Execution execution = new Execution(Execution.Context.createContextStub());
@@ -557,28 +622,28 @@ public class JsonRendererTestCase {
@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"
+ String expected = "{"
+ + " \"root\": {"
+ + " \"coverage\": {"
+ + " \"coverage\": 83,"
+ + " \"documents\": 500,"
+ + " \"degraded\" : {"
+ + " \"match-phase\" : true,"
+ + " \"timeout\" : false,"
+ + " \"adaptive-timeout\" : true,"
+ " \"non-ideal-state\" : false"
- + " },\n"
- + " \"full\": false,\n"
- + " \"nodes\": 1,\n"
- + " \"results\": 1,\n"
- + " \"resultsFull\": 0\n"
- + " },\n"
- + " \"fields\": {\n"
- + " \"totalCount\": 0\n"
- + " },\n"
- + " \"id\": \"toplevel\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
+ + " },"
+ + " \"full\": false,"
+ + " \"nodes\": 1,"
+ + " \"results\": 1,"
+ + " \"resultsFull\": 0"
+ + " },"
+ + " \"fields\": {"
+ + " \"totalCount\": 0"
+ + " },"
+ + " \"id\": \"toplevel\","
+ + " \"relevance\": 1.0"
+ + " }"
+ "}";
Query q = new Query("/?query=a&tracelevel=5");
Execution execution = new Execution(Execution.Context.createContextStub());
@@ -591,27 +656,27 @@ public class JsonRendererTestCase {
@Test
public void testMoreTypes() throws InterruptedException, ExecutionException, IOException {
- String expected = "{\n"
- + " \"root\": {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"fields\": {\n"
- + " \"bigDecimal\": 3.402823669209385e+38,\n"
- + " \"bigInteger\": 340282366920938463463374607431768211455,\n"
- + " \"byte\": 8,\n"
- + " \"short\": 16\n"
- + " },\n"
- + " \"id\": \"moredatatypestuff\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + " ],\n"
- + " \"fields\": {\n"
- + " \"totalCount\": 1\n"
- + " },\n"
- + " \"id\": \"toplevel\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + "}\n";
+ String expected = "{"
+ + " \"root\": {"
+ + " \"children\": ["
+ + " {"
+ + " \"fields\": {"
+ + " \"bigDecimal\": 3.402823669209385e+38,"
+ + " \"bigInteger\": 340282366920938463463374607431768211455,"
+ + " \"byte\": 8,"
+ + " \"short\": 16"
+ + " },"
+ + " \"id\": \"moredatatypestuff\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + " ],"
+ + " \"fields\": {"
+ + " \"totalCount\": 1"
+ + " },"
+ + " \"id\": \"toplevel\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + "}";
Result r = newEmptyResult();
Hit h = new Hit("moredatatypestuff");
h.setField("byte", Byte.valueOf((byte) 8));
@@ -629,24 +694,24 @@ public class JsonRendererTestCase {
@Test
public void testNullField() throws InterruptedException, ExecutionException, IOException {
- String expected = "{\n"
- + " \"root\": {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"fields\": {\n"
- + " \"null\": null\n"
- + " },\n"
- + " \"id\": \"nullstuff\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + " ],\n"
- + " \"fields\": {\n"
- + " \"totalCount\": 1\n"
- + " },\n"
- + " \"id\": \"toplevel\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + "}\n";
+ String expected = "{"
+ + " \"root\": {"
+ + " \"children\": ["
+ + " {"
+ + " \"fields\": {"
+ + " \"null\": null"
+ + " },"
+ + " \"id\": \"nullstuff\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + " ],"
+ + " \"fields\": {"
+ + " \"totalCount\": 1"
+ + " },"
+ + " \"id\": \"toplevel\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + "}";
Result r = newEmptyResult();
Hit h = new Hit("nullstuff");
h.setField("null", null);
@@ -658,22 +723,22 @@ public class JsonRendererTestCase {
@Test
public void testHitWithSource() throws IOException, InterruptedException, ExecutionException {
- String expected = "{\n"
- + " \"root\": {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"id\": \"datatypestuff\",\n"
- + " \"relevance\": 1.0,\n"
- + " \"source\": \"unit test\"\n"
- + " }\n"
- + " ],\n"
- + " \"fields\": {\n"
- + " \"totalCount\": 1\n"
- + " },\n"
- + " \"id\": \"toplevel\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + "}\n";
+ String expected = "{"
+ + " \"root\": {"
+ + " \"children\": ["
+ + " {"
+ + " \"id\": \"datatypestuff\","
+ + " \"relevance\": 1.0,"
+ + " \"source\": \"unit test\""
+ + " }"
+ + " ],"
+ + " \"fields\": {"
+ + " \"totalCount\": 1"
+ + " },"
+ + " \"id\": \"toplevel\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + "}";
Result r = newEmptyResult();
Hit h = new Hit("datatypestuff");
h.setSource("unit test");
@@ -685,23 +750,23 @@ public class JsonRendererTestCase {
@Test
public void testErrorWithStackTrace() throws InterruptedException, ExecutionException, IOException {
- String expected = "{\n"
- + " \"root\": {\n"
- + " \"errors\": [\n"
- + " {\n"
- + " \"code\": 1234,\n"
- + " \"message\": \"top of the day\",\n"
- + " \"stackTrace\": \"java.lang.Throwable\\n\\tat com.yahoo.search.rendering.JsonRendererTestCase.testErrorWithStackTrace(JsonRendererTestCase.java:732)\\n\",\n"
- + " \"summary\": \"hello\"\n"
- + " }\n"
- + " ],\n"
- + " \"fields\": {\n"
- + " \"totalCount\": 0\n"
- + " },\n"
- + " \"id\": \"toplevel\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + "}\n";
+ String expected = "{"
+ + " \"root\": {"
+ + " \"errors\": ["
+ + " {"
+ + " \"code\": 1234,"
+ + " \"message\": \"top of the day\","
+ + " \"stackTrace\": \"java.lang.Throwable\\n\\tat com.yahoo.search.rendering.JsonRendererTestCase.testErrorWithStackTrace(JsonRendererTestCase.java:732)\\n\","
+ + " \"summary\": \"hello\""
+ + " }"
+ + " ],"
+ + " \"fields\": {"
+ + " \"totalCount\": 0"
+ + " },"
+ + " \"id\": \"toplevel\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + "}";
Query q = new Query("/?query=a&tracelevel=5");
Result r = new Result(q);
Throwable t = new Throwable();
@@ -724,45 +789,45 @@ public class JsonRendererTestCase {
@Test
public void testGrouping() throws InterruptedException, ExecutionException, IOException {
- String expected = "{\n"
- + " \"root\": {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"fields\": {\n"
- + " \"count()\": 7\n"
- + " },\n"
- + " \"value\": \"Jones\",\n"
- + " \"id\": \"group:string:Jones\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + " ],\n"
- + " \"continuation\": {\n"
- + " \"next\": \"CCCC\",\n"
- + " \"prev\": \"BBBB\"\n"
- + " },\n"
- + " \"id\": \"grouplist:customer\",\n"
- + " \"label\": \"customer\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + " ],\n"
- + " \"continuation\": {\n"
- + " \"this\": \"AAAA\"\n"
- + " },\n"
- + " \"id\": \"group:root:0\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + " ],\n"
- + " \"fields\": {\n"
- + " \"totalCount\": 1\n"
- + " },\n"
- + " \"id\": \"toplevel\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + "}\n";
+ String expected = "{"
+ + " \"root\": {"
+ + " \"children\": ["
+ + " {"
+ + " \"children\": ["
+ + " {"
+ + " \"children\": ["
+ + " {"
+ + " \"fields\": {"
+ + " \"count()\": 7"
+ + " },"
+ + " \"value\": \"Jones\","
+ + " \"id\": \"group:string:Jones\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + " ],"
+ + " \"continuation\": {"
+ + " \"next\": \"CCCC\","
+ + " \"prev\": \"BBBB\""
+ + " },"
+ + " \"id\": \"grouplist:customer\","
+ + " \"label\": \"customer\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + " ],"
+ + " \"continuation\": {"
+ + " \"this\": \"AAAA\""
+ + " },"
+ + " \"id\": \"group:root:0\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + " ],"
+ + " \"fields\": {"
+ + " \"totalCount\": 1"
+ + " },"
+ + " \"id\": \"toplevel\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + "}";
Result r = newEmptyResult();
RootGroup rg = new RootGroup(0, new Continuation() {
@Override
@@ -810,44 +875,44 @@ public class JsonRendererTestCase {
@Test
public void testGroupingWithBucket() throws InterruptedException, ExecutionException, IOException {
- String expected = "{\n"
- + " \"root\": {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"fields\": {\n"
- + " \"something()\": 7\n"
- + " },\n"
- + " \"limits\": {\n"
- + " \"from\": \"1.0\",\n"
- + " \"to\": \"2.0\"\n"
- + " },\n"
- + " \"id\": \"group:double_bucket:1.0:2.0\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + " ],\n"
- + " \"id\": \"grouplist:customer\",\n"
- + " \"label\": \"customer\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + " ],\n"
- + " \"continuation\": {\n"
- + " \"this\": \"AAAA\"\n"
- + " },\n"
- + " \"id\": \"group:root:0\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + " ],\n"
- + " \"fields\": {\n"
- + " \"totalCount\": 1\n"
- + " },\n"
- + " \"id\": \"toplevel\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + "}\n";
+ String expected = "{"
+ + " \"root\": {"
+ + " \"children\": ["
+ + " {"
+ + " \"children\": ["
+ + " {"
+ + " \"children\": ["
+ + " {"
+ + " \"fields\": {"
+ + " \"something()\": 7"
+ + " },"
+ + " \"limits\": {"
+ + " \"from\": \"1.0\","
+ + " \"to\": \"2.0\""
+ + " },"
+ + " \"id\": \"group:double_bucket:1.0:2.0\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + " ],"
+ + " \"id\": \"grouplist:customer\","
+ + " \"label\": \"customer\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + " ],"
+ + " \"continuation\": {"
+ + " \"this\": \"AAAA\""
+ + " },"
+ + " \"id\": \"group:root:0\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + " ],"
+ + " \"fields\": {"
+ + " \"totalCount\": 1"
+ + " },"
+ + " \"id\": \"toplevel\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + "}";
Result r = newEmptyResult();
RootGroup rg = new RootGroup(0, new Continuation() {
@Override
@@ -873,40 +938,40 @@ public class JsonRendererTestCase {
@Test
public void testJsonObjects() throws InterruptedException, ExecutionException, IOException, JSONException {
- String expected = "{\n"
- + " \"root\": {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"fields\": {\n"
- + " \"inspectable\": {\n"
- + " \"a\": \"b\"\n"
- + " },\n"
- + " \"jackson\": {\n"
- + " \"Nineteen-eighty-four\": 1984\n"
- + " },\n"
- + " \"json producer\": {\n"
- + " \"long in structured\": 7809531904\n"
- + " },\n"
- + " \"org.json array\": [\n"
- + " true,\n"
- + " true,\n"
- + " false\n"
- + " ],\n"
- + " \"org.json object\": {\n"
- + " \"forty-two\": 42\n"
- + " }\n"
- + " },\n"
- + " \"id\": \"json objects\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + " ],\n"
- + " \"fields\": {\n"
- + " \"totalCount\": 0\n"
- + " },\n"
- + " \"id\": \"toplevel\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + "}\n";
+ String expected = "{"
+ + " \"root\": {"
+ + " \"children\": ["
+ + " {"
+ + " \"fields\": {"
+ + " \"inspectable\": {"
+ + " \"a\": \"b\""
+ + " },"
+ + " \"jackson\": {"
+ + " \"Nineteen-eighty-four\": 1984"
+ + " },"
+ + " \"json producer\": {"
+ + " \"long in structured\": 7809531904"
+ + " },"
+ + " \"org.json array\": ["
+ + " true,"
+ + " true,"
+ + " false"
+ + " ],"
+ + " \"org.json object\": {"
+ + " \"forty-two\": 42"
+ + " }"
+ + " },"
+ + " \"id\": \"json objects\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + " ],"
+ + " \"fields\": {"
+ + " \"totalCount\": 0"
+ + " },"
+ + " \"id\": \"toplevel\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + "}";
Result r = newEmptyResult();
Hit h = new Hit("json objects");
JSONObject o = new JSONObject();
@@ -936,24 +1001,24 @@ public class JsonRendererTestCase {
@Test
public void testFieldValueInHit() throws IOException, InterruptedException, ExecutionException {
- String expected = "{\n"
- + " \"root\": {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"fields\": {\n"
+ String expected = "{"
+ + " \"root\": {"
+ + " \"children\": ["
+ + " {"
+ + " \"fields\": {"
+ " \"fromDocumentApi\":{\"integerField\":123, \"stringField\":\"abc\"}"
- + " },\n"
- + " \"id\": \"fieldValueTest\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + " ],\n"
- + " \"fields\": {\n"
- + " \"totalCount\": 1\n"
- + " },\n"
- + " \"id\": \"toplevel\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + "}\n";
+ + " },"
+ + " \"id\": \"fieldValueTest\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + " ],"
+ + " \"fields\": {"
+ + " \"totalCount\": 1"
+ + " },"
+ + " \"id\": \"toplevel\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + "}";
Result r = newEmptyResult();
Hit h = new Hit("fieldValueTest");
StructDataType structType = new StructDataType("jsonRenderer");
@@ -971,21 +1036,21 @@ public class JsonRendererTestCase {
@Test
public void testHiddenFields() throws IOException, InterruptedException, ExecutionException {
- String expected = "{\n"
- + " \"root\": {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"id\": \"hiddenFields\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + " ],\n"
- + " \"fields\": {\n"
- + " \"totalCount\": 1\n"
- + " },\n"
- + " \"id\": \"toplevel\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + "}\n";
+ String expected = "{"
+ + " \"root\": {"
+ + " \"children\": ["
+ + " {"
+ + " \"id\": \"hiddenFields\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + " ],"
+ + " \"fields\": {"
+ + " \"totalCount\": 1"
+ + " },"
+ + " \"id\": \"toplevel\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + "}";
Result r = newEmptyResult();
Hit h = createHitWithOnlyHiddenFields();
r.hits().add(h);
@@ -996,27 +1061,27 @@ public class JsonRendererTestCase {
@Test
public void testDebugRendering() throws IOException, InterruptedException, ExecutionException {
- String expected = "{\n"
- + " \"root\": {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"fields\": {\n"
- + " \"NaN\": \"NaN\",\n"
- + " \"emptyString\": \"\",\n"
- + " \"emptyStringFieldValue\": \"\",\n"
- + " \"$vespaImplementationDetail\": \"Hello, World!\"\n"
- + " },\n"
- + " \"id\": \"hiddenFields\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + " ],\n"
- + " \"fields\": {\n"
- + " \"totalCount\": 1\n"
- + " },\n"
- + " \"id\": \"toplevel\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + "}\n";
+ String expected = "{"
+ + " \"root\": {"
+ + " \"children\": ["
+ + " {"
+ + " \"fields\": {"
+ + " \"NaN\": \"NaN\","
+ + " \"emptyString\": \"\","
+ + " \"emptyStringFieldValue\": \"\","
+ + " \"$vespaImplementationDetail\": \"Hello, World!\""
+ + " },"
+ + " \"id\": \"hiddenFields\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + " ],"
+ + " \"fields\": {"
+ + " \"totalCount\": 1"
+ + " },"
+ + " \"id\": \"toplevel\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + "}";
Result r = new Result(new Query("/?renderer.json.debug=true"));
Hit h = createHitWithOnlyHiddenFields();
r.hits().add(h);
@@ -1062,24 +1127,24 @@ public class JsonRendererTestCase {
@Test
public void testJsonCallback() throws IOException, InterruptedException, ExecutionException {
- String expected = "{\n"
- + " \"root\": {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"fields\": {\n"
- + " \"documentid\": \"id:unittest:smoke::whee\"\n"
- + " },\n"
- + " \"id\": \"id:unittest:smoke::whee\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + " ],\n"
- + " \"fields\": {\n"
- + " \"totalCount\": 1\n"
- + " },\n"
- + " \"id\": \"toplevel\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + "}\n";
+ String expected = "{"
+ + " \"root\": {"
+ + " \"children\": ["
+ + " {"
+ + " \"fields\": {"
+ + " \"documentid\": \"id:unittest:smoke::whee\""
+ + " },"
+ + " \"id\": \"id:unittest:smoke::whee\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + " ],"
+ + " \"fields\": {"
+ + " \"totalCount\": 1"
+ + " },"
+ + " \"id\": \"toplevel\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + "}";
String jsonCallback = "some_function_name";
Result r = newEmptyResult(new String[] {"query=a", "jsoncallback="+jsonCallback} );
@@ -1100,28 +1165,28 @@ public class JsonRendererTestCase {
@Test
public void testMapInField() throws IOException, InterruptedException, ExecutionException {
- String expected = "{\n"
- + " \"root\": {\n"
- + " \"children\": [\n"
- + " {\n"
- + " \"fields\": {\n"
- + " \"structured\": {\n"
- + " \"foo\": \"string foo\",\n"
- + " \"bar\": [\"array bar elem 1\", \"array bar elem 2\"],\n"
- + " \"baz\": {\"f1\": \"object baz field 1\", \"f2\": \"object baz field 2\"}\n"
- + " }\n"
- + " },\n"
- + " \"id\": \"MapInField\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + " ],\n"
- + " \"fields\": {\n"
- + " \"totalCount\": 1\n"
- + " },\n"
- + " \"id\": \"toplevel\",\n"
- + " \"relevance\": 1.0\n"
- + " }\n"
- + "}\n";
+ String expected = "{"
+ + " \"root\": {"
+ + " \"children\": ["
+ + " {"
+ + " \"fields\": {"
+ + " \"structured\": {"
+ + " \"foo\": \"string foo\","
+ + " \"bar\": [\"array bar elem 1\", \"array bar elem 2\"],"
+ + " \"baz\": {\"f1\": \"object baz field 1\", \"f2\": \"object baz field 2\"}"
+ + " }"
+ + " },"
+ + " \"id\": \"MapInField\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + " ],"
+ + " \"fields\": {"
+ + " \"totalCount\": 1"
+ + " },"
+ + " \"id\": \"toplevel\","
+ + " \"relevance\": 1.0"
+ + " }"
+ + "}";
Result r = newEmptyResult();
Hit h = new Hit("MapInField");
Value.ArrayValue atop = new Value.ArrayValue();
@@ -1164,7 +1229,7 @@ public class JsonRendererTestCase {
String summary = render(result);
String expected =
- "{ \n" +
+ "{ " +
" \"root\":{ " +
" \"id\":\"toplevel\"," +
" \"relevance\":1.0," +
@@ -1176,13 +1241,13 @@ public class JsonRendererTestCase {
" \"id\":\"http://abc.html/\"," +
" \"relevance\":1.0," +
" \"fields\":{ " +
- " \"sddocname\":\"one\",\n" +
- " \"dynteaser\":\"Feeding <hi>documents</hi> into Vespa is<sep />increment of a set of <hi>documents</hi> fed into Vespa <sep />float in XML when <hi>document</hi> attribute is int<sep />\"\n" +
- " }\n" +
- " }\n" +
- " ]\n" +
- " }\n" +
- "}\n";
+ " \"sddocname\":\"one\"," +
+ " \"dynteaser\":\"Feeding <hi>documents</hi> into Vespa is<sep />increment of a set of <hi>documents</hi> fed into Vespa <sep />float in XML when <hi>document</hi> attribute is int<sep />\"" +
+ " }" +
+ " }" +
+ " ]" +
+ " }" +
+ "}";
assertEqualJson(expected, summary);
}
@@ -1218,12 +1283,13 @@ public class JsonRendererTestCase {
@SuppressWarnings("unchecked")
private void assertEqualJson(String expected, String generated) throws IOException {
+ assertEquals("", validateJSON(expected));
+ assertEquals("", validateJSON(generated));
+
ObjectMapper m = new ObjectMapper();
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) {
diff --git a/documentapi/abi-spec.json b/documentapi/abi-spec.json
index 1619d053aa2..663d04e3074 100644
--- a/documentapi/abi-spec.json
+++ b/documentapi/abi-spec.json
@@ -1054,9 +1054,9 @@
"public com.yahoo.documentapi.messagebus.MessageBusParams setProtocolConfigId(java.lang.String)",
"public com.yahoo.documentapi.messagebus.MessageBusParams setRouteName(java.lang.String)",
"public com.yahoo.documentapi.messagebus.MessageBusParams setRoute(java.lang.String)",
- "public java.lang.String getRoute()",
"public com.yahoo.documentapi.messagebus.MessageBusParams setRouteNameForGet(java.lang.String)",
"public com.yahoo.documentapi.messagebus.MessageBusParams setRouteForGet(java.lang.String)",
+ "public java.lang.String getRoute()",
"public java.lang.String getRouteForGet()",
"public int getTraceLevel()",
"public com.yahoo.documentapi.messagebus.MessageBusParams setTraceLevel(int)",
diff --git a/processing/abi-spec.json b/processing/abi-spec.json
index 464049ec116..78058f1a8b7 100644
--- a/processing/abi-spec.json
+++ b/processing/abi-spec.json
@@ -127,6 +127,7 @@
"public void setForceTimestamps(boolean)",
"public boolean getForceTimestamps()",
"public void trace(java.lang.String, int)",
+ "public void trace(java.lang.Object, int)",
"public void logValue(java.lang.String, java.lang.String)",
"public java.util.Iterator logValueIterator()",
"public com.yahoo.yolean.trace.TraceVisitor accept(com.yahoo.yolean.trace.TraceVisitor)",
diff --git a/processing/src/main/java/com/yahoo/processing/execution/Execution.java b/processing/src/main/java/com/yahoo/processing/execution/Execution.java
index 957e39bf41f..9aff586ef75 100644
--- a/processing/src/main/java/com/yahoo/processing/execution/Execution.java
+++ b/processing/src/main/java/com/yahoo/processing/execution/Execution.java
@@ -310,6 +310,9 @@ public class Execution {
* Adds a trace message to this trace, if this trace has at most the given trace level
*/
public void trace(String message, int traceLevel) {
+ trace((Object)message, traceLevel);
+ }
+ public void trace(Object message, int traceLevel) {
if (this.traceLevel >= traceLevel) {
traceNode.add(new TraceNode(message, timestamp(traceLevel, forceTimestamps)));
}
diff --git a/searchcore/src/vespa/searchcore/proton/matchengine/matchengine.cpp b/searchcore/src/vespa/searchcore/proton/matchengine/matchengine.cpp
index 8fcb30a4143..3a28eeb4dfd 100644
--- a/searchcore/src/vespa/searchcore/proton/matchengine/matchengine.cpp
+++ b/searchcore/src/vespa/searchcore/proton/matchengine/matchengine.cpp
@@ -2,9 +2,12 @@
#include "matchengine.h"
#include <vespa/searchcore/proton/common/state_reporter_utils.h>
#include <vespa/vespalib/data/slime/cursor.h>
-#include <algorithm>
+#include <vespa/vespalib/data/smart_buffer.h>
+#include <vespa/vespalib/data/slime/binary_format.h>
+#include <vespa/vespalib/stllike/asciistream.h>
#include <vespa/log/log.h>
+
LOG_SETUP(".proton.matchengine.matchengine");
namespace {
@@ -47,7 +50,6 @@ MatchEngine::MatchEngine(size_t numThreads, size_t threadsPerSearch, uint32_t di
_threadBundlePool(std::max(size_t(1), threadsPerSearch)),
_nodeUp(false)
{
- // empty
}
MatchEngine::~MatchEngine()
@@ -103,9 +105,7 @@ MatchEngine::search(search::engine::SearchRequest::Source request,
return ret;
}
- vespalib::Executor::Task::UP task;
- task.reset(new SearchTask(*this, std::move(request), client));
- _executor.execute(std::move(task));
+ _executor.execute(std::make_unique<SearchTask>(*this, std::move(request), client));
return search::engine::SearchReply::UP();
}
@@ -113,9 +113,9 @@ void
MatchEngine::performSearch(search::engine::SearchRequest::Source req,
search::engine::SearchClient &client)
{
- search::engine::SearchReply::UP ret(new search::engine::SearchReply);
+ auto ret = std::make_unique<search::engine::SearchReply>();
- if (req.get() != NULL) {
+ if (req.get()) {
ISearchHandler::SP searchHandler;
vespalib::SimpleThreadBundle::UP threadBundle = _threadBundlePool.obtain();
{ // try to find the match handler corresponding to the specified search doc type
@@ -123,7 +123,7 @@ MatchEngine::performSearch(search::engine::SearchRequest::Source req,
DocTypeName docTypeName(*req.get());
searchHandler = _handlers.getHandler(docTypeName);
}
- if (searchHandler.get() != NULL) {
+ if (searchHandler) {
ret = searchHandler->match(searchHandler, *req.get(), *threadBundle);
} else {
HandlerMap<ISearchHandler>::Snapshot::UP snapshot;
@@ -140,6 +140,13 @@ MatchEngine::performSearch(search::engine::SearchRequest::Source req,
}
ret->request = req.release();
ret->setDistributionKey(_distributionKey);
+ if (ret->request->getTraceLevel() > 0) {
+ ret->request->trace().getRoot().setLong("distribution-key", _distributionKey);
+ search::fef::Properties & trace = ret->propertiesMap.lookupCreate("trace");
+ vespalib::SmartBuffer output(4096);
+ vespalib::slime::BinaryFormat::encode(ret->request->trace().getSlime(), output);
+ trace.add("slime", output.obtain().make_stringref());
+ }
client.searchDone(std::move(ret));
}
diff --git a/searchcore/src/vespa/searchcore/proton/matching/match_tools.h b/searchcore/src/vespa/searchcore/proton/matching/match_tools.h
index 0ecf6eb5b78..dfef51bfc5e 100644
--- a/searchcore/src/vespa/searchcore/proton/matching/match_tools.h
+++ b/searchcore/src/vespa/searchcore/proton/matching/match_tools.h
@@ -132,6 +132,7 @@ public:
std::unique_ptr<AttributeOperationTask> createOnSummaryTask() const;
const RequestContext & requestContext() const { return _requestContext; }
+ const Query & query() const { return _query; }
};
}
diff --git a/searchcore/src/vespa/searchcore/proton/matching/matcher.cpp b/searchcore/src/vespa/searchcore/proton/matching/matcher.cpp
index 382e197c16b..4ebf74c373f 100644
--- a/searchcore/src/vespa/searchcore/proton/matching/matcher.cpp
+++ b/searchcore/src/vespa/searchcore/proton/matching/matcher.cpp
@@ -14,6 +14,7 @@
#include <vespa/searchlib/engine/searchreply.h>
#include <vespa/searchlib/features/setup.h>
#include <vespa/searchlib/fef/test/plugin/setup.h>
+#include <vespa/vespalib/data/slime/inserter.h>
#include <vespa/log/log.h>
LOG_SETUP(".proton.matching.matcher");
@@ -203,6 +204,17 @@ Matcher::computeNumThreadsPerSearch(Blueprint::HitEstimate hits, const Propertie
return threads;
}
+namespace {
+ void traceQuery(const SearchRequest &request, const Query & query) {
+ if (request.getTraceLevel() > 3) {
+ if (query.peekRoot()) {
+ vespalib::slime::ObjectInserter inserter(request.trace().createCursor("blueprint"), "optimized");
+ query.peekRoot()->asSlime(inserter);
+ }
+ }
+ }
+}
+
SearchReply::UP
Matcher::match(const SearchRequest &request, vespalib::ThreadBundle &threadBundle,
ISearchContext &searchContext, IAttributeContext &attrContext, SessionManager &sessionMgr,
@@ -240,6 +252,7 @@ Matcher::match(const SearchRequest &request, vespalib::ThreadBundle &threadBundl
if (!mtf->valid()) {
reply->errorCode = ECODE_QUERY_PARSE_ERROR;
reply->errorMessage = "query execution failed (invalid query)";
+ traceQuery(request, mtf->query());
return reply;
}
@@ -274,6 +287,7 @@ Matcher::match(const SearchRequest &request, vespalib::ThreadBundle &threadBundl
sessionMgr.insert(std::move(session));
}
reply = std::move(result->_reply);
+ traceQuery(request, mtf->query());
uint32_t numActiveLids = metaStore.getNumActiveLids();
// note: this is actually totalSpace+1, since 0 is reserved
diff --git a/searchlib/src/tests/engine/searchapi/searchapi_test.cpp b/searchlib/src/tests/engine/searchapi/searchapi_test.cpp
index a517890620c..626435360a0 100644
--- a/searchlib/src/tests/engine/searchapi/searchapi_test.cpp
+++ b/searchlib/src/tests/engine/searchapi/searchapi_test.cpp
@@ -1,10 +1,12 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#include <vespa/log/log.h>
-LOG_SETUP("searchapi_test");
+
#include <vespa/vespalib/testkit/testapp.h>
#include <vespa/searchlib/common/packets.h>
#include <vespa/searchlib/engine/searchapi.h>
#include <vespa/searchlib/engine/packetconverter.h>
+#include <vespa/vespalib/data/slime/slime.h>
+#include <vespa/log/log.h>
+LOG_SETUP("searchapi_test");
using namespace search::engine;
using namespace search::fs4transport;
@@ -28,18 +30,8 @@ template <typename T> void copyPacket(T &src, T &dst) {
} // namespace <unnamed>
-class Test : public vespalib::TestApp
-{
-public:
- void propertyNames();
- void convertToRequest();
- void convertFromReply();
- int Main() override;
-};
-
-void
-Test::propertyNames()
-{
+
+TEST("propertyNames") {
EXPECT_EQUAL(search::MapNames::RANK, "rank");
EXPECT_EQUAL(search::MapNames::FEATURE, "feature");
EXPECT_EQUAL(search::MapNames::HIGHLIGHTTERMS, "highlightterms");
@@ -47,9 +39,7 @@ Test::propertyNames()
EXPECT_EQUAL(search::MapNames::CACHES, "caches");
}
-void
-Test::convertToRequest()
-{
+TEST("convertToReques") {
FS4Packet_QUERYX src;
src._offset = 2u;
src._maxhits = 3u;
@@ -116,9 +106,7 @@ Test::convertToRequest()
}
}
-void
-Test::convertFromReply()
-{
+TEST("convertFromReply") {
SearchReply src;
src.offset = 1u;
src.totalHitCount = 2u;
@@ -240,14 +228,46 @@ Test::convertFromReply()
}
}
-int
-Test::Main()
-{
- TEST_INIT("searchapi_test");
- propertyNames();
- convertToRequest();
- convertFromReply();
- TEST_DONE();
+void verify(vespalib::stringref expected, const vespalib::Slime & slime) {
+ vespalib::Slime expectedSlime;
+ vespalib::slime::JsonFormat::decode(expected, expectedSlime);
+ EXPECT_EQUAL(expectedSlime, slime);
+}
+
+TEST("verify trace") {
+ Trace t(7);
+ verify("{"
+ " traces: ["
+ " ],"
+ " creation_time: 7"
+ "}",
+ t.getSlime());
+
+ t.createCursor("tag_a");
+ verify("{"
+ " traces: ["
+ " {"
+ " tag: 'tag_a'"
+ " }"
+ " ],"
+ " creation_time: 7"
+ "}",
+ t.getSlime());
+ Trace::Cursor & tagB = t.createCursor("tag_b");
+ tagB.setLong("long", 19);
+ verify("{"
+ " traces: ["
+ " {"
+ " tag: 'tag_a'"
+ " },"
+ " {"
+ " tag: 'tag_b',"
+ " long: 19"
+ " }"
+ " ],"
+ " creation_time: 7"
+ "}",
+ t.getSlime());
}
-TEST_APPHOOK(Test);
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp b/searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp
index e5ee83507ae..108b7b9d20d 100644
--- a/searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp
+++ b/searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp
@@ -5,6 +5,7 @@
#include <vespa/searchlib/queryeval/intermediate_blueprints.h>
#include <vespa/vespalib/objects/objectdumper.h>
#include <vespa/vespalib/objects/visit.h>
+#include <vespa/vespalib/data/slime/slime.h>
#include <algorithm>
#include <vespa/log/log.h>
@@ -178,6 +179,7 @@ public:
void testSearchCreation();
void testBlueprintMakeNew();
void requireThatAsStringWorks();
+ void requireThatAsSlimeWorks();
void requireThatVisitMembersWorks();
void requireThatDocIdLimitInjectionWorks();
int Main() override;
@@ -698,6 +700,58 @@ getExpectedBlueprint()
"}\n";
}
+vespalib::string
+getExpectedSlimeBlueprint() {
+ return "{"
+ " '[type]': '(anonymous namespace)::MyOr',"
+ " isTermLike: true,"
+ " fields: {"
+ " '[type]': 'FieldList',"
+ " '[0]': {"
+ " '[type]': 'Field',"
+ " fieldId: 5,"
+ " handle: 7,"
+ " isFilter: false"
+ " }"
+ " },"
+ " estimate: {"
+ " '[type]': 'HitEstimate',"
+ " empty: false,"
+ " estHits: 9,"
+ " tree_size: 2,"
+ " allow_termwise_eval: 0"
+ " },"
+ " sourceId: 4294967295,"
+ " docid_limit: 0,"
+ " children: {"
+ " '[type]': 'std::vector',"
+ " '[0]': {"
+ " isTermLike: true,"
+ " fields: {"
+ " '[type]': 'FieldList',"
+ " '[0]': {"
+ " '[type]': 'Field',"
+ " fieldId: 5,"
+ " handle: 7,"
+ " isFilter: false"
+ " }"
+ " },"
+ " '[type]': '(anonymous namespace)::MyTerm',"
+ " estimate: {"
+ " '[type]': 'HitEstimate',"
+ " empty: false,"
+ " estHits: 9,"
+ " tree_size: 1,"
+ " allow_termwise_eval: 1"
+ " },"
+ " sourceId: 4294967295,"
+ " docid_limit: 0"
+ " }"
+ " }"
+ "}";
+}
+
+
struct BlueprintFixture
{
MyOr _blueprint;
@@ -714,6 +768,18 @@ Test::requireThatAsStringWorks()
}
void
+Test::requireThatAsSlimeWorks()
+{
+ BlueprintFixture f;
+ vespalib::Slime slime;
+ f._blueprint.asSlime(vespalib::slime::SlimeInserter(slime));
+ auto s = slime.toString();
+ vespalib::Slime expectedSlime;
+ vespalib::slime::JsonFormat::decode(getExpectedSlimeBlueprint(), expectedSlime);
+ EXPECT_EQUAL(expectedSlime, slime);
+}
+
+void
Test::requireThatVisitMembersWorks()
{
BlueprintFixture f;
@@ -749,6 +815,7 @@ Test::Main()
testSearchCreation();
testBlueprintMakeNew();
requireThatAsStringWorks();
+ requireThatAsSlimeWorks();
requireThatVisitMembersWorks();
requireThatDocIdLimitInjectionWorks();
TEST_DONE();
diff --git a/searchlib/src/vespa/searchlib/attribute/imported_attribute_vector.cpp b/searchlib/src/vespa/searchlib/attribute/imported_attribute_vector.cpp
index 0ba4bc814d5..9e3b0bbde3e 100644
--- a/searchlib/src/vespa/searchlib/attribute/imported_attribute_vector.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/imported_attribute_vector.cpp
@@ -39,8 +39,7 @@ ImportedAttributeVector::ImportedAttributeVector(vespalib::stringref name,
{
}
-ImportedAttributeVector::~ImportedAttributeVector() {
-}
+ImportedAttributeVector::~ImportedAttributeVector() = default;
std::unique_ptr<AttributeReadGuard>
ImportedAttributeVector::makeReadGuard(bool stableEnumGuard) const
diff --git a/searchlib/src/vespa/searchlib/attribute/imported_attribute_vector.h b/searchlib/src/vespa/searchlib/attribute/imported_attribute_vector.h
index e6356866ed9..33afaaf7f1e 100644
--- a/searchlib/src/vespa/searchlib/attribute/imported_attribute_vector.h
+++ b/searchlib/src/vespa/searchlib/attribute/imported_attribute_vector.h
@@ -6,11 +6,9 @@
#include <vespa/vespalib/stllike/string.h>
#include <memory>
-namespace search {
+namespace search { struct IDocumentMetaStoreContext; }
-struct IDocumentMetaStoreContext;
-
-namespace attribute {
+namespace search::attribute {
class BitVectorSearchCache;
class ReadableAttributeVector;
@@ -63,7 +61,7 @@ public:
return _name;
}
- virtual std::unique_ptr<AttributeReadGuard> makeReadGuard(bool stableEnumGuard) const override;
+ std::unique_ptr<AttributeReadGuard> makeReadGuard(bool stableEnumGuard) const override;
protected:
vespalib::string _name;
@@ -75,4 +73,3 @@ protected:
};
}
-}
diff --git a/searchlib/src/vespa/searchlib/attribute/imported_attribute_vector_read_guard.cpp b/searchlib/src/vespa/searchlib/attribute/imported_attribute_vector_read_guard.cpp
index 428b14671cd..50fa1ffded3 100644
--- a/searchlib/src/vespa/searchlib/attribute/imported_attribute_vector_read_guard.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/imported_attribute_vector_read_guard.cpp
@@ -6,8 +6,7 @@
#include "reference_attribute.h"
#include <vespa/searchlib/query/queryterm.h>
-namespace search {
-namespace attribute {
+namespace search::attribute {
ImportedAttributeVectorReadGuard::ImportedAttributeVectorReadGuard(
const ImportedAttributeVector &imported_attribute,
@@ -24,8 +23,7 @@ ImportedAttributeVectorReadGuard::ImportedAttributeVectorReadGuard(
_targetLids = _reference_attribute.getTargetLids();
}
-ImportedAttributeVectorReadGuard::~ImportedAttributeVectorReadGuard() {
-}
+ImportedAttributeVectorReadGuard::~ImportedAttributeVectorReadGuard() = default;
const vespalib::string& ImportedAttributeVectorReadGuard::getName() const {
return _imported_attribute.getName();
@@ -169,4 +167,3 @@ long ImportedAttributeVectorReadGuard::onSerializeForDescendingSort(DocId doc,
}
}
-}
diff --git a/searchlib/src/vespa/searchlib/attribute/imported_attribute_vector_read_guard.h b/searchlib/src/vespa/searchlib/attribute/imported_attribute_vector_read_guard.h
index 4cf4d5b64c1..f130a095006 100644
--- a/searchlib/src/vespa/searchlib/attribute/imported_attribute_vector_read_guard.h
+++ b/searchlib/src/vespa/searchlib/attribute/imported_attribute_vector_read_guard.h
@@ -45,49 +45,48 @@ protected:
}
public:
- ImportedAttributeVectorReadGuard(const ImportedAttributeVector &imported_attribute,
- bool stableEnumGuard);
- ~ImportedAttributeVectorReadGuard();
+ ImportedAttributeVectorReadGuard(const ImportedAttributeVector &imported_attribute, bool stableEnumGuard);
+ ~ImportedAttributeVectorReadGuard() override;
- virtual const vespalib::string &getName() const override;
- virtual uint32_t getNumDocs() const override;
- virtual uint32_t getValueCount(uint32_t doc) const override;
- virtual uint32_t getMaxValueCount() const override;
- virtual largeint_t getInt(DocId doc) const override;
- virtual double getFloat(DocId doc) const override;
- virtual const char *getString(DocId doc, char *buffer, size_t sz) const override;
- virtual EnumHandle getEnum(DocId doc) const override;
- virtual uint32_t get(DocId docId, largeint_t *buffer, uint32_t sz) const override;
- virtual uint32_t get(DocId docId, double *buffer, uint32_t sz) const override;
- virtual uint32_t get(DocId docId, const char **buffer, uint32_t sz) const override;
- virtual uint32_t get(DocId docId, EnumHandle *buffer, uint32_t sz) const override;
- virtual uint32_t get(DocId docId, WeightedInt *buffer, uint32_t sz) const override;
- virtual uint32_t get(DocId docId, WeightedFloat *buffer, uint32_t sz) const override;
- virtual uint32_t get(DocId docId, WeightedString *buffer, uint32_t sz) const override;
- virtual uint32_t get(DocId docId, WeightedConstChar *buffer, uint32_t sz) const override;
- virtual uint32_t get(DocId docId, WeightedEnum *buffer, uint32_t sz) const override;
- virtual bool findEnum(const char * value, EnumHandle & e) const override;
- virtual std::vector<EnumHandle> findFoldedEnums(const char *value) const override;
+ const vespalib::string &getName() const override;
+ uint32_t getNumDocs() const override;
+ uint32_t getValueCount(uint32_t doc) const override;
+ uint32_t getMaxValueCount() const override;
+ largeint_t getInt(DocId doc) const override;
+ double getFloat(DocId doc) const override;
+ const char *getString(DocId doc, char *buffer, size_t sz) const override;
+ EnumHandle getEnum(DocId doc) const override;
+ uint32_t get(DocId docId, largeint_t *buffer, uint32_t sz) const override;
+ uint32_t get(DocId docId, double *buffer, uint32_t sz) const override;
+ uint32_t get(DocId docId, const char **buffer, uint32_t sz) const override;
+ uint32_t get(DocId docId, EnumHandle *buffer, uint32_t sz) const override;
+ uint32_t get(DocId docId, WeightedInt *buffer, uint32_t sz) const override;
+ uint32_t get(DocId docId, WeightedFloat *buffer, uint32_t sz) const override;
+ uint32_t get(DocId docId, WeightedString *buffer, uint32_t sz) const override;
+ uint32_t get(DocId docId, WeightedConstChar *buffer, uint32_t sz) const override;
+ uint32_t get(DocId docId, WeightedEnum *buffer, uint32_t sz) const override;
+ bool findEnum(const char * value, EnumHandle & e) const override;
+ std::vector<EnumHandle> findFoldedEnums(const char *value) const override;
- virtual const char * getStringFromEnum(EnumHandle e) const override;
- virtual std::unique_ptr<ISearchContext> createSearchContext(std::unique_ptr<QueryTermSimple> term,
- const SearchContextParams &params) const override;
- virtual const IDocumentWeightAttribute *asDocumentWeightAttribute() const override;
- virtual const tensor::ITensorAttribute *asTensorAttribute() const override;
- virtual BasicType::Type getBasicType() const override;
- virtual size_t getFixedWidth() const override;
- virtual CollectionType::Type getCollectionType() const override;
- virtual bool hasEnum() const override;
- virtual bool getIsFilter() const override;
- virtual bool getIsFastSearch() const override;
- virtual uint32_t getCommittedDocIdLimit() const override;
- virtual bool isImported() const override;
+ const char * getStringFromEnum(EnumHandle e) const override;
+ std::unique_ptr<ISearchContext> createSearchContext(std::unique_ptr<QueryTermSimple> term,
+ const SearchContextParams &params) const override;
+ const IDocumentWeightAttribute *asDocumentWeightAttribute() const override;
+ const tensor::ITensorAttribute *asTensorAttribute() const override;
+ BasicType::Type getBasicType() const override;
+ size_t getFixedWidth() const override;
+ CollectionType::Type getCollectionType() const override;
+ bool hasEnum() const override;
+ bool getIsFilter() const override;
+ bool getIsFastSearch() const override;
+ uint32_t getCommittedDocIdLimit() const override;
+ bool isImported() const override;
protected:
- virtual long onSerializeForAscendingSort(DocId doc, void * serTo, long available,
- const common::BlobConverter * bc) const override;
- virtual long onSerializeForDescendingSort(DocId doc, void * serTo, long available,
- const common::BlobConverter * bc) const override;
+ long onSerializeForAscendingSort(DocId doc, void * serTo, long available,
+ const common::BlobConverter * bc) const override;
+ long onSerializeForDescendingSort(DocId doc, void * serTo, long available,
+ const common::BlobConverter * bc) const override;
};
}
diff --git a/searchlib/src/vespa/searchlib/engine/CMakeLists.txt b/searchlib/src/vespa/searchlib/engine/CMakeLists.txt
index 95b47b991af..e475d786a60 100644
--- a/searchlib/src/vespa/searchlib/engine/CMakeLists.txt
+++ b/searchlib/src/vespa/searchlib/engine/CMakeLists.txt
@@ -13,6 +13,7 @@ vespa_add_library(searchlib_engine OBJECT
searchreply.cpp
searchrequest.cpp
source_description.cpp
+ trace.cpp
transport_metrics.cpp
transportserver.cpp
DEPENDS
diff --git a/searchlib/src/vespa/searchlib/engine/packetconverter.cpp b/searchlib/src/vespa/searchlib/engine/packetconverter.cpp
index e6fb37223d6..d87d9888aae 100644
--- a/searchlib/src/vespa/searchlib/engine/packetconverter.cpp
+++ b/searchlib/src/vespa/searchlib/engine/packetconverter.cpp
@@ -1,9 +1,14 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "packetconverter.h"
+#include <vespa/searchlib/fef/indexproperties.h>
+
#include <vespa/log/log.h>
LOG_SETUP(".engine.packetconverter");
+using search::fef::Property;
+using search::fef::Properties;
+
namespace {
bool checkFeature(uint32_t features, uint32_t mask) {
@@ -14,8 +19,8 @@ struct FS4PropertiesBuilder : public search::fef::IPropertiesVisitor {
uint32_t idx;
search::fs4transport::FS4Properties &props;
FS4PropertiesBuilder(search::fs4transport::FS4Properties &p) : idx(0), props(p) {}
- void visitProperty(const search::fef::Property::Value &key,
- const search::fef::Property &values) override
+ void visitProperty(const Property::Value &key,
+ const Property &values) override
{
for (uint32_t i = 0; i < values.size(); ++i) {
props.setKey(idx, key.data(), key.size());
@@ -39,7 +44,7 @@ PacketConverter::fillPacketProperties(const PropertiesMap &source, PropsVector&
PropertiesMap::ITR end = source.end();
for (uint32_t i = 0; itr != end; ++itr, ++i) {
const vespalib::string &name = itr->first;
- const search::fef::Properties &values = itr->second;
+ const Properties &values = itr->second;
target[i].setName(name.c_str(), name.size());
target[i].allocEntries(values.numValues());
FS4PropertiesBuilder builder(target[i]);
@@ -58,9 +63,8 @@ PacketConverter::toSearchRequest(const QUERYX &packet, SearchRequest &request)
request.queryFlags = packet.getQueryFlags();
request.ranking = packet._ranking;
- for (uint32_t i = 0; i < packet._propsVector.size(); ++i) {
- const FS4Properties &src = packet._propsVector[i];
- search::fef::Properties &dst = request.propertiesMap.lookupCreate(src.getName());
+ for (const FS4Properties &src : packet._propsVector) {
+ Properties &dst = request.propertiesMap.lookupCreate(src.getName());
for (uint32_t e = 0; e < src.size(); ++e) {
dst.add(vespalib::stringref(src.getKey(e), src.getKeyLen(e)),
vespalib::stringref(src.getValue(e), src.getValueLen(e)));
@@ -72,6 +76,7 @@ PacketConverter::toSearchRequest(const QUERYX &packet, SearchRequest &request)
request.location = packet._location;
request.stackItems = packet._numStackItems;
request.stackDump.assign( packet._stackDump.begin(), packet._stackDump.end());
+ request.setTraceLevel(search::fef::indexproperties::trace::Level::lookup(request.propertiesMap.modelOverrides()));
}
void
@@ -100,7 +105,7 @@ PacketConverter::fromSearchReply(const SearchReply &reply, QUERYRESULTX &packet)
packet._totNumDocs = reply.totalHitCount;
packet._maxRank = reply.maxRank;
packet.setDistributionKey(reply.getDistributionKey());
- if (reply.sortIndex.size() > 0) {
+ if ( ! reply.sortIndex.empty()) {
packet._features |= QRF_SORTDATA;
uint32_t idxCnt = reply.sortIndex.size();
LOG_ASSERT(reply.sortIndex.size() == reply.hits.size()+1);
@@ -112,7 +117,7 @@ PacketConverter::fromSearchReply(const SearchReply &reply, QUERYRESULTX &packet)
}
memcpy(packet._sortData, &(reply.sortData[0]), reply.sortData.size());
}
- if (reply.groupResult.size() > 0) {
+ if ( ! reply.groupResult.empty()) {
packet._features |= QRF_GROUPDATA;
packet.AllocateGroupData(reply.groupResult.size());
memcpy(packet._groupData, &(reply.groupResult[0]), reply.groupResult.size());
@@ -123,8 +128,10 @@ PacketConverter::fromSearchReply(const SearchReply &reply, QUERYRESULTX &packet)
packet._coverageDegradeReason = reply.coverage.getDegradeReason();
packet.setNodesQueried(reply.coverage.getNodesQueried());
packet.setNodesReplied(reply.coverage.getNodesReplied());
- if (reply.request && (reply.request->queryFlags & QFLAG_COVERAGE_NODES)) {
- packet._features |= QRF_COVERAGE_NODES;
+ if (reply.request) {
+ if (reply.request->queryFlags & QFLAG_COVERAGE_NODES) {
+ packet._features |= QRF_COVERAGE_NODES;
+ }
}
if (reply.useWideHits) {
packet._features |= QRF_MLD;
@@ -150,9 +157,8 @@ PacketConverter::toDocsumRequest(const GETDOCSUMSX &packet, DocsumRequest &reque
request.ranking = packet._ranking;
request.queryFlags = packet._qflags;
request.resultClassName = packet._resultClassName;
- for (uint32_t i = 0; i < packet._propsVector.size(); ++i) {
- const FS4Properties &src = packet._propsVector[i];
- search::fef::Properties &dst = request.propertiesMap.lookupCreate(src.getName());
+ for (const FS4Properties &src : packet._propsVector) {
+ Properties &dst = request.propertiesMap.lookupCreate(src.getName());
for (uint32_t e = 0; e < src.size(); ++e) {
dst.add(vespalib::stringref(src.getKey(e), src.getKeyLen(e)),
vespalib::stringref(src.getValue(e), src.getValueLen(e)));
@@ -169,8 +175,7 @@ PacketConverter::toDocsumRequest(const GETDOCSUMSX &packet, DocsumRequest &reque
request.hits[i].gid = packet._docid[i]._gid;
request.hits[i].path = packet._docid[i]._partid;
}
- search::fef::Property sessionId =
- request.propertiesMap.rankProperties().lookup("sessionId");
+ Property sessionId = request.propertiesMap.rankProperties().lookup("sessionId");
if (sessionId.found()) {
vespalib::string id = sessionId.get();
request.sessionId.assign(id.begin(), id.end());
@@ -198,7 +203,7 @@ PacketConverter::toDocsumReplyElement(const DOCSUM &packet, DocsumReply::Docsum
void
PacketConverter::fromDocsumReplyElement(const DocsumReply::Docsum &docsum, DOCSUM &packet)
{
- if (docsum.data.get() != 0) {
+ if (docsum.data.get() != nullptr) {
packet.SetBuf(docsum.data.c_str(), docsum.data.size());
}
packet.setGid(docsum.gid);
diff --git a/searchlib/src/vespa/searchlib/engine/propertiesmap.cpp b/searchlib/src/vespa/searchlib/engine/propertiesmap.cpp
index b09813c0258..4726a3b9c27 100644
--- a/searchlib/src/vespa/searchlib/engine/propertiesmap.cpp
+++ b/searchlib/src/vespa/searchlib/engine/propertiesmap.cpp
@@ -11,7 +11,7 @@ PropertiesMap::PropertiesMap()
: _propertiesMap()
{ }
-PropertiesMap::~PropertiesMap() { }
+PropertiesMap::~PropertiesMap() = default;
fef::Properties &
PropertiesMap::lookupCreate(vespalib::stringref name)
diff --git a/searchlib/src/vespa/searchlib/engine/request.cpp b/searchlib/src/vespa/searchlib/engine/request.cpp
index 8f4aa427988..fd4a46ccc43 100644
--- a/searchlib/src/vespa/searchlib/engine/request.cpp
+++ b/searchlib/src/vespa/searchlib/engine/request.cpp
@@ -8,12 +8,14 @@ namespace search::engine {
Request::Request(const fastos::TimeStamp &start_time)
: _startTime(start_time),
_timeOfDoom(fastos::TimeStamp(fastos::TimeStamp::FUTURE)),
- ranking(),
+ _traceLevel(0),
queryFlags(0),
+ ranking(),
location(),
propertiesMap(),
stackItems(0),
- stackDump()
+ stackDump(),
+ _trace(start_time)
{
}
diff --git a/searchlib/src/vespa/searchlib/engine/request.h b/searchlib/src/vespa/searchlib/engine/request.h
index 02ab75fe509..521a5be39fc 100644
--- a/searchlib/src/vespa/searchlib/engine/request.h
+++ b/searchlib/src/vespa/searchlib/engine/request.h
@@ -3,6 +3,7 @@
#pragma once
#include "propertiesmap.h"
+#include "trace.h"
#include <vespa/fastos/timestamp.h>
namespace search::engine {
@@ -11,6 +12,8 @@ class Request
{
public:
Request(const fastos::TimeStamp &start_time);
+ Request(const Request &) = delete;
+ Request & operator =(const Request &) = delete;
virtual ~Request();
void setTimeout(const fastos::TimeStamp & timeout);
fastos::TimeStamp getStartTime() const { return _startTime; }
@@ -26,17 +29,24 @@ public:
bool should_drop_sort_data() const;
+ uint32_t getTraceLevel() const { return _traceLevel; }
+ Request & setTraceLevel(uint32_t traceLevel) { _traceLevel = traceLevel; return *this; }
+
+ Trace & trace() const { return _trace; }
private:
const fastos::TimeStamp _startTime;
fastos::TimeStamp _timeOfDoom;
+ uint32_t _traceLevel;
public:
/// Everything here should move up to private section and have accessors
- vespalib::string ranking;
uint32_t queryFlags;
+ vespalib::string ranking;
vespalib::string location;
PropertiesMap propertiesMap;
uint32_t stackItems;
std::vector<char> stackDump;
+private:
+ mutable Trace _trace;
};
}
diff --git a/searchlib/src/vespa/searchlib/engine/searchrequest.cpp b/searchlib/src/vespa/searchlib/engine/searchrequest.cpp
index 7159c83716b..b558e07480d 100644
--- a/searchlib/src/vespa/searchlib/engine/searchrequest.cpp
+++ b/searchlib/src/vespa/searchlib/engine/searchrequest.cpp
@@ -17,20 +17,20 @@ SearchRequest::SearchRequest(const fastos::TimeStamp &start_time)
sessionId()
{ }
-SearchRequest::~SearchRequest() {}
+SearchRequest::~SearchRequest() = default;
void SearchRequest::Source::lazyDecode() const
{
- if ((_request.get() == NULL) && (_fs4Packet != NULL)) {
- _request.reset(new SearchRequest(_start));
+ if (!_request && (_fs4Packet != nullptr)) {
+ _request = std::make_unique<SearchRequest>(_start);
PacketConverter::toSearchRequest(*_fs4Packet, *_request);
_fs4Packet->Free();
- _fs4Packet = NULL;
+ _fs4Packet = nullptr;
}
}
SearchRequest::Source::~Source() {
- if (_fs4Packet != NULL) {
+ if (_fs4Packet != nullptr) {
_fs4Packet->Free();
}
}
diff --git a/searchlib/src/vespa/searchlib/engine/trace.cpp b/searchlib/src/vespa/searchlib/engine/trace.cpp
new file mode 100644
index 00000000000..e12fb9affa6
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/engine/trace.cpp
@@ -0,0 +1,31 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "trace.h"
+#include <vespa/vespalib/data/slime/slime.h>
+#include <vespa/fastos/timestamp.h>
+
+namespace search::engine {
+
+Trace::Trace(const fastos::TimeStamp &start_time)
+ : _trace(std::make_unique<vespalib::Slime>()),
+ _root(_trace->setObject()),
+ _traces(_root.setArray("traces"))
+{
+ _root.setLong("creation_time", start_time);
+}
+
+Trace::~Trace() = default;
+
+Trace::Cursor &
+Trace::createCursor(vespalib::stringref name) {
+ Cursor & trace = _traces.addObject();
+ trace.setString("tag", name);
+ return trace;
+}
+
+vespalib::string
+Trace::toString() const {
+ return _trace->toString();
+}
+
+}
diff --git a/searchlib/src/vespa/searchlib/engine/trace.h b/searchlib/src/vespa/searchlib/engine/trace.h
new file mode 100644
index 00000000000..09d61c0f199
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/engine/trace.h
@@ -0,0 +1,39 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <vespa/vespalib/stllike/string.h>
+
+namespace fastos { class TimeStamp; }
+namespace vespalib { class Slime; }
+namespace vespalib::slime { class Cursor; }
+
+namespace search::engine {
+
+ /**
+ * Used for adding traces to a request. Acquire a new Cursor for everytime you want to trace something.
+ * Note that it is not thread safe. All use of any cursor aquired must be thread safe.
+ */
+class Trace
+{
+public:
+ using Cursor = vespalib::slime::Cursor;
+ Trace(const fastos::TimeStamp &start_time);
+ ~Trace();
+
+ /**
+ * Will give you a trace entry. It will also add a timestamp relative to the creation of the trace.
+ * @param name
+ * @return a Cursor to use for further tracing.
+ */
+ Cursor & createCursor(vespalib::stringref name);
+ vespalib::string toString() const;
+ Cursor & getRoot() const { return _root; }
+ vespalib::Slime & getSlime() const { return *_trace; }
+private:
+ std::unique_ptr<vespalib::Slime> _trace;
+ Cursor & _root;
+ Cursor & _traces;
+};
+
+}
diff --git a/searchlib/src/vespa/searchlib/fef/indexproperties.cpp b/searchlib/src/vespa/searchlib/fef/indexproperties.cpp
index 5cd6c479d24..fd73ce5a0f0 100644
--- a/searchlib/src/vespa/searchlib/fef/indexproperties.cpp
+++ b/searchlib/src/vespa/searchlib/fef/indexproperties.cpp
@@ -388,6 +388,24 @@ DiversityCutoffStrategy::lookup(const Properties &props, const vespalib::string
return lookupString(props, NAME, defaultValue);
}
+}
+
+namespace trace {
+
+const vespalib::string Level::NAME("tracelevel");
+const uint32_t Level::DEFAULT_VALUE(0);
+
+uint32_t
+Level::lookup(const Properties &props)
+{
+ return lookup(props, DEFAULT_VALUE);
+}
+
+uint32_t
+Level::lookup(const Properties &props, uint32_t defaultValue)
+{
+ return lookupUint32(props, NAME, defaultValue);
+}
}
diff --git a/searchlib/src/vespa/searchlib/fef/indexproperties.h b/searchlib/src/vespa/searchlib/fef/indexproperties.h
index 8b78e347a90..23efe514d68 100644
--- a/searchlib/src/vespa/searchlib/fef/indexproperties.h
+++ b/searchlib/src/vespa/searchlib/fef/indexproperties.h
@@ -310,6 +310,20 @@ namespace matchphase {
} // namespace matchphase
+namespace trace {
+
+ /**
+ * Property for the heap size used in the hit collector.
+ **/
+ struct Level {
+ static const vespalib::string NAME;
+ static const uint32_t DEFAULT_VALUE;
+ static uint32_t lookup(const Properties &props);
+ static uint32_t lookup(const Properties &props, uint32_t defaultValue);
+ };
+
+}
+
namespace hitcollector {
diff --git a/searchlib/src/vespa/searchlib/queryeval/blueprint.cpp b/searchlib/src/vespa/searchlib/queryeval/blueprint.cpp
index c99e07cd355..3f3b4a2300e 100644
--- a/searchlib/src/vespa/searchlib/queryeval/blueprint.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/blueprint.cpp
@@ -6,7 +6,9 @@
#include "equiv_blueprint.h"
#include <vespa/vespalib/objects/visit.hpp>
#include <vespa/vespalib/objects/objectdumper.h>
+#include <vespa/vespalib/objects/object2slime.h>
#include <vespa/vespalib/util/classname.h>
+#include <vespa/vespalib/data/slime/inserter.h>
#include <map>
#include <vespa/log/log.h>
@@ -117,6 +119,15 @@ Blueprint::asString() const
return dumper.toString();
}
+vespalib::slime::Cursor &
+Blueprint::asSlime(const vespalib::slime::Inserter & inserter) const
+{
+ vespalib::slime::Cursor & cursor = inserter.insertObject();
+ vespalib::Object2Slime dumper(cursor);
+ visit(dumper, "", this);
+ return cursor;
+}
+
vespalib::string
Blueprint::getClassName() const
{
diff --git a/searchlib/src/vespa/searchlib/queryeval/blueprint.h b/searchlib/src/vespa/searchlib/queryeval/blueprint.h
index 165f592867a..4b60e7d0d8b 100644
--- a/searchlib/src/vespa/searchlib/queryeval/blueprint.h
+++ b/searchlib/src/vespa/searchlib/queryeval/blueprint.h
@@ -9,7 +9,11 @@
#include <vespa/searchlib/fef/termfieldmatchdata.h>
#include <vespa/searchlib/fef/termfieldmatchdataarray.h>
-namespace vespalib { class ObjectVisitor; };
+namespace vespalib { class ObjectVisitor; }
+namespace vespalib::slime {
+ class Cursor;
+ class Inserter;
+}
namespace search::queryeval {
@@ -173,6 +177,7 @@ public:
// for debug dumping
vespalib::string asString() const;
+ vespalib::slime::Cursor & asSlime(const vespalib::slime::Inserter & cursor) const;
virtual vespalib::string getClassName() const;
virtual void visitMembers(vespalib::ObjectVisitor &visitor) const;
virtual bool isEquiv() const { return false; }
diff --git a/staging_vespalib/src/vespa/vespalib/objects/CMakeLists.txt b/staging_vespalib/src/vespa/vespalib/objects/CMakeLists.txt
index 12970a7d39b..f3afda94fe6 100644
--- a/staging_vespalib/src/vespa/vespalib/objects/CMakeLists.txt
+++ b/staging_vespalib/src/vespa/vespalib/objects/CMakeLists.txt
@@ -5,6 +5,7 @@ vespa_add_library(staging_vespalib_vespalib_objects OBJECT
namedobject.cpp
objectvisitor.cpp
objectdumper.cpp
+ object2slime.cpp
visit.cpp
objectpredicate.cpp
objectoperation.cpp
diff --git a/staging_vespalib/src/vespa/vespalib/objects/object2slime.cpp b/staging_vespalib/src/vespa/vespalib/objects/object2slime.cpp
new file mode 100644
index 00000000000..562f332fb89
--- /dev/null
+++ b/staging_vespalib/src/vespa/vespalib/objects/object2slime.cpp
@@ -0,0 +1,76 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "object2slime.h"
+#include <vespa/vespalib/util/stringfmt.h>
+#include <vespa/vespalib/data/slime/cursor.h>
+
+namespace vespalib {
+
+Object2Slime::Object2Slime(slime::Cursor & cursor)
+ : _cursor(cursor),
+ _stack()
+{
+}
+
+Object2Slime::~Object2Slime() = default;
+
+//-----------------------------------------------------------------------------
+
+void
+Object2Slime::openStruct(const vespalib::string &name, const vespalib::string &type)
+{
+ if (name.empty()) {
+ _cursor.get().setString("[type]", type);
+ } else {
+ _stack.push_back(_cursor);
+ _cursor = _cursor.get().setObject(name);
+ _cursor.get().setString("[type]", type);
+ }
+}
+
+void
+Object2Slime::closeStruct()
+{
+ if ( ! _stack.empty()) {
+ _cursor = _stack.back();
+ _stack.pop_back();
+ }
+}
+
+void
+Object2Slime::visitBool(const vespalib::string &name, bool value)
+{
+ _cursor.get().setBool(name, value);
+}
+
+void
+Object2Slime::visitInt(const vespalib::string &name, int64_t value)
+{
+ _cursor.get().setLong(name, value);
+}
+
+void
+Object2Slime::visitFloat(const vespalib::string &name, double value)
+{
+ _cursor.get().setDouble(name, value);
+}
+
+void
+Object2Slime::visitString(const vespalib::string &name, const vespalib::string &value)
+{
+ _cursor.get().setString(name, value);
+}
+
+void
+Object2Slime::visitNull(const vespalib::string &name)
+{
+ _cursor.get().setNix(name);
+}
+
+void
+Object2Slime::visitNotImplemented()
+{
+ _cursor.get().setNix("not_implemented");
+}
+
+}
diff --git a/staging_vespalib/src/vespa/vespalib/objects/object2slime.h b/staging_vespalib/src/vespa/vespalib/objects/object2slime.h
new file mode 100644
index 00000000000..4c1ee660502
--- /dev/null
+++ b/staging_vespalib/src/vespa/vespalib/objects/object2slime.h
@@ -0,0 +1,36 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "objectvisitor.h"
+#include <vector>
+
+namespace vespalib {
+
+namespace slime { class Cursor; }
+
+/**
+ * This is a concrete object visitor that will build up a structured
+ * slime representation of an object.
+ **/
+class Object2Slime : public ObjectVisitor
+{
+private:
+ std::reference_wrapper<slime::Cursor> _cursor;
+ std::vector<std::reference_wrapper<slime::Cursor>> _stack;
+
+public:
+ Object2Slime(slime::Cursor & cursor);
+ ~Object2Slime();
+
+ void openStruct(const vespalib::string &name, const vespalib::string &type) override;
+ void closeStruct() override;
+ void visitBool(const vespalib::string &name, bool value) override;
+ void visitInt(const vespalib::string &name, int64_t value) override;
+ void visitFloat(const vespalib::string &name, double value) override;
+ void visitString(const vespalib::string &name, const vespalib::string &value) override;
+ void visitNull(const vespalib::string &name) override;
+ void visitNotImplemented() override;
+};
+
+} // namespace vespalib
diff --git a/staging_vespalib/src/vespa/vespalib/objects/objectdumper.cpp b/staging_vespalib/src/vespa/vespalib/objects/objectdumper.cpp
index 3237996aafd..67ce43bb0c0 100644
--- a/staging_vespalib/src/vespa/vespalib/objects/objectdumper.cpp
+++ b/staging_vespalib/src/vespa/vespalib/objects/objectdumper.cpp
@@ -41,8 +41,7 @@ ObjectDumper::ObjectDumper(int indent)
{
}
-ObjectDumper::~ObjectDumper() {
-}
+ObjectDumper::~ObjectDumper() = default;
//-----------------------------------------------------------------------------
diff --git a/staging_vespalib/src/vespa/vespalib/objects/objectdumper.h b/staging_vespalib/src/vespa/vespalib/objects/objectdumper.h
index 27f37bf839a..e517c75deec 100644
--- a/staging_vespalib/src/vespa/vespalib/objects/objectdumper.h
+++ b/staging_vespalib/src/vespa/vespalib/objects/objectdumper.h
@@ -60,15 +60,14 @@ public:
**/
vespalib::string toString() const { return _str; }
- virtual void openStruct(const vespalib::string &name, const vespalib::string &type) override;
- virtual void closeStruct() override;
- virtual void visitBool(const vespalib::string &name, bool value) override;
- virtual void visitInt(const vespalib::string &name, int64_t value) override;
- virtual void visitFloat(const vespalib::string &name, double value) override;
- virtual void visitString(const vespalib::string &name, const vespalib::string &value) override;
- virtual void visitNull(const vespalib::string &name) override;
- virtual void visitNotImplemented() override;
+ void openStruct(const vespalib::string &name, const vespalib::string &type) override;
+ void closeStruct() override;
+ void visitBool(const vespalib::string &name, bool value) override;
+ void visitInt(const vespalib::string &name, int64_t value) override;
+ void visitFloat(const vespalib::string &name, double value) override;
+ void visitString(const vespalib::string &name, const vespalib::string &value) override;
+ void visitNull(const vespalib::string &name) override;
+ void visitNotImplemented() override;
};
} // namespace vespalib
-