summaryrefslogtreecommitdiffstats
path: root/container-search
diff options
context:
space:
mode:
authorArne H Juul <arnej@yahooinc.com>2022-02-11 15:04:09 +0000
committerArne H Juul <arnej@yahooinc.com>2022-02-11 18:26:39 +0000
commit9ea871e3958ee387c6c38023a1b5ca770cc20c13 (patch)
tree9d6cb559bf0a6e06057f3ce09d7c4482feead8dd /container-search
parentfc8d4006be96b568fe4a7c57fbb36db3e5f58a2a (diff)
parent0be7fcf7bf80995a2bbcf19bb88860c13b3f55e5 (diff)
Merge branch 'master' into arnej/wset-and-map-rendering
Conflicts: container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java
Diffstat (limited to 'container-search')
-rw-r--r--container-search/abi-spec.json17
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/fastsearch/FastHit.java2
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/query/Item.java2
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/query/parser/AbstractParser.java1
-rw-r--r--container-search/src/main/java/com/yahoo/search/Query.java31
-rw-r--r--container-search/src/main/java/com/yahoo/search/cluster/ClusterMonitor.java5
-rw-r--r--container-search/src/main/java/com/yahoo/search/cluster/ClusterSearcher.java9
-rw-r--r--container-search/src/main/java/com/yahoo/search/cluster/NodeManager.java3
-rw-r--r--container-search/src/main/java/com/yahoo/search/dispatch/Dispatcher.java7
-rw-r--r--container-search/src/main/java/com/yahoo/search/dispatch/LeanHit.java14
-rw-r--r--container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/SearchCluster.java3
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/Presentation.java99
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/QueryProfileProperties.java86
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/compiled/CompiledQueryProfileRegistry.java44
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/config/QueryProfileConfigurer.java1
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/types/FieldDescription.java4
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/properties/PropertyAliases.java1
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/properties/QueryProperties.java48
-rw-r--r--container-search/src/main/java/com/yahoo/search/querytransform/DefaultPositionSearcher.java22
-rw-r--r--container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java97
-rw-r--r--container-search/src/main/java/com/yahoo/search/yql/YqlParser.java2
-rw-r--r--container-search/src/main/java/com/yahoo/vespa/streamingvisitors/MetricsSearcher.java1
-rw-r--r--container-search/src/main/java/com/yahoo/vespa/streamingvisitors/QueryEncoder.java90
-rw-r--r--container-search/src/main/java/com/yahoo/vespa/streamingvisitors/VdsVisitor.java8
-rw-r--r--container-search/src/test/java/com/yahoo/prelude/searcher/test/QuotingSearcherTestCase.java1
-rw-r--r--container-search/src/test/java/com/yahoo/search/cluster/test/ClusteredConnectionTestCase.java2
-rw-r--r--container-search/src/test/java/com/yahoo/search/query/profile/compiled/CompiledQueryProfileRegistryTest.java7
-rw-r--r--container-search/src/test/java/com/yahoo/search/query/profile/test/DumpToolTestCase.java7
-rw-r--r--container-search/src/test/java/com/yahoo/search/query/profile/types/test/NameTestCase.java1
-rw-r--r--container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java27
-rw-r--r--container-search/src/test/java/com/yahoo/search/searchers/ValidateNearestNeighborTestCase.java11
-rw-r--r--container-search/src/test/java/com/yahoo/search/searchers/test/ValidateMatchPhaseSearcherTestCase.java6
-rw-r--r--container-search/src/test/java/com/yahoo/search/test/QueryTestCase.java58
-rw-r--r--container-search/src/test/java/com/yahoo/vespa/streamingvisitors/VdsVisitorTestCase.java1
34 files changed, 519 insertions, 199 deletions
diff --git a/container-search/abi-spec.json b/container-search/abi-spec.json
index f2117764be1..8cd32985057 100644
--- a/container-search/abi-spec.json
+++ b/container-search/abi-spec.json
@@ -720,7 +720,6 @@
"public static final enum com.yahoo.prelude.query.Item$ItemType WORD",
"public static final enum com.yahoo.prelude.query.Item$ItemType INT",
"public static final enum com.yahoo.prelude.query.Item$ItemType PHRASE",
- "public static final enum com.yahoo.prelude.query.Item$ItemType PAREN",
"public static final enum com.yahoo.prelude.query.Item$ItemType PREFIX",
"public static final enum com.yahoo.prelude.query.Item$ItemType SUBSTRING",
"public static final enum com.yahoo.prelude.query.Item$ItemType NEAR",
@@ -2120,6 +2119,7 @@
"public void <init>(com.yahoo.component.ComponentId, java.util.List, boolean)",
"public void <init>(com.yahoo.component.ComponentId, java.util.List, com.yahoo.search.cluster.Hasher, boolean)",
"protected void <init>(com.yahoo.component.ComponentId, java.util.List, com.yahoo.search.cluster.Hasher, boolean, boolean)",
+ "public java.lang.String name()",
"public final void ping(com.yahoo.search.cluster.ClusterMonitor, java.lang.Object, java.util.concurrent.Executor)",
"protected abstract com.yahoo.prelude.Pong ping(com.yahoo.prelude.Ping, java.lang.Object)",
"protected java.lang.Object getFirstConnection(com.yahoo.search.cluster.Hasher$NodeList, int, int, com.yahoo.search.Query)",
@@ -2213,6 +2213,7 @@
"abstract"
],
"methods": [
+ "public java.lang.String name()",
"public abstract void working(java.lang.Object)",
"public abstract void failed(java.lang.Object)",
"public void ping(java.lang.Object, java.util.concurrent.Executor)",
@@ -5236,13 +5237,16 @@
"public java.lang.String getFormat()",
"public void setFormat(java.lang.String)",
"public java.lang.Object clone()",
- "public boolean equals(java.lang.Object)",
- "public int hashCode()",
"public boolean getTiming()",
"public void setTiming(boolean)",
"public java.util.Set getSummaryFields()",
+ "public void setSummaryFields(java.lang.String)",
+ "public boolean getTensorShortForm()",
+ "public void setTensorShortForm(java.lang.String)",
+ "public void setTensorShortForm(boolean)",
"public void prepare()",
- "public void setSummaryFields(java.lang.String)"
+ "public boolean equals(java.lang.Object)",
+ "public int hashCode()"
],
"fields": [
"public static final java.lang.String PRESENTATION",
@@ -5250,6 +5254,7 @@
"public static final java.lang.String TIMING",
"public static final java.lang.String SUMMARY",
"public static final java.lang.String SUMMARY_FIELDS",
+ "public static final java.lang.String TENSORS",
"public static final java.lang.String FORMAT"
]
},
@@ -6244,7 +6249,7 @@
"public"
],
"methods": [
- "public void <init>(com.yahoo.search.query.profile.config.QueryProfilesConfig)",
+ "public void <init>(com.yahoo.search.query.profile.config.QueryProfilesConfig, java.util.concurrent.Executor)",
"public void <init>()",
"public void <init>(com.yahoo.search.query.profile.types.QueryProfileTypeRegistry)",
"public final void register(com.yahoo.search.query.profile.compiled.CompiledQueryProfile)",
@@ -7157,6 +7162,7 @@
"methods": [
"public void <init>(com.fasterxml.jackson.core.JsonGenerator, boolean)",
"public void <init>(com.fasterxml.jackson.core.JsonGenerator, boolean, boolean)",
+ "protected void <init>(boolean, boolean, boolean)",
"public void <init>(com.fasterxml.jackson.core.JsonGenerator, boolean, boolean, boolean)",
"public void accept(java.lang.String, java.lang.Object)",
"public void accept(java.lang.String, byte[], int, int)",
@@ -7198,6 +7204,7 @@
"public void endResponse()",
"public java.lang.String getEncoding()",
"public java.lang.String getMimeType()",
+ "protected com.yahoo.search.rendering.JsonRenderer$FieldConsumer createFieldConsumer(boolean)",
"protected com.yahoo.search.rendering.JsonRenderer$FieldConsumer createFieldConsumer(com.fasterxml.jackson.core.JsonGenerator, boolean)"
],
"fields": []
diff --git a/container-search/src/main/java/com/yahoo/prelude/fastsearch/FastHit.java b/container-search/src/main/java/com/yahoo/prelude/fastsearch/FastHit.java
index 7c7f01b414a..094367dc140 100644
--- a/container-search/src/main/java/com/yahoo/prelude/fastsearch/FastHit.java
+++ b/container-search/src/main/java/com/yahoo/prelude/fastsearch/FastHit.java
@@ -377,7 +377,7 @@ public class FastHit extends Hit {
}
}
- private static void appendAsHex(byte [] gid, StringBuilder sb) {
+ private static void appendAsHex(byte[] gid, StringBuilder sb) {
for (byte b : gid) {
String hex = Integer.toHexString(0xFF & b);
if (hex.length() == 1) {
diff --git a/container-search/src/main/java/com/yahoo/prelude/query/Item.java b/container-search/src/main/java/com/yahoo/prelude/query/Item.java
index 670983f0cd7..41d897b8d83 100644
--- a/container-search/src/main/java/com/yahoo/prelude/query/Item.java
+++ b/container-search/src/main/java/com/yahoo/prelude/query/Item.java
@@ -39,7 +39,7 @@ public abstract class Item implements Cloneable {
WORD(4),
INT(5),
PHRASE(6),
- PAREN(7), // TODO not used - remove on Vespa 8
+ // 7 was PAREN, unused in Vespa 7
PREFIX(8),
SUBSTRING(9),
NEAR(11),
diff --git a/container-search/src/main/java/com/yahoo/prelude/query/parser/AbstractParser.java b/container-search/src/main/java/com/yahoo/prelude/query/parser/AbstractParser.java
index 5fb90deaa0a..586d1d32d57 100644
--- a/container-search/src/main/java/com/yahoo/prelude/query/parser/AbstractParser.java
+++ b/container-search/src/main/java/com/yahoo/prelude/query/parser/AbstractParser.java
@@ -3,7 +3,6 @@ package com.yahoo.prelude.query.parser;
import com.yahoo.language.Language;
import com.yahoo.language.process.Segmenter;
-import com.yahoo.log.event.*;
import com.yahoo.prelude.Index;
import com.yahoo.prelude.IndexFacts;
import com.yahoo.prelude.query.*;
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 623c38fa9f0..1c38bb8f876 100644
--- a/container-search/src/main/java/com/yahoo/search/Query.java
+++ b/container-search/src/main/java/com/yahoo/search/Query.java
@@ -235,18 +235,18 @@ public class Query extends com.yahoo.processing.Request implements Cloneable {
private static final Map<String, CompoundName> propertyAliases;
static {
Map<String,CompoundName> propertyAliasesBuilder = new HashMap<>();
- addAliases(Query.getArgumentType(), propertyAliasesBuilder);
- addAliases(Ranking.getArgumentType(), propertyAliasesBuilder);
- addAliases(Model.getArgumentType(), propertyAliasesBuilder);
- addAliases(Presentation.getArgumentType(), propertyAliasesBuilder);
- addAliases(Select.getArgumentType(), propertyAliasesBuilder);
+ addAliases(Query.getArgumentType(), CompoundName.empty, propertyAliasesBuilder);
propertyAliases = ImmutableMap.copyOf(propertyAliasesBuilder);
}
- private static void addAliases(QueryProfileType arguments, Map<String, CompoundName> aliases) {
- CompoundName prefix = getPrefix(arguments);
+ private static void addAliases(QueryProfileType arguments, CompoundName prefix, Map<String, CompoundName> aliases) {
for (FieldDescription field : arguments.fields().values()) {
for (String alias : field.getAliases())
aliases.put(alias, prefix.append(field.getName()));
+ if (field.getType() instanceof QueryProfileFieldType) {
+ var type = ((QueryProfileFieldType) field.getType()).getQueryProfileType();
+ if (type != null)
+ addAliases(type, prefix.append(type.getComponentIdAsCompoundName()), aliases);
+ }
}
}
@@ -1048,6 +1048,7 @@ public class Query extends com.yahoo.processing.Request implements Cloneable {
return new SessionId(requestId, getRanking().getProfile());
}
+ @Deprecated // TODO: Remove on Vespa 8
public boolean hasEncodableProperties() {
if ( ! ranking.getProperties().isEmpty()) return true;
if ( ! ranking.getFeatures().isEmpty()) return true;
@@ -1064,39 +1065,29 @@ public class Query extends com.yahoo.processing.Request implements Cloneable {
* @param buffer the buffer to encode to
* @param encodeQueryData true to encode all properties, false to only include session information, not actual query data
* @return the encoded length
+ * @deprecated do not use
*/
+ @Deprecated // TODO: Remove on Vespa 8
public int encodeAsProperties(ByteBuffer buffer, boolean encodeQueryData) {
// Make sure we don't encode anything here if we have turned the property feature off
// Due to sendQuery we sometimes end up turning this feature on and then encoding a 0 int as the number of
// property maps - that's ok (probably we should simplify by just always turning the feature on)
if (! hasEncodableProperties()) return 0;
-
int start = buffer.position();
-
int mapCountPosition = buffer.position();
buffer.putInt(0); // map count will go here
-
int mapCount = 0;
-
- // TODO: Push down
mapCount += ranking.getProperties().encode(buffer, encodeQueryData);
if (encodeQueryData) {
mapCount += ranking.getFeatures().encode(buffer);
-
- // TODO: Push down
if (presentation.getHighlight() != null) {
mapCount += MapEncoder.encodeMultiMap(Highlight.HIGHLIGHTTERMS, presentation.getHighlight().getHighlightTerms(), 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);
-
buffer.putInt(mapCountPosition, mapCount);
-
return buffer.position() - start;
}
@@ -1126,7 +1117,7 @@ public class Query extends com.yahoo.processing.Request implements Cloneable {
/**
* Prepares this for binary serialization.
- * <p>
+ *
* This must be invoked after all changes have been made to this query before it is passed
* on to a receiving backend. Calling it is somewhat expensive, so it should only happen once.
* If a prepared query is cloned, it stays prepared.
diff --git a/container-search/src/main/java/com/yahoo/search/cluster/ClusterMonitor.java b/container-search/src/main/java/com/yahoo/search/cluster/ClusterMonitor.java
index 016e22839c1..360dcd38d3b 100644
--- a/container-search/src/main/java/com/yahoo/search/cluster/ClusterMonitor.java
+++ b/container-search/src/main/java/com/yahoo/search/cluster/ClusterMonitor.java
@@ -47,7 +47,7 @@ public class ClusterMonitor<T> {
public ClusterMonitor(NodeManager<T> manager, boolean startPingThread) {
nodeManager = manager;
- monitorThread = new MonitorThread("search.clustermonitor");
+ monitorThread = new MonitorThread("search.clustermonitor." + manager.name());
if (startPingThread) {
monitorThread.start();
}
@@ -81,7 +81,9 @@ public class ClusterMonitor<T> {
/**
* Returns the monitor of the given node, or null if this node has not been added
+ * @deprecated Will be removed in Vespa 8.
*/
+ @Deprecated(forRemoval = true, since = "7.537")
public BaseNodeMonitor<T> getNodeMonitor(T node) {
return nodeMonitors.get(node);
}
@@ -147,6 +149,7 @@ public class ClusterMonitor<T> {
private class MonitorThread extends Thread {
MonitorThread(String name) {
super(name);
+ setDaemon(true);
}
public void run() {
diff --git a/container-search/src/main/java/com/yahoo/search/cluster/ClusterSearcher.java b/container-search/src/main/java/com/yahoo/search/cluster/ClusterSearcher.java
index 51ae2ee432d..097d714b47b 100644
--- a/container-search/src/main/java/com/yahoo/search/cluster/ClusterSearcher.java
+++ b/container-search/src/main/java/com/yahoo/search/cluster/ClusterSearcher.java
@@ -51,7 +51,7 @@ public abstract class ClusterSearcher<T> extends PingableSearcher implements Nod
* @param internal whether or not this cluster is internal (part of the same installation)
*/
public ClusterSearcher(ComponentId id, List<T> connections, boolean internal) {
- this(id, connections, new Hasher<T>(), internal);
+ this(id, connections, new Hasher<>(), internal);
}
public ClusterSearcher(ComponentId id, List<T> connections, Hasher<T> hasher, boolean internal) {
@@ -68,6 +68,9 @@ public abstract class ClusterSearcher<T> extends PingableSearcher implements Nod
}
}
+ @Override
+ public String name() { return getIdString(); }
+
/** Pinging a node, called from ClusterMonitor */
@Override
public final void ping(ClusterMonitor<T> clusterMonitor, T p, Executor executor) {
@@ -112,7 +115,7 @@ public abstract class ClusterSearcher<T> extends PingableSearcher implements Nod
if (k == null) {
b.append("null\n");
} else {
- b.append(k.toString()).append('\n');
+ b.append(k).append('\n');
}
}
traceAsString = b.toString();
@@ -303,7 +306,7 @@ public abstract class ClusterSearcher<T> extends PingableSearcher implements Nod
private class Pinger implements Callable<Pong> {
- private T connection;
+ private final T connection;
public Pinger(T connection) {
this.connection = connection;
diff --git a/container-search/src/main/java/com/yahoo/search/cluster/NodeManager.java b/container-search/src/main/java/com/yahoo/search/cluster/NodeManager.java
index 1a74194d694..db583a65606 100644
--- a/container-search/src/main/java/com/yahoo/search/cluster/NodeManager.java
+++ b/container-search/src/main/java/com/yahoo/search/cluster/NodeManager.java
@@ -11,6 +11,9 @@ import java.util.concurrent.Executor;
*/
public interface NodeManager<T> {
+ /** Name to identify Nodemanager */
+ default String name() { return ""; }
+
/** Called when a failed node is working (ready for production) again */
void working(T node);
diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/Dispatcher.java b/container-search/src/main/java/com/yahoo/search/dispatch/Dispatcher.java
index 3cf2903b88f..e09b1d51733 100644
--- a/container-search/src/main/java/com/yahoo/search/dispatch/Dispatcher.java
+++ b/container-search/src/main/java/com/yahoo/search/dispatch/Dispatcher.java
@@ -46,17 +46,12 @@ import java.util.stream.Collectors;
public class Dispatcher extends AbstractComponent {
public static final String DISPATCH = "dispatch";
- private static final String INTERNAL = "internal";
- private static final String PROTOBUF = "protobuf";
private static final String TOP_K_PROBABILITY = "topKProbability";
private static final String INTERNAL_METRIC = "dispatch_internal";
private static final int MAX_GROUP_SELECTION_ATTEMPTS = 3;
- /** If enabled, search queries will use protobuf rpc */
- public static final CompoundName dispatchProtobuf = CompoundName.fromComponents(DISPATCH, PROTOBUF);
-
/** If set will control computation of how many hits will be fetched from each partition.*/
public static final CompoundName topKProbability = CompoundName.fromComponents(DISPATCH, TOP_K_PROBABILITY);
@@ -79,8 +74,6 @@ public class Dispatcher extends AbstractComponent {
argumentType = new QueryProfileType(DISPATCH);
argumentType.setStrict(true);
argumentType.setBuiltin(true);
- argumentType.addField(new FieldDescription(INTERNAL, FieldType.booleanType));
- argumentType.addField(new FieldDescription(PROTOBUF, FieldType.booleanType));
argumentType.addField(new FieldDescription(TOP_K_PROBABILITY, FieldType.doubleType));
argumentType.freeze();
}
diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/LeanHit.java b/container-search/src/main/java/com/yahoo/search/dispatch/LeanHit.java
index 03b0f092abb..bd0415fa449 100644
--- a/container-search/src/main/java/com/yahoo/search/dispatch/LeanHit.java
+++ b/container-search/src/main/java/com/yahoo/search/dispatch/LeanHit.java
@@ -11,17 +11,17 @@ import java.util.Arrays;
*/
public class LeanHit implements Comparable<LeanHit> {
- private final byte [] gid;
+ private final byte[] gid;
private final double relevance;
- private final byte [] sortData;
+ private final byte[] sortData;
private final int partId;
private final int distributionKey;
private FeatureData matchFeatures;
- public LeanHit(byte [] gid, int partId, int distributionKey, double relevance) {
+ public LeanHit(byte[] gid, int partId, int distributionKey, double relevance) {
this(gid, partId, distributionKey, relevance, null);
}
- public LeanHit(byte [] gid, int partId, int distributionKey, double relevance, byte [] sortData) {
+ public LeanHit(byte[] gid, int partId, int distributionKey, double relevance, byte[] sortData) {
this.gid = gid;
this.relevance = Double.isNaN(relevance) ? Double.NEGATIVE_INFINITY : relevance;
this.sortData = sortData;
@@ -31,8 +31,8 @@ public class LeanHit implements Comparable<LeanHit> {
}
public double getRelevance() { return relevance; }
- public byte [] getGid() { return gid; }
- public byte [] getSortData() { return sortData; }
+ public byte[] getGid() { return gid; }
+ public byte[] getSortData() { return sortData; }
public boolean hasSortData() { return sortData != null; }
public int getPartId() { return partId; }
public int getDistributionKey() { return distributionKey; }
@@ -51,7 +51,7 @@ public class LeanHit implements Comparable<LeanHit> {
return (res != 0) ? res : compareData(gid, o.gid);
}
- private static int compareData(byte [] left, byte [] right) {
+ private static int compareData(byte[] left, byte[] right) {
int i = Arrays.mismatch(left, right);
if (i < 0) {
return 0;
diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/SearchCluster.java b/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/SearchCluster.java
index f90abba330a..36d7e7a85a9 100644
--- a/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/SearchCluster.java
+++ b/container-search/src/main/java/com/yahoo/search/dispatch/searchcluster/SearchCluster.java
@@ -84,6 +84,9 @@ public class SearchCluster implements NodeManager<Node> {
this.localCorpusDispatchTarget = findLocalCorpusDispatchTarget(HostName.getLocalhost(), nodesByHost, groups);
}
+ @Override
+ public String name() { return clusterId; }
+
public void addMonitoring(ClusterMonitor<Node> clusterMonitor) {
for (var group : orderedGroups()) {
for (var node : group.nodes())
diff --git a/container-search/src/main/java/com/yahoo/search/query/Presentation.java b/container-search/src/main/java/com/yahoo/search/query/Presentation.java
index db6b07e0b0d..31641b5c2f0 100644
--- a/container-search/src/main/java/com/yahoo/search/query/Presentation.java
+++ b/container-search/src/main/java/com/yahoo/search/query/Presentation.java
@@ -8,7 +8,9 @@ import com.yahoo.prelude.query.Highlight;
import com.yahoo.prelude.query.IndexedItem;
import com.yahoo.search.Query;
import com.yahoo.search.query.profile.types.FieldDescription;
+import com.yahoo.search.query.profile.types.QueryProfileFieldType;
import com.yahoo.search.query.profile.types.QueryProfileType;
+import com.yahoo.search.query.ranking.MatchPhase;
import com.yahoo.search.rendering.RendererRegistry;
import java.util.ArrayList;
@@ -30,19 +32,26 @@ public class Presentation implements Cloneable {
public static final String TIMING = "timing";
public static final String SUMMARY = "summary";
public static final String SUMMARY_FIELDS = "summaryFields";
+ public static final String TENSORS = "tensors";
/** The (short) name of the parameter holding the name of the return format to use */
public static final String FORMAT = "format";
static {
- argumentType=new QueryProfileType(PRESENTATION);
+ argumentType = new QueryProfileType(PRESENTATION);
argumentType.setStrict(true);
argumentType.setBuiltin(true);
argumentType.addField(new FieldDescription(BOLDING, "boolean", "bolding"));
argumentType.addField(new FieldDescription(TIMING, "boolean", "timing"));
argumentType.addField(new FieldDescription(SUMMARY, "string", "summary"));
- argumentType.addField(new FieldDescription(FORMAT, "string", "format template"));
argumentType.addField(new FieldDescription(SUMMARY_FIELDS, "string", "summaryFields"));
+ QueryProfileType formatArgumentType = new QueryProfileType(FORMAT);
+ formatArgumentType.setBuiltin(true);
+ formatArgumentType.setStrict(true);
+ formatArgumentType.addField(new FieldDescription("", "string", "format template"));
+ formatArgumentType.addField(new FieldDescription(TENSORS, "string", "format.tensors"));
+ formatArgumentType.freeze();
+ argumentType.addField(new FieldDescription(FORMAT, new QueryProfileFieldType(formatArgumentType), "format"));
argumentType.freeze();
}
public static QueryProfileType getArgumentType() { return argumentType; }
@@ -53,7 +62,7 @@ public class Presentation implements Cloneable {
/** The terms to highlight in the result (only used by BoldingSearcher, may be removed later). */
private List<IndexedItem> boldingData = null;
- /** Whether or not to do highlighting */
+ /** Whether to do highlighting */
private boolean bolding = true;
/** The summary class to be shown */
@@ -65,6 +74,9 @@ public class Presentation implements Cloneable {
/** Whether optional timing data should be rendered */
private boolean timing = false;
+ /** Whether to renders tensors in short form */
+ private boolean tensorShortForm = false;
+
/** Set of explicitly requested summary fields, instead of summary classes */
private Set<String> summaryFields = LazySet.newHashSet();
@@ -98,14 +110,10 @@ public class Presentation implements Cloneable {
this.format = (format != null) ? format : RendererRegistry.defaultRendererId.toSpecification();
}
- /**
- * Get the name of the format desired for result rendering.
- */
+ /** Get the name of the format desired for result rendering. */
public String getFormat() { return format.getName(); }
- /**
- * Set the desired format for result rendering. If null, use the default renderer.
- */
+ /** Set the desired format for result rendering. If null, use the default renderer. */
public void setFormat(String format) {
setRenderer(ComponentSpecification.fromString(format));
}
@@ -132,21 +140,7 @@ public class Presentation implements Cloneable {
}
}
- @Override
- public boolean equals(Object o) {
- if ( ! (o instanceof Presentation)) return false;
- Presentation p = (Presentation) o;
- return QueryHelper.equals(bolding, p.bolding) && QueryHelper.equals(summary, p.summary);
- }
-
- @Override
- public int hashCode() {
- return QueryHelper.combineHash(bolding, summary);
- }
-
- /**
- * @return whether to add optional timing data to the rendered result
- */
+ /** Returns whether to add optional timing data to the rendered result. */
public boolean getTiming() {
return timing;
}
@@ -166,20 +160,13 @@ public class Presentation implements Cloneable {
return summaryFields;
}
- /** Prepares this for binary serialization. For internal use - see {@link Query#prepare} */
- public void prepare() {
- if (highlight != null)
- highlight.prepare();
- }
-
/**
* Parse the given string as a comma delimited set of field names and
* overwrite the set of summary fields. Whitespace will be trimmed. If you
* want to add or remove fields programmatically, use
* {@link #getSummaryFields()} and modify the returned set.
*
- * @param asString
- * the summary fields requested, e.g. "price,author,title"
+ * @param asString the summary fields requested, e.g. "price,author,title"
*/
public void setSummaryFields(String asString) {
summaryFields.clear();
@@ -189,5 +176,53 @@ public class Presentation implements Cloneable {
}
+ /**
+ * Returns whether tensors should use short form in JSON and textual representations, see
+ * <a href="https://docs.vespa.ai/en/reference/document-json-format.html#tensor">https://docs.vespa.ai/en/reference/document-json-format.html#tensor</a>
+ * and <a href="https://docs.vespa.ai/en/reference/tensor.html#tensor-literal-form">https://docs.vespa.ai/en/reference/tensor.html#tensor-literal-form</a>.
+ * Default is false.
+ */
+ public boolean getTensorShortForm() { return tensorShortForm; }
+
+ /**
+ * Sets whether tensors should use short form in JSON and textual representations from a string.
+ *
+ * @param value a string which must be either 'short' or 'long'
+ * @throws IllegalArgumentException if any other value is passed
+ */
+ public void setTensorShortForm(String value) {
+ tensorShortForm = toTensorShortForm(value);
+ }
+
+ private boolean toTensorShortForm(String value) {
+ switch (value) {
+ case "short": return true;
+ case "long": return false;
+ default: throw new IllegalArgumentException("Value must be 'long' or 'short', not '" + value + "'");
+ }
+ }
+
+ public void setTensorShortForm(boolean tensorShortForm) {
+ this.tensorShortForm = tensorShortForm;
+ }
+
+ /** Prepares this for binary serialization. For internal use - see {@link Query#prepare} */
+ public void prepare() {
+ if (highlight != null)
+ highlight.prepare();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if ( ! (o instanceof Presentation)) return false;
+ Presentation p = (Presentation) o;
+ return QueryHelper.equals(bolding, p.bolding) && QueryHelper.equals(summary, p.summary);
+ }
+
+ @Override
+ public int hashCode() {
+ return QueryHelper.combineHash(bolding, summary);
+ }
+
}
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfileProperties.java b/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfileProperties.java
index 90d9eab687d..f58395fd5bb 100644
--- a/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfileProperties.java
+++ b/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfileProperties.java
@@ -87,7 +87,6 @@ public class QueryProfileProperties extends Properties {
*/
@Override
public void set(CompoundName name, Object value, Map<String, String> context) {
- // TODO: Refactor
try {
name = unalias(name, context);
@@ -101,42 +100,8 @@ public class QueryProfileProperties extends Properties {
if (runtimeReference != null && ! runtimeReference.getSecond().isOverridable(name.rest(runtimeReference.getFirst().size()), context))
return;
- // Check types
- if ( ! profile.getTypes().isEmpty()) {
- QueryProfileType type;
- QueryProfileType explicitTypeFromField = null;
- for (int i = 0; i < name.size(); i++) {
- if (explicitTypeFromField != null)
- type = explicitTypeFromField;
- else
- type = profile.getType(name.first(i), context);
- if (type == null) continue;
-
- String localName = name.get(i);
- FieldDescription fieldDescription = type.getField(localName);
- if (fieldDescription == null && type.isStrict())
- throw new IllegalInputException("'" + localName + "' is not declared in " + type + ", and the type is strict");
-
- // TODO: In addition to strictness, check legality along the way
-
- if (fieldDescription != null) {
- if (i == name.size() - 1) { // at the end of the path, check the assignment type
- value = fieldDescription.getType().convertFrom(value, new ConversionContext(localName,
- profile.getRegistry(),
- embedder,
- context));
- if (value == null)
- throw new IllegalInputException("'" + value + "' is not a " +
- fieldDescription.getType().toInstanceDescription());
- }
- else if (fieldDescription.getType() instanceof QueryProfileFieldType) {
- // If a type is specified, use that instead of the type implied by the name
- explicitTypeFromField = ((QueryProfileFieldType) fieldDescription.getType()).getQueryProfileType();
- }
- }
-
- }
- }
+ if ( ! profile.getTypes().isEmpty())
+ value = convertByType(name, value, context);
if (value instanceof String && value.toString().startsWith("ref:")) {
if (profile.getRegistry() == null)
@@ -164,6 +129,53 @@ public class QueryProfileProperties extends Properties {
}
}
+ private Object convertByType(CompoundName name, Object value, Map<String, String> context) {
+ QueryProfileType type;
+ QueryProfileType explicitTypeFromField = null;
+ for (int i = 0; i < name.size(); i++) {
+ if (explicitTypeFromField != null)
+ type = explicitTypeFromField;
+ else
+ type = profile.getType(name.first(i), context);
+ if (type == null) continue;
+
+ String localName = name.get(i);
+ FieldDescription fieldDescription = type.getField(localName);
+ if (fieldDescription == null && type.isStrict())
+ throw new IllegalInputException("'" + localName + "' is not declared in " + type + ", and the type is strict");
+ // TODO: In addition to strictness, check legality along the way
+
+ if (fieldDescription != null) {
+ if (i == name.size() - 1) { // at the end of the path, check the assignment type
+ var conversionContext = new ConversionContext(localName, profile.getRegistry(), embedder, context);
+ var convertedValue = fieldDescription.getType().convertFrom(value, conversionContext);
+ if (convertedValue == null
+ && fieldDescription.getType() instanceof QueryProfileFieldType
+ && ((QueryProfileFieldType) fieldDescription.getType()).getQueryProfileType() != null) {
+ // Try the value of the query profile itself instead
+ var queryProfileValueDescription = ((QueryProfileFieldType) fieldDescription.getType()).getQueryProfileType().getField("");
+ if (queryProfileValueDescription != null) {
+ convertedValue = queryProfileValueDescription.getType().convertFrom(value, conversionContext);
+ if (convertedValue == null)
+ throw new IllegalInputException("'" + value + "' is neither a " +
+ fieldDescription.getType().toInstanceDescription() + " nor a " +
+ queryProfileValueDescription.getType().toInstanceDescription());
+ }
+ } else if (convertedValue == null)
+ throw new IllegalInputException("'" + value + "' is not a " +
+ fieldDescription.getType().toInstanceDescription());
+
+ value = convertedValue;
+ } else if (fieldDescription.getType() instanceof QueryProfileFieldType) {
+ // If a type is specified, use that instead of the type implied by the name
+ explicitTypeFromField = ((QueryProfileFieldType) fieldDescription.getType()).getQueryProfileType();
+ }
+ }
+
+ }
+ return value;
+ }
+
@Override
public void clearAll(CompoundName name, Map<String, String> context) {
if (references == null)
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/compiled/CompiledQueryProfileRegistry.java b/container-search/src/main/java/com/yahoo/search/query/profile/compiled/CompiledQueryProfileRegistry.java
index fb1552e549b..39ec3e1c647 100644
--- a/container-search/src/main/java/com/yahoo/search/query/profile/compiled/CompiledQueryProfileRegistry.java
+++ b/container-search/src/main/java/com/yahoo/search/query/profile/compiled/CompiledQueryProfileRegistry.java
@@ -10,6 +10,11 @@ import com.yahoo.search.query.profile.QueryProfileRegistry;
import com.yahoo.search.query.profile.config.QueryProfileConfigurer;
import com.yahoo.search.query.profile.config.QueryProfilesConfig;
import com.yahoo.search.query.profile.types.QueryProfileTypeRegistry;
+import com.yahoo.yolean.UncheckedInterruptedException;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Executor;
+import java.util.concurrent.LinkedBlockingQueue;
/**
* A set of compiled query profiles.
@@ -24,14 +29,47 @@ public class CompiledQueryProfileRegistry extends ComponentRegistry<CompiledQuer
private final QueryProfileTypeRegistry typeRegistry;
@Inject
- public CompiledQueryProfileRegistry(QueryProfilesConfig config) {
+ public CompiledQueryProfileRegistry(QueryProfilesConfig config, Executor executor) {
QueryProfileRegistry registry = QueryProfileConfigurer.createFromConfig(config);
typeRegistry = registry.getTypeRegistry();
- for (QueryProfile inputProfile : registry.allComponents()) {
- register(QueryProfileCompiler.compile(inputProfile, this));
+ int maxConcurrent = 1; // TODO hold this one after Concurrency issue has been found: Math.max(1, (int)(Runtime.getRuntime().availableProcessors() * 0.20));
+ BlockingQueue<CompiledQueryProfile> doneQ = new LinkedBlockingQueue<>();
+ int started = 0;
+ int completed = 0;
+ try {
+ for (QueryProfile inputProfile : registry.allComponents()) {
+ abortIfInterrupted();
+ if (started++ >= maxConcurrent) {
+ register(doneQ.take());
+ completed++;
+ }
+ executor.execute(() -> {
+ Thread self = Thread.currentThread();
+ int prevPriority = self.getPriority();
+ try {
+ self.setPriority(Thread.MIN_PRIORITY);
+ doneQ.add(QueryProfileCompiler.compile(inputProfile, this));
+ } finally {
+ self.setPriority(prevPriority);
+ }
+ });
+ }
+ while (completed < started) {
+ register(doneQ.take());
+ completed++;
+ }
+ } catch (InterruptedException e) {
+ throw new UncheckedInterruptedException("Interrupted while waiting for compiled query profiles", true);
}
}
+ // Query profile construction is very expensive and triggers no operations that automatically throws on interrupt
+ // We need to manually check the interrupt flag in case the container reconfigurer should shut down
+ private void abortIfInterrupted() {
+ if (Thread.interrupted())
+ throw new UncheckedInterruptedException("Interrupted while building query profile registry", true);
+ }
+
/** Creates a compiled query profile registry with no types */
public CompiledQueryProfileRegistry() {
this(QueryProfileTypeRegistry.emptyFrozen());
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/config/QueryProfileConfigurer.java b/container-search/src/main/java/com/yahoo/search/query/profile/config/QueryProfileConfigurer.java
index ceeed9d9167..c3bd4f7b962 100644
--- a/container-search/src/main/java/com/yahoo/search/query/profile/config/QueryProfileConfigurer.java
+++ b/container-search/src/main/java/com/yahoo/search/query/profile/config/QueryProfileConfigurer.java
@@ -19,6 +19,7 @@ import java.util.Set;
/**
* @author bratseth
*/
+@SuppressWarnings("removal") // TODO Vespa 8: remove
public class QueryProfileConfigurer implements ConfigSubscriber.SingleSubscriber<QueryProfilesConfig> {
private final ConfigSubscriber subscriber = new ConfigSubscriber();
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/types/FieldDescription.java b/container-search/src/main/java/com/yahoo/search/query/profile/types/FieldDescription.java
index 1d6f584bfaa..a0f38f61672 100644
--- a/container-search/src/main/java/com/yahoo/search/query/profile/types/FieldDescription.java
+++ b/container-search/src/main/java/com/yahoo/search/query/profile/types/FieldDescription.java
@@ -81,7 +81,7 @@ public class FieldDescription implements Comparable<FieldDescription> {
/**
* Creates a field description
*
- * @param name the name of the field
+ * @param name the name of the field, empty means it describes the value held by the query profile itself
* @param type the type of the field represented as a string - see {@link com.yahoo.search.query.profile.types.FieldType}
* @param aliases a list of aliases, never null. Aliases are not following dotted
* (meaning they are global, not that they cannot contain dots) and are case insensitive.
@@ -89,8 +89,6 @@ public class FieldDescription implements Comparable<FieldDescription> {
* @param overridable whether this can be overridden when first set in a profile. Default: true
*/
public FieldDescription(CompoundName name, FieldType type, List<String> aliases, boolean mandatory, boolean overridable) {
- if (name.isEmpty())
- throw new IllegalArgumentException("Illegal name ''");
for (String nameComponent : name.asList())
QueryProfile.validateName(nameComponent);
this.name = name;
diff --git a/container-search/src/main/java/com/yahoo/search/query/properties/PropertyAliases.java b/container-search/src/main/java/com/yahoo/search/query/properties/PropertyAliases.java
index 87f39b88981..1b036b96e01 100644
--- a/container-search/src/main/java/com/yahoo/search/query/properties/PropertyAliases.java
+++ b/container-search/src/main/java/com/yahoo/search/query/properties/PropertyAliases.java
@@ -38,7 +38,6 @@ public class PropertyAliases extends Properties {
*/
protected CompoundName unalias(CompoundName nameOrAlias) {
if (aliases.isEmpty()) return nameOrAlias;
- if (nameOrAlias.size() > 1) return nameOrAlias; // aliases are simple names
CompoundName properName = aliases.get(nameOrAlias.getLowerCasedName());
return (properName != null) ? properName : nameOrAlias;
}
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 9a09c23b23b..243915662d2 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
@@ -122,12 +122,16 @@ public class QueryProperties extends Properties {
if (key.last().equals(Select.GROUPING)) return query.getSelect().getGroupingString();
}
}
- else if (key.size() == 2 && key.first().equals(Presentation.PRESENTATION)) {
- if (key.last().equals(Presentation.BOLDING)) return query.getPresentation().getBolding();
- if (key.last().equals(Presentation.SUMMARY)) return query.getPresentation().getSummary();
- if (key.last().equals(Presentation.FORMAT)) return query.getPresentation().getFormat();
- if (key.last().equals(Presentation.TIMING)) return query.getPresentation().getTiming();
- if (key.last().equals(Presentation.SUMMARY_FIELDS)) return query.getPresentation().getSummaryFields();
+ else if (key.first().equals(Presentation.PRESENTATION)) {
+ if (key.size() == 2) {
+ if (key.last().equals(Presentation.BOLDING)) return query.getPresentation().getBolding();
+ if (key.last().equals(Presentation.SUMMARY)) return query.getPresentation().getSummary();
+ if (key.last().equals(Presentation.FORMAT)) return query.getPresentation().getFormat();
+ if (key.last().equals(Presentation.TIMING)) return query.getPresentation().getTiming();
+ if (key.last().equals(Presentation.SUMMARY_FIELDS)) return query.getPresentation().getSummaryFields();
+ } else if (key.size() == 3 && key.get(1).equals(Presentation.FORMAT)) {
+ if (key.last().equals(Presentation.TENSORS)) return query.getPresentation().getTensorShortForm();
+ }
}
else if (key.first().equals("rankfeature") || key.first().equals("featureoverride")) { // featureoverride is deprecated
return query.getRanking().getFeatures().getObject(key.rest().toString());
@@ -273,17 +277,27 @@ public class QueryProperties extends Properties {
throwIllegalParameter(key.rest().toString(), Ranking.RANKING);
}
}
- else if (key.size() == 2 && key.first().equals(Presentation.PRESENTATION)) {
- if (key.last().equals(Presentation.BOLDING))
- query.getPresentation().setBolding(asBoolean(value, true));
- else if (key.last().equals(Presentation.SUMMARY))
- query.getPresentation().setSummary(asString(value, ""));
- else if (key.last().equals(Presentation.FORMAT))
- query.getPresentation().setFormat(asString(value,""));
- else if (key.last().equals(Presentation.TIMING))
- query.getPresentation().setTiming(asBoolean(value, true));
- else if (key.last().equals(Presentation.SUMMARY_FIELDS))
- query.getPresentation().setSummaryFields(asString(value,""));
+ else if (key.first().equals(Presentation.PRESENTATION)) {
+ if (key.size() == 2) {
+ if (key.last().equals(Presentation.BOLDING))
+ query.getPresentation().setBolding(asBoolean(value, true));
+ else if (key.last().equals(Presentation.SUMMARY))
+ query.getPresentation().setSummary(asString(value, ""));
+ else if (key.last().equals(Presentation.FORMAT))
+ query.getPresentation().setFormat(asString(value, ""));
+ else if (key.last().equals(Presentation.TIMING))
+ query.getPresentation().setTiming(asBoolean(value, true));
+ else if (key.last().equals(Presentation.SUMMARY_FIELDS))
+ query.getPresentation().setSummaryFields(asString(value, ""));
+ else
+ throwIllegalParameter(key.last(), Presentation.PRESENTATION);
+ }
+ else if (key.size() == 3 && key.get(1).equals(Presentation.FORMAT)) {
+ if (key.last().equals(Presentation.TENSORS))
+ query.getPresentation().setTensorShortForm(asString(value, ""));
+ else
+ throwIllegalParameter(key.last(), Presentation.FORMAT);
+ }
else
throwIllegalParameter(key.last(), Presentation.PRESENTATION);
}
diff --git a/container-search/src/main/java/com/yahoo/search/querytransform/DefaultPositionSearcher.java b/container-search/src/main/java/com/yahoo/search/querytransform/DefaultPositionSearcher.java
index 83daf6398c3..fff8935eefb 100644
--- a/container-search/src/main/java/com/yahoo/search/querytransform/DefaultPositionSearcher.java
+++ b/container-search/src/main/java/com/yahoo/search/querytransform/DefaultPositionSearcher.java
@@ -5,17 +5,20 @@ import static com.yahoo.prelude.searcher.PosSearcher.POSITION_PARSING;
import com.yahoo.component.chain.dependencies.After;
import com.yahoo.component.chain.dependencies.Before;
+import com.yahoo.document.config.DocumentmanagerConfig;
import com.yahoo.prelude.IndexFacts;
import com.yahoo.prelude.Location;
+import com.yahoo.prelude.query.GeoLocationItem;
import com.yahoo.search.Query;
import com.yahoo.search.Searcher;
import com.yahoo.search.searchchain.Execution;
import com.yahoo.search.searchchain.PhaseNames;
+import com.google.inject.Inject;
import java.util.List;
/**
- * If default position has not been set, it will be set here.
+ * If position attribute has not been set, it will be set here.
*
* @author baldersheim
*/
@@ -23,6 +26,17 @@ import java.util.List;
@Before(PhaseNames.TRANSFORMED_QUERY)
public class DefaultPositionSearcher extends Searcher {
+ private final boolean useV8GeoPositions;
+
+ @Inject
+ public DefaultPositionSearcher(DocumentmanagerConfig cfg) {
+ this.useV8GeoPositions = cfg.usev8geopositions();
+ }
+
+ DefaultPositionSearcher() {
+ this.useV8GeoPositions = false;
+ }
+
@Override
public com.yahoo.search.Result search(Query query, Execution execution) {
Location location = query.getRanking().getLocation();
@@ -40,6 +54,12 @@ public class DefaultPositionSearcher extends Searcher {
location.setAttribute(facts.getDefaultPosition(null));
}
}
+ if (useV8GeoPositions && (location != null) && (location.getAttribute() != null)) {
+ var geoLoc = new GeoLocationItem(location);
+ query.getModel().getQueryTree().and(geoLoc);
+ location = null;
+ query.getRanking().setLocation(location);
+ }
return execution.search(query);
}
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 9009fb227da..2ba507a83b8 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
@@ -80,7 +80,6 @@ public class JsonRenderer extends AsynchronousSectionedRenderer<Result> {
private static final CompoundName WRAP_WSETS = new CompoundName("renderer.json.jsonWsets");
private static final CompoundName DEBUG_RENDERING_KEY = new CompoundName("renderer.json.debug");
private static final CompoundName JSON_CALLBACK = new CompoundName("jsoncallback");
- private static final CompoundName TENSOR_FORMAT = new CompoundName("format.tensors");
// if this must be optimized, simply use com.fasterxml.jackson.core.SerializableString
private static final String BUCKET_LIMITS = "limits";
@@ -154,8 +153,7 @@ public class JsonRenderer extends AsynchronousSectionedRenderer<Result> {
// we may need more fine tuning, but for now use the same query parameters here:
this.jsonMapsAll = props.getBoolean(WRAP_DEEP_MAPS, false);
this.jsonWsetsAll = props.getBoolean(WRAP_WSETS, false);
- this.tensorShortForm = (props.get(TENSOR_FORMAT) != null &&
- props.getString(TENSOR_FORMAT).equalsIgnoreCase("short"));
+ this.tensorShortForm = q.getPresentation().getTensorShortForm();
}
}
private final FieldConsumerSettings fieldConsumerSettings = new FieldConsumerSettings();
@@ -166,7 +164,7 @@ public class JsonRenderer extends AsynchronousSectionedRenderer<Result> {
this(null);
}
- /**
+ /**
* Creates a json renderer using a custom executor.
* Using a custom executor is useful for tests to avoid creating new threads for each renderer registry.
*/
@@ -181,8 +179,12 @@ public class JsonRenderer extends AsynchronousSectionedRenderer<Result> {
* method will be invoked when creating the first renderer instance, but not
* for each fresh clone used by individual results.
*
+ * @deprecated Will be removed in Vespa 8. Override the individual render methods of {@link JsonRenderer} to alter
+ * rendering behaviour. Override {@link #createFieldConsumer(boolean)} and sub-class {@link FieldConsumer}
+ * to alter rendering of hit fields.
* @return an object mapper for the internal JsonFactory
*/
+ @Deprecated(forRemoval = true, since = "7") // TODO Vespa 8 make private
protected static ObjectMapper createJsonCodec() {
return new ObjectMapper().disable(FLUSH_AFTER_WRITE_VALUE);
}
@@ -538,6 +540,14 @@ public class JsonRenderer extends AsynchronousSectionedRenderer<Result> {
this.fieldConsumer = generator == null ? null : createFieldConsumer(generator, settings);
}
+ /** Override this method to use a custom {@link FieldConsumer} sub-class to render fields */
+ protected FieldConsumer createFieldConsumer(boolean debugRendering) {
+ fieldConsumerSettings.debugRendering = debugRendering;
+ return createFieldConsumer(generator, fieldConsumerSettings);
+ }
+
+ /** @deprecated Will be removed in Vespa 8. Use {@link #createFieldConsumer(boolean)} instead. */
+ @Deprecated(forRemoval = true, since = "7") // TODO Vespa 8 remove method
protected FieldConsumer createFieldConsumer(JsonGenerator generator, boolean debugRendering) {
fieldConsumerSettings.debugRendering = debugRendering;
return createFieldConsumer(generator, fieldConsumerSettings);
@@ -565,19 +575,33 @@ public class JsonRenderer extends AsynchronousSectionedRenderer<Result> {
private final FieldConsumerSettings settings;
private MutableBoolean hasFieldsField;
+ /** @deprecated Will be removed in Vespa 8. Use {@link #FieldConsumer(boolean, boolean, boolean)} instead. */
+ @Deprecated(forRemoval = true, since = "7") // TODO Vespa 8 Remove
public FieldConsumer(JsonGenerator generator, boolean debugRendering) {
this(generator, debugRendering, false);
}
+
+ /** @deprecated Will be removed in Vespa 8. Use {@link #FieldConsumer(boolean, boolean, boolean)} instead. */
+ @Deprecated(forRemoval = true, since = "7") // TODO Vespa 8 Remove
public FieldConsumer(JsonGenerator generator, boolean debugRendering, boolean tensorShortForm) {
this(generator, debugRendering, tensorShortForm, false);
}
- public FieldConsumer(JsonGenerator generator, boolean debugRendering, boolean tensorShortForm, boolean jsonDeepMaps) {
+
+ /** Invoke this from your constructor when sub-classing {@link FieldConsumer} */
+ protected FieldConsumer(boolean debugRendering, boolean tensorShortForm, boolean jsonMaps) {
+ this(null, debugRendering, tensorShortForm, jsonMaps);
+ }
+
+ /** @deprecated Will be removed in Vespa 8. Use {@link #FieldConsumer(boolean, boolean, boolean)} instead. */
+ @Deprecated(forRemoval = true, since = "7") // TODO Vespa 8 remove
+ public FieldConsumer(JsonGenerator generator, boolean debugRendering, boolean tensorShortForm, boolean jsonMaps) {
this.generator = generator;
this.settings = new FieldConsumerSettings();
this.settings.debugRendering = debugRendering;
this.settings.tensorShortForm = tensorShortForm;
- this.settings.jsonDeepMaps = jsonDeepMaps;
+ this.settings.jsonDeepMaps = jsonMaps;
}
+
FieldConsumer(JsonGenerator generator, FieldConsumerSettings settings) {
this.generator = generator;
this.settings = settings;
@@ -594,14 +618,14 @@ public class JsonRenderer extends AsynchronousSectionedRenderer<Result> {
/** Call before rendering a field to the generator */
void ensureFieldsField() throws IOException {
if (hasFieldsField.get()) return;
- generator.writeObjectFieldStart(FIELDS);
+ generator().writeObjectFieldStart(FIELDS);
hasFieldsField.set(true);
}
/** Call after all fields in a hit to close the "fields" field of the JSON object */
void endHitFields() throws IOException {
if ( ! hasFieldsField.get()) return;
- generator.writeEndObject();
+ generator().writeEndObject();
this.hasFieldsField = null;
}
@@ -610,7 +634,7 @@ public class JsonRenderer extends AsynchronousSectionedRenderer<Result> {
try {
if (shouldRender(name, value)) {
ensureFieldsField();
- generator.writeFieldName(name);
+ generator().writeFieldName(name);
renderFieldContents(value);
}
}
@@ -624,8 +648,8 @@ public class JsonRenderer extends AsynchronousSectionedRenderer<Result> {
try {
if (shouldRenderUtf8Value(name, length)) {
ensureFieldsField();
- generator.writeFieldName(name);
- generator.writeUTF8String(utf8Data, offset, length);
+ generator().writeFieldName(name);
+ generator().writeUTF8String(utf8Data, offset, length);
}
}
catch (IOException e) {
@@ -779,7 +803,7 @@ public class JsonRenderer extends AsynchronousSectionedRenderer<Result> {
private void renderInspectorDirect(Inspector data) throws IOException {
StringBuilder intermediate = new StringBuilder();
JsonRender.render(data, intermediate, true);
- generator.writeRawValue(intermediate.toString());
+ generator().writeRawValue(intermediate.toString());
}
protected void renderFieldContents(Object field) throws IOException {
@@ -793,68 +817,75 @@ public class JsonRenderer extends AsynchronousSectionedRenderer<Result> {
@Override
public void accept(Object field) throws IOException {
if (field == null) {
- generator.writeNull();
+ generator().writeNull();
} else if (field instanceof Boolean) {
- generator.writeBoolean((Boolean)field);
+ generator().writeBoolean((Boolean)field);
} else if (field instanceof Number) {
renderNumberField((Number) field);
} else if (field instanceof TreeNode) {
- generator.writeTree((TreeNode) field);
+ generator().writeTree((TreeNode) field);
} else if (field instanceof Tensor) {
renderTensor(Optional.of((Tensor)field));
} else if (field instanceof FeatureData) {
- generator.writeRawValue(((FeatureData)field).toJson(settings.tensorShortForm));
+ generator().writeRawValue(((FeatureData)field).toJson(settings.tensorShortForm));
} else if (field instanceof Inspectable) {
renderInspectorDirect(((Inspectable)field).inspect());
} else if (field instanceof JsonProducer) {
- generator.writeRawValue(((JsonProducer) field).toJson());
+ generator().writeRawValue(((JsonProducer) field).toJson());
} else if (field instanceof StringFieldValue) {
- generator.writeString(((StringFieldValue)field).getString());
+ generator().writeString(((StringFieldValue)field).getString());
} else if (field instanceof TensorFieldValue) {
renderTensor(((TensorFieldValue)field).getTensor());
} else if (field instanceof FieldValue) {
// the null below is the field which has already been written
((FieldValue) field).serialize(null, new JsonWriter(generator));
} else {
- generator.writeString(field.toString());
+ generator().writeString(field.toString());
}
}
private void renderNumberField(Number field) throws IOException {
if (field instanceof Integer) {
- generator.writeNumber(field.intValue());
+ generator().writeNumber(field.intValue());
} else if (field instanceof Float) {
- generator.writeNumber(field.floatValue());
+ generator().writeNumber(field.floatValue());
} else if (field instanceof Double) {
- generator.writeNumber(field.doubleValue());
+ generator().writeNumber(field.doubleValue());
} else if (field instanceof Long) {
- generator.writeNumber(field.longValue());
+ generator().writeNumber(field.longValue());
} else if (field instanceof Byte || field instanceof Short) {
- generator.writeNumber(field.intValue());
+ generator().writeNumber(field.intValue());
} else if (field instanceof BigInteger) {
- generator.writeNumber((BigInteger) field);
+ generator().writeNumber((BigInteger) field);
} else if (field instanceof BigDecimal) {
- generator.writeNumber((BigDecimal) field);
+ generator().writeNumber((BigDecimal) field);
} else {
- generator.writeNumber(field.doubleValue());
+ generator().writeNumber(field.doubleValue());
}
}
private void renderTensor(Optional<Tensor> tensor) throws IOException {
if (tensor.isEmpty()) {
- generator.writeStartObject();
- generator.writeArrayFieldStart("cells");
- generator.writeEndArray();
- generator.writeEndObject();
+ generator().writeStartObject();
+ generator().writeArrayFieldStart("cells");
+ generator().writeEndArray();
+ generator().writeEndObject();
return;
}
if (settings.tensorShortForm) {
- generator.writeRawValue(new String(JsonFormat.encodeShortForm(tensor.get()), StandardCharsets.UTF_8));
+ generator().writeRawValue(new String(JsonFormat.encodeShortForm(tensor.get()), StandardCharsets.UTF_8));
} else {
- generator.writeRawValue(new String(JsonFormat.encode(tensor.get()), StandardCharsets.UTF_8));
+ generator().writeRawValue(new String(JsonFormat.encode(tensor.get()), StandardCharsets.UTF_8));
}
}
+ private JsonGenerator generator() {
+ if (generator == null)
+ throw new UnsupportedOperationException("Generator required but not assigned. " +
+ "All accept() methods must be overridden when sub-classing FieldConsumer");
+ return generator;
+ }
+
}
}
diff --git a/container-search/src/main/java/com/yahoo/search/yql/YqlParser.java b/container-search/src/main/java/com/yahoo/search/yql/YqlParser.java
index c0c5b0ee0b0..846f70fd26b 100644
--- a/container-search/src/main/java/com/yahoo/search/yql/YqlParser.java
+++ b/container-search/src/main/java/com/yahoo/search/yql/YqlParser.java
@@ -400,7 +400,7 @@ public class YqlParser implements Parser {
case NON_EMPTY:
return ensureNonEmpty(ast);
default:
- throw newUnexpectedArgumentException(names.get(0), DOT_PRODUCT, NEAREST_NEIGHBOR,
+ throw newUnexpectedArgumentException(names.get(0), DOT_PRODUCT, GEO_LOCATION, NEAREST_NEIGHBOR,
RANGE, RANK, USER_QUERY, WAND, WEAK_AND, WEIGHTED_SET,
PREDICATE, USER_INPUT, NON_EMPTY);
}
diff --git a/container-search/src/main/java/com/yahoo/vespa/streamingvisitors/MetricsSearcher.java b/container-search/src/main/java/com/yahoo/vespa/streamingvisitors/MetricsSearcher.java
index ab9da8ccee5..f20fc85b448 100644
--- a/container-search/src/main/java/com/yahoo/vespa/streamingvisitors/MetricsSearcher.java
+++ b/container-search/src/main/java/com/yahoo/vespa/streamingvisitors/MetricsSearcher.java
@@ -20,6 +20,7 @@ import static com.yahoo.vespa.streamingvisitors.VdsStreamingSearcher.STREAMING_S
/**
* Generates mail-specific query metrics.
*/
+@SuppressWarnings("removal") // TODO Vespa 8: remove (com.yahoo.log.event)
public class MetricsSearcher extends Searcher {
private static final CompoundName metricsearcherId=new CompoundName("metricsearcher.id");
diff --git a/container-search/src/main/java/com/yahoo/vespa/streamingvisitors/QueryEncoder.java b/container-search/src/main/java/com/yahoo/vespa/streamingvisitors/QueryEncoder.java
new file mode 100644
index 00000000000..e252a230d4f
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/vespa/streamingvisitors/QueryEncoder.java
@@ -0,0 +1,90 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.streamingvisitors;
+
+import com.yahoo.fs4.MapEncoder;
+import com.yahoo.prelude.fastsearch.DocumentDatabase;
+import com.yahoo.prelude.query.Highlight;
+import com.yahoo.search.Query;
+import com.yahoo.search.dispatch.rpc.ProtobufSerialization;
+
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Encodes the query in binary form.
+ *
+ * @author bratseth
+ */
+class QueryEncoder {
+
+ /**
+ * Encodes properties of this query.
+ *
+ * @param buffer the buffer to encode to
+ * @param encodeQueryData true to encode all properties, false to only include session information, not actual query data
+ * @return the encoded length
+ */
+ static int encodeAsProperties(Query query, ByteBuffer buffer, boolean encodeQueryData) {
+ // Make sure we don't encode anything here if we have turned the property feature off
+ // Due to sendQuery we sometimes end up turning this feature on and then encoding a 0 int as the number of
+ // property maps - that's ok (probably we should simplify by just always turning the feature on)
+ if (! hasEncodableProperties(query)) return 0;
+
+ int start = buffer.position();
+ int mapCountPosition = buffer.position();
+ buffer.putInt(0); // map count will go here
+ int mapCount = 0;
+ mapCount += query.getRanking().getProperties().encode(buffer, encodeQueryData);
+ if (encodeQueryData) {
+ mapCount += query.getRanking().getFeatures().encode(buffer);
+ if (query.getPresentation().getHighlight() != null) {
+ mapCount += MapEncoder.encodeMultiMap(Highlight.HIGHLIGHTTERMS,
+ query.getPresentation().getHighlight().getHighlightTerms(), buffer);
+ }
+ mapCount += MapEncoder.encodeMap("model", createModelMap(query), buffer);
+ }
+ mapCount += MapEncoder.encodeSingleValue(DocumentDatabase.MATCH_PROPERTY, DocumentDatabase.SEARCH_DOC_TYPE_KEY,
+ query.getModel().getDocumentDb(), buffer);
+ mapCount += MapEncoder.encodeMap("caches", createCacheSettingMap(query), buffer);
+ buffer.putInt(mapCountPosition, mapCount);
+ return buffer.position() - start;
+ }
+
+ static boolean hasEncodableProperties(Query query) {
+ if ( ! query.getRanking().getProperties().isEmpty()) return true;
+ if ( ! query.getRanking().getFeatures().isEmpty()) return true;
+ if ( query.getRanking().getFreshness() != null) return true;
+ if ( query.getModel().getSearchPath() != null) return true;
+ if ( query.getModel().getDocumentDb() != null) return true;
+ if ( query.getPresentation().getHighlight() != null &&
+ ! query.getPresentation().getHighlight().getHighlightItems().isEmpty()) return true;
+ return false;
+ }
+
+ private static Map<String, Boolean> createCacheSettingMap(Query query) {
+ if (query.getGroupingSessionCache() && query.getRanking().getQueryCache()) {
+ Map<String, Boolean> cacheSettingMap = new HashMap<>();
+ cacheSettingMap.put("grouping", true);
+ cacheSettingMap.put("query", true);
+ return cacheSettingMap;
+ }
+ if (query.getGroupingSessionCache())
+ return Collections.singletonMap("grouping", true);
+ if (query.getRanking().getQueryCache())
+ return Collections.singletonMap("query", true);
+ return Collections.emptyMap();
+ }
+
+ private static Map<String, String> createModelMap(Query query) {
+ Map<String, String> m = new HashMap<>();
+ if (query.getModel().getSearchPath() != null) m.put("searchpath", query.getModel().getSearchPath());
+
+ int traceLevel = ProtobufSerialization.getTraceLevelForBackend(query);
+ if (traceLevel > 0) m.put("tracelevel", String.valueOf(traceLevel));
+
+ return m;
+ }
+
+}
diff --git a/container-search/src/main/java/com/yahoo/vespa/streamingvisitors/VdsVisitor.java b/container-search/src/main/java/com/yahoo/vespa/streamingvisitors/VdsVisitor.java
index e2233d51ae4..b2e4821f164 100644
--- a/container-search/src/main/java/com/yahoo/vespa/streamingvisitors/VdsVisitor.java
+++ b/container-search/src/main/java/com/yahoo/vespa/streamingvisitors/VdsVisitor.java
@@ -3,13 +3,10 @@ package com.yahoo.vespa.streamingvisitors;
import com.yahoo.document.select.parser.ParseException;
import com.yahoo.documentapi.AckToken;
-import com.yahoo.documentapi.DocumentAccess;
import com.yahoo.documentapi.VisitorControlHandler;
import com.yahoo.documentapi.VisitorDataHandler;
import com.yahoo.documentapi.VisitorParameters;
import com.yahoo.documentapi.VisitorSession;
-import com.yahoo.documentapi.messagebus.MessageBusDocumentAccess;
-import com.yahoo.documentapi.messagebus.MessageBusParams;
import com.yahoo.documentapi.messagebus.loadtypes.LoadType;
import com.yahoo.documentapi.messagebus.loadtypes.LoadTypeSet;
import com.yahoo.documentapi.messagebus.protocol.DocumentProtocol;
@@ -41,7 +38,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Logger;
/**
@@ -187,7 +183,7 @@ class VdsVisitor extends VisitorDataHandler implements Visitor {
params.setLibraryParameter("location", af);
}
- if (query.hasEncodableProperties()) {
+ if (QueryEncoder.hasEncodableProperties(query)) {
encodeQueryData(query, 1, ed);
params.setLibraryParameter("rankproperties", ed.getEncodedData());
}
@@ -254,7 +250,7 @@ class VdsVisitor extends VisitorDataHandler implements Visitor {
ed.setReturned(query.getModel().getQueryTree().getRoot().encode(buf));
break;
case 1:
- ed.setReturned(query.encodeAsProperties(buf, true));
+ ed.setReturned(QueryEncoder.encodeAsProperties(query, buf, true));
break;
case 2:
throw new IllegalArgumentException("old aggregation no longer exists!");
diff --git a/container-search/src/test/java/com/yahoo/prelude/searcher/test/QuotingSearcherTestCase.java b/container-search/src/test/java/com/yahoo/prelude/searcher/test/QuotingSearcherTestCase.java
index 2a7e9da2992..ef3526f2fb1 100644
--- a/container-search/src/test/java/com/yahoo/prelude/searcher/test/QuotingSearcherTestCase.java
+++ b/container-search/src/test/java/com/yahoo/prelude/searcher/test/QuotingSearcherTestCase.java
@@ -32,7 +32,6 @@ import static org.junit.Assert.assertTrue;
*
* @author Steinar Knutsen
*/
-@SuppressWarnings("deprecation")
public class QuotingSearcherTestCase {
public static QuotingSearcher createQuotingSearcher(String configId) {
diff --git a/container-search/src/test/java/com/yahoo/search/cluster/test/ClusteredConnectionTestCase.java b/container-search/src/test/java/com/yahoo/search/cluster/test/ClusteredConnectionTestCase.java
index 1e35b6447ff..fa8d237aee0 100644
--- a/container-search/src/test/java/com/yahoo/search/cluster/test/ClusteredConnectionTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/cluster/test/ClusteredConnectionTestCase.java
@@ -133,7 +133,7 @@ public class ClusteredConnectionTestCase {
/** Represents a connection, e.g over http, in this test */
private static class Connection {
- private String id;
+ private final String id;
private boolean inService = true;
diff --git a/container-search/src/test/java/com/yahoo/search/query/profile/compiled/CompiledQueryProfileRegistryTest.java b/container-search/src/test/java/com/yahoo/search/query/profile/compiled/CompiledQueryProfileRegistryTest.java
index a9792049fb7..511921ac047 100644
--- a/container-search/src/test/java/com/yahoo/search/query/profile/compiled/CompiledQueryProfileRegistryTest.java
+++ b/container-search/src/test/java/com/yahoo/search/query/profile/compiled/CompiledQueryProfileRegistryTest.java
@@ -4,7 +4,10 @@ package com.yahoo.search.query.profile.compiled;
import com.yahoo.search.query.profile.config.QueryProfilesConfig;
import org.junit.Test;
+import java.util.concurrent.Executors;
+
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
/**
* @author gjoranv
@@ -21,7 +24,9 @@ public class CompiledQueryProfileRegistryTest {
.value("5")))
.build();
- var registry = new CompiledQueryProfileRegistry(config);
+ var executor = Executors.newCachedThreadPool();
+ var registry = new CompiledQueryProfileRegistry(config, executor);
+ assertTrue(executor.shutdownNow().isEmpty());
var profile1 = registry.findQueryProfile("profile1");
assertEquals("5", profile1.get("hits"));
}
diff --git a/container-search/src/test/java/com/yahoo/search/query/profile/test/DumpToolTestCase.java b/container-search/src/test/java/com/yahoo/search/query/profile/test/DumpToolTestCase.java
index 60b6c6dbe84..3ed044601f0 100644
--- a/container-search/src/test/java/com/yahoo/search/query/profile/test/DumpToolTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/query/profile/test/DumpToolTestCase.java
@@ -11,7 +11,7 @@ import static org.junit.Assert.assertTrue;
*/
public class DumpToolTestCase {
- private String profileDir = "src/test/java/com/yahoo/search/query/profile/config/test/multiprofile";
+ private final String profileDir = "src/test/java/com/yahoo/search/query/profile/config/test/multiprofile";
@Test
public void testNoParameters() {
@@ -25,12 +25,13 @@ public class DumpToolTestCase {
@Test
public void testNoDimensionValues() {
- assertTrue(new DumpTool().resolveAndDump("multiprofile1", profileDir).startsWith("a=general-a\n"));
+ System.out.println(new DumpTool().resolveAndDump("multiprofile1", profileDir));
+ assertTrue(new DumpTool().resolveAndDump("multiprofile1", profileDir).contains("a=general-a\n"));
}
@Test
public void testAllParametersSet() {
- assertTrue(new DumpTool().resolveAndDump("multiprofile1", profileDir, "").startsWith("a=general-a\n"));
+ assertTrue(new DumpTool().resolveAndDump("multiprofile1", profileDir, "").contains("a=general-a\n"));
}
@Test
diff --git a/container-search/src/test/java/com/yahoo/search/query/profile/types/test/NameTestCase.java b/container-search/src/test/java/com/yahoo/search/query/profile/types/test/NameTestCase.java
index 4b8edfdde73..fa7508ea5ac 100644
--- a/container-search/src/test/java/com/yahoo/search/query/profile/types/test/NameTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/query/profile/types/test/NameTestCase.java
@@ -47,7 +47,6 @@ public class NameTestCase {
assertLegalFieldName("a/b");
assertLegalFieldName("/a/b");
assertLegalFieldName("/a/b/");
- assertIllegalFieldName("");
assertIllegalFieldName("aBc.dooEee.ce_d.-some-other.moreHere",
"Could not set 'aBc.dooEee.ce_d.-some-other.moreHere' to 'anyValue'",
"Illegal name '-some-other'");
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 2a7e9aa0822..637666a6a19 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
@@ -54,6 +54,7 @@ import com.yahoo.tensor.Tensor;
import com.yahoo.tensor.TensorType;
import com.yahoo.tensor.serialization.TypedBinaryFormat;
import com.yahoo.text.Utf8;
+import com.yahoo.yolean.Exceptions;
import com.yahoo.yolean.trace.TraceNode;
import org.junit.Before;
import org.junit.Test;
@@ -69,6 +70,7 @@ import java.util.concurrent.ExecutionException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
/**
* Functional testing of {@link JsonRenderer}.
@@ -165,11 +167,26 @@ public class JsonRendererTestCase {
h.setField("tensor_mixed", new TensorFieldValue(Tensor.from("tensor(x{},y[2]):{a:[1,2], b:[3,4]}")));
h.setField("summaryfeatures", summaryFeatures);
- Result r = new Result(new Query("/?format.tensors=short"));
- r.hits().add(h);
- r.setTotalHitCount(1L);
- String summary = render(r);
- assertEqualJson(expected, summary);
+ Result result1 = new Result(new Query("/?presentation.format.tensors=short"));
+ result1.hits().add(h);
+ result1.setTotalHitCount(1L);
+ String summary1 = render(result1);
+ assertEqualJson(expected, summary1);
+
+ Result result2 = new Result(new Query("/?format.tensors=short"));
+ result2.hits().add(h);
+ result2.setTotalHitCount(1L);
+ String summary2 = render(result2);
+ assertEqualJson(expected, summary2);
+
+ try {
+ render(new Result(new Query("/?presentation.format.tensors=unknown")));
+ fail("Expected exception");
+ }
+ catch (IllegalArgumentException e) {
+ assertEquals("Could not set 'presentation.format.tensors' to 'unknown': Value must be 'long' or 'short', not 'unknown'",
+ Exceptions.toMessageString(e));
+ }
}
@Test
diff --git a/container-search/src/test/java/com/yahoo/search/searchers/ValidateNearestNeighborTestCase.java b/container-search/src/test/java/com/yahoo/search/searchers/ValidateNearestNeighborTestCase.java
index 14fdd047391..9a760bfb0cb 100644
--- a/container-search/src/test/java/com/yahoo/search/searchers/ValidateNearestNeighborTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/searchers/ValidateNearestNeighborTestCase.java
@@ -2,6 +2,7 @@
package com.yahoo.search.searchers;
import com.yahoo.config.subscription.ConfigGetter;
+import com.yahoo.config.subscription.FileSource;
import com.yahoo.config.subscription.RawSource;
import com.yahoo.prelude.IndexFacts;
import com.yahoo.prelude.IndexModel;
@@ -18,7 +19,11 @@ import com.yahoo.tensor.Tensor;
import com.yahoo.tensor.TensorType;
import com.yahoo.vespa.config.search.AttributesConfig;
+import com.yahoo.vespa.config.search.RankProfilesConfig;
import org.junit.Test;
+
+import java.io.File;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -32,8 +37,8 @@ public class ValidateNearestNeighborTestCase {
public ValidateNearestNeighborTestCase() {
searcher = new ValidateNearestNeighborSearcher(
ConfigGetter.getConfig(AttributesConfig.class,
- "raw:",
- new RawSource("attribute[5]\n" +
+ "raw:" +
+ "attribute[5]\n" +
"attribute[0].name simple\n" +
"attribute[0].datatype INT32\n" +
"attribute[1].name dvector\n" +
@@ -57,7 +62,7 @@ public class ValidateNearestNeighborTestCase {
"attribute[7].name threetypes\n" +
"attribute[7].datatype TENSOR\n" +
"attribute[7].tensortype tensor(x{})\n"
- )));
+ ));
}
private static TensorType tt_dense_dvector_42 = TensorType.fromSpec("tensor(x[42])");
diff --git a/container-search/src/test/java/com/yahoo/search/searchers/test/ValidateMatchPhaseSearcherTestCase.java b/container-search/src/test/java/com/yahoo/search/searchers/test/ValidateMatchPhaseSearcherTestCase.java
index 86ffb1f9830..01e360858c1 100644
--- a/container-search/src/test/java/com/yahoo/search/searchers/test/ValidateMatchPhaseSearcherTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/searchers/test/ValidateMatchPhaseSearcherTestCase.java
@@ -27,8 +27,8 @@ public class ValidateMatchPhaseSearcherTestCase {
public ValidateMatchPhaseSearcherTestCase() {
searcher = new ValidateMatchPhaseSearcher(
ConfigGetter.getConfig(AttributesConfig.class,
- "raw:",
- new RawSource("attribute[4]\n" +
+ "raw:" +
+ "attribute[4]\n" +
"attribute[0].name ok\n" +
"attribute[0].datatype INT32\n" +
"attribute[0].collectiontype SINGLE\n" +
@@ -45,7 +45,7 @@ public class ValidateMatchPhaseSearcherTestCase {
"attribute[3].datatype INT32\n" +
"attribute[3].collectiontype ARRAY\n" +
"attribute[3].fastsearch true"
- )));
+ ));
}
private static String getErrorMatch(String attribute) {
diff --git a/container-search/src/test/java/com/yahoo/search/test/QueryTestCase.java b/container-search/src/test/java/com/yahoo/search/test/QueryTestCase.java
index f6273fdf723..a4fd37145c8 100644
--- a/container-search/src/test/java/com/yahoo/search/test/QueryTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/test/QueryTestCase.java
@@ -2,6 +2,7 @@
package com.yahoo.search.test;
import com.yahoo.component.chain.Chain;
+import com.yahoo.data.JsonProducer;
import com.yahoo.language.Language;
import com.yahoo.language.Linguistics;
import com.yahoo.language.detect.Detection;
@@ -15,6 +16,7 @@ import com.yahoo.prelude.Index;
import com.yahoo.prelude.IndexFacts;
import com.yahoo.prelude.IndexModel;
import com.yahoo.prelude.SearchDefinition;
+import com.yahoo.prelude.fastsearch.FastHit;
import com.yahoo.prelude.query.AndItem;
import com.yahoo.prelude.query.AndSegmentItem;
import com.yahoo.prelude.query.CompositeItem;
@@ -36,10 +38,12 @@ import com.yahoo.search.query.profile.DimensionValues;
import com.yahoo.search.query.profile.QueryProfile;
import com.yahoo.search.query.profile.QueryProfileRegistry;
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.result.Hit;
import com.yahoo.search.searchchain.Execution;
import com.yahoo.yolean.Exceptions;
+import org.json.JSONObject;
import org.junit.Ignore;
import org.junit.Test;
@@ -69,6 +73,17 @@ import static org.junit.Assert.fail;
public class QueryTestCase {
@Test
+ public void testIt() throws Exception {
+ JSONObject newroot = new JSONObject("{\"key\": 3}");
+ var hit = new FastHit();
+ hit.setField("data", (JsonProducer)s -> s.append(newroot));
+ var field = hit.getField("data");
+ if (field instanceof JsonProducer) {
+ System.out.println((((JsonProducer) field).toJson()));
+ }
+ }
+
+ @Test
public void testSimpleFunctionality() {
Query q = new Query(QueryTestCase.httpEncode("/sdfsd.html?query=this is a simple query&aParameter"));
assertEquals("this is a simple query", q.getModel().getQueryString());
@@ -1078,6 +1093,49 @@ public class QueryTestCase {
query.getSelect().getGrouping().toString());
}
+ /**
+ * Tests that the value presentation.format.tensors can be set in a query profile.
+ * This is special because presentation.format is a native query profile.
+ */
+ @Test
+ public void testSettingNativeQueryProfileValueInQueryProfile() {
+ {
+ QueryProfileRegistry registry = new QueryProfileRegistry();
+ QueryProfile profile = new QueryProfile("default");
+ profile.set("presentation.format.tensors", "short", Map.of(), registry);
+ registry.register(profile);
+ CompiledQueryProfileRegistry cRegistry = registry.compile();
+ Query query = new Query("?query=foo", cRegistry.findQueryProfile("default"));
+ assertTrue(query.getPresentation().getTensorShortForm());
+ }
+
+ { // Same as above but also set presentation.format
+ QueryProfileRegistry registry = new QueryProfileRegistry();
+ QueryProfile profile = new QueryProfile("default");
+ profile.set("presentation.format", "xml", Map.of(), registry);
+ profile.set("presentation.format.tensors", "short", Map.of(), registry);
+ registry.register(profile);
+ CompiledQueryProfileRegistry cRegistry = registry.compile();
+ Query query = new Query("?query=foo", cRegistry.findQueryProfile("default"));
+ assertEquals("xml", query.getPresentation().getFormat());
+ assertTrue(query.getPresentation().getTensorShortForm());
+ }
+
+ { // Set presentation.format with a typed query profile type
+ QueryProfileRegistry registry = new QueryProfileRegistry();
+ QueryProfileType type = new QueryProfileType("mytype");
+ type.inherited().add(registry.getType("native"));
+ registry.getTypeRegistry().register(type);
+ type.addField(new FieldDescription("ranking.features.query(embedding)", "tensor(x[5])"),
+ registry.getTypeRegistry());
+ QueryProfile profile = new QueryProfile("default");
+ profile.setType(type);
+ registry.register(profile);
+ CompiledQueryProfileRegistry cRegistry = registry.compile();
+ Query query = new Query("?query=foo&presentation.format=xml", cRegistry.findQueryProfile("default"));
+ }
+ }
+
private void assertDetectionText(String expectedDetectionText, String queryString, String ... indexSpecs) {
Query q = new Query(httpEncode("/?query=" + queryString));
SearchDefinition sd = new SearchDefinition("testSearchDefinition");
diff --git a/container-search/src/test/java/com/yahoo/vespa/streamingvisitors/VdsVisitorTestCase.java b/container-search/src/test/java/com/yahoo/vespa/streamingvisitors/VdsVisitorTestCase.java
index cc70f46fca1..1d07cafeda9 100644
--- a/container-search/src/test/java/com/yahoo/vespa/streamingvisitors/VdsVisitorTestCase.java
+++ b/container-search/src/test/java/com/yahoo/vespa/streamingvisitors/VdsVisitorTestCase.java
@@ -243,7 +243,6 @@ public class VdsVisitorTestCase {
assertEquals(DocumentProtocol.Priority.VERY_HIGH, params.getPriority());
}
}
- assertEquals(-1, params.getMaxFirstPassHits());
if (qa.maxBucketsPerVisitor != 0) {
assertEquals(qa.maxBucketsPerVisitor, params.getMaxBucketsPerVisitor());
} else {