diff options
author | Arne H Juul <arnej@yahooinc.com> | 2022-02-11 15:04:09 +0000 |
---|---|---|
committer | Arne H Juul <arnej@yahooinc.com> | 2022-02-11 18:26:39 +0000 |
commit | 9ea871e3958ee387c6c38023a1b5ca770cc20c13 (patch) | |
tree | 9d6cb559bf0a6e06057f3ce09d7c4482feead8dd /container-search | |
parent | fc8d4006be96b568fe4a7c57fbb36db3e5f58a2a (diff) | |
parent | 0be7fcf7bf80995a2bbcf19bb88860c13b3f55e5 (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')
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 { |