diff options
21 files changed, 247 insertions, 111 deletions
diff --git a/component/abi-spec.json b/component/abi-spec.json index a6b6a558db6..cd42b8ff95a 100644 --- a/component/abi-spec.json +++ b/component/abi-spec.json @@ -73,6 +73,7 @@ "public static com.yahoo.component.ComponentId fromString(java.lang.String)", "public java.lang.String toFileName()", "public static com.yahoo.component.ComponentId fromFileName(java.lang.String)", + "public static void resetGlobalCountersForTests()", "public bridge synthetic int compareTo(java.lang.Object)" ], "fields": [] diff --git a/component/src/main/java/com/yahoo/component/ComponentId.java b/component/src/main/java/com/yahoo/component/ComponentId.java index 05c710e3fc1..4613be09543 100644 --- a/component/src/main/java/com/yahoo/component/ComponentId.java +++ b/component/src/main/java/com/yahoo/component/ComponentId.java @@ -32,15 +32,15 @@ public final class ComponentId implements Comparable<ComponentId> { private int count = 0; public int getAndIncrement() { return count++; } } - private static ThreadLocal<Counter> gid = new ThreadLocal<Counter>() { + private static ThreadLocal<Counter> threadLocalUniqueId = new ThreadLocal<Counter>() { @Override protected Counter initialValue() { return new Counter(); } }; - private static AtomicInteger uniqueTid = new AtomicInteger(0); - private static ThreadLocal<String> tid = new ThreadLocal<String>() { + private static AtomicInteger threadIdCounter = new AtomicInteger(0); + private static ThreadLocal<String> threadId = new ThreadLocal<String>() { @Override protected String initialValue() { - return new String("_"+uniqueTid.getAndIncrement()+"_"); + return new String("_" + threadIdCounter.getAndIncrement() + "_"); } }; @@ -58,7 +58,7 @@ public final class ComponentId implements Comparable<ComponentId> { } private String createAnonymousName(String name) { - return new StringBuilder(name).append(tid.get()).append(gid.get().getAndIncrement()).toString(); + return new StringBuilder(name).append(threadId.get()).append(threadLocalUniqueId.get().getAndIncrement()).toString(); } public ComponentId(String name, Version version, ComponentId namespace) { @@ -148,10 +148,7 @@ public final class ComponentId implements Comparable<ComponentId> { return spec.compareTo(other.spec); } - /** - * Creates a componentId that is unique for this run-time instance - */ - // TODO: Check if we really need this. -JB + /** Creates a componentId that is unique for this run-time instance */ public static ComponentId createAnonymousComponentId(String baseName) { return new ComponentId(baseName, null, null, true); } @@ -196,27 +193,27 @@ public final class ComponentId implements Comparable<ComponentId> { * Creates an id from a file <b>first</b> name string encoded in the standard translation (see {@link #toFileName}). * <b>Note</b> that any file last name, like e.g ".xml" must be stripped off before handoff to this method. */ - public static ComponentId fromFileName(final String fileName) { + public static ComponentId fromFileName(String fileName) { // Initial assumptions - String id=fileName; - Version version =null; - ComponentId namespace=null; + String id = fileName; + Version version = null; + ComponentId namespace = null; // Split out namespace, if any - int at=id.indexOf("@"); - if (at>0) { - String newId=id.substring(0,at); - namespace=ComponentId.fromString(id.substring(at+1)); - id=newId; + int at = id.indexOf("@"); + if (at > 0) { + String newId = id.substring(0, at); + namespace = ComponentId.fromString(id.substring(at + 1)); + id = newId; } // Split out version, if any - int dash=id.lastIndexOf("-"); - if (dash>0) { - String newId=id.substring(0,dash); + int dash = id.lastIndexOf("-"); + if (dash > 0) { + String newId = id.substring(0, dash); try { - version=new Version(id.substring(dash+1)); - id=newId; + version = new Version(id.substring(dash + 1)); + id = newId; } catch (IllegalArgumentException e) { // don't interpret the text following the dash as a version @@ -229,4 +226,10 @@ public final class ComponentId implements Comparable<ComponentId> { return new ComponentId(id,version,namespace); } + /** WARNING: For testing only: Resets counters creating anonymous component ids for this thread. */ + public static void resetGlobalCountersForTests() { + threadId.set("_0_"); + threadLocalUniqueId.set(new Counter()); + } + } diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/derived/DerivedConfiguration.java b/config-model/src/main/java/com/yahoo/searchdefinition/derived/DerivedConfiguration.java index 245913d7822..bb409ab6632 100644 --- a/config-model/src/main/java/com/yahoo/searchdefinition/derived/DerivedConfiguration.java +++ b/config-model/src/main/java/com/yahoo/searchdefinition/derived/DerivedConfiguration.java @@ -15,9 +15,11 @@ import com.yahoo.search.query.profile.QueryProfileRegistry; import com.yahoo.searchdefinition.RankProfileRegistry; import com.yahoo.searchdefinition.Search; import com.yahoo.searchdefinition.derived.validation.Validation; +import com.yahoo.vespa.model.container.search.QueryProfiles; import java.io.IOException; import java.io.Writer; +import java.util.logging.Level; /** * A set of all derived configuration of a search definition. Use this as a facade to individual configurations when @@ -44,7 +46,7 @@ public class DerivedConfiguration { * Creates a complete derived configuration from a search definition. * Only used in tests. * - * @param search The search to derive a configuration from. Derived objects will be snapshots, but this argument is + * @param search the search to derive a configuration from. Derived objects will be snapshots, but this argument is * live. Which means that this object will be inconsistent when the given search definition is later * modified. * @param rankProfileRegistry a {@link com.yahoo.searchdefinition.RankProfileRegistry} @@ -56,11 +58,11 @@ public class DerivedConfiguration { /** * Creates a complete derived configuration snapshot from a search definition. * - * @param search The search to derive a configuration from. Derived objects will be snapshots, but this + * @param search the search to derive a configuration from. Derived objects will be snapshots, but this * argument is live. Which means that this object will be inconsistent when the given * search definition is later modified. * @param deployLogger a {@link DeployLogger} for logging when doing operations on this - * @param deployProperties Properties set on deploy. + * @param deployProperties properties set on deploy * @param rankProfileRegistry a {@link com.yahoo.searchdefinition.RankProfileRegistry} * @param queryProfiles the query profiles of this application */ @@ -120,6 +122,10 @@ public class DerivedConfiguration { exportCfg(new DocumenttypesConfig(documentTypesCfg), toDirectory + "/" + "documenttypes.cfg"); } + public static void exportQueryProfiles(QueryProfileRegistry queryProfileRegistry, String toDirectory) throws IOException { + exportCfg(new QueryProfiles(queryProfileRegistry, (level, message) -> {}).getConfig(), toDirectory + "/" + "query-profiles.cfg"); + } + private static void exportCfg(ConfigInstance instance, String fileName) throws IOException { Writer writer = null; try { diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/QueryProfiles.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/QueryProfiles.java index 0abb0803405..0a9618e7b08 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/QueryProfiles.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/QueryProfiles.java @@ -232,8 +232,7 @@ public class QueryProfiles implements Serializable, QueryProfilesConfig.Producer return propB; } - private QueryProfilesConfig.Queryprofile.Queryprofilevariant.Property.Builder createVariantPropertyFieldConfig( - String fullName, Object value) { + private QueryProfilesConfig.Queryprofile.Queryprofilevariant.Property.Builder createVariantPropertyFieldConfig(String fullName, Object value) { QueryProfilesConfig.Queryprofile.Queryprofilevariant.Property.Builder propB = new QueryProfilesConfig.Queryprofile.Queryprofilevariant.Property.Builder(); if (value instanceof SubstituteString) value=value.toString(); // Send only types understood by configBuilder downwards @@ -251,7 +250,7 @@ public class QueryProfiles implements Serializable, QueryProfilesConfig.Producer qtB.matchaspath(true); for (QueryProfileType inherited : profileType.inherited()) qtB.inherit(inherited.getId().stringValue()); - List<FieldDescription> fields=new ArrayList<>(profileType.declaredFields().values()); + List<FieldDescription> fields = new ArrayList<>(profileType.declaredFields().values()); Collections.sort(fields); for (FieldDescription field : fields) qtB.field(createConfig(field)); @@ -260,22 +259,20 @@ public class QueryProfiles implements Serializable, QueryProfilesConfig.Producer private QueryProfilesConfig.Queryprofiletype.Field.Builder createConfig(FieldDescription field) { QueryProfilesConfig.Queryprofiletype.Field.Builder fB = new QueryProfilesConfig.Queryprofiletype.Field.Builder(); - fB. - name(field.getName()). - type(field.getType().stringValue()); + fB.name(field.getName()).type(field.getType().stringValue()); if ( ! field.isOverridable()) fB.overridable(false); if (field.isMandatory()) fB.mandatory(true); - String aliases=toSpaceSeparatedString(field.getAliases()); - if (!aliases.isEmpty()) + String aliases = toSpaceSeparatedString(field.getAliases()); + if ( ! aliases.isEmpty()) fB.alias(aliases); return fB; } public String toSpaceSeparatedString(List<String> list) { - StringBuilder b=new StringBuilder(); - for (Iterator<String> i=list.iterator(); i.hasNext(); ) { + StringBuilder b = new StringBuilder(); + for (Iterator<String> i = list.iterator(); i.hasNext(); ) { b.append(i.next()); if (i.hasNext()) b.append(" "); @@ -290,10 +287,7 @@ public class QueryProfiles implements Serializable, QueryProfilesConfig.Producer } } - /** - * The config produced by this - * @return query profiles config - */ + /** Returns the config produced by this */ public QueryProfilesConfig getConfig() { QueryProfilesConfig.Builder qB = new QueryProfilesConfig.Builder(); getConfig(qB); diff --git a/config-model/src/test/derived/neuralnet/query-profiles.cfg b/config-model/src/test/derived/neuralnet/query-profiles.cfg new file mode 100644 index 00000000000..ed69df7895d --- /dev/null +++ b/config-model/src/test/derived/neuralnet/query-profiles.cfg @@ -0,0 +1,53 @@ +queryprofile[].id "default" +queryprofile[].type "DefaultQueryProfileType" +queryprofiletype[].id "DefaultQueryProfileType" +queryprofiletype[].strict false +queryprofiletype[].matchaspath false +queryprofiletype[].field[].name "ranking" +queryprofiletype[].field[].type "query-profile:ranking_0_0" +queryprofiletype[].field[].overridable true +queryprofiletype[].field[].mandatory false +queryprofiletype[].field[].alias "" +queryprofiletype[].id "ranking_0_0" +queryprofiletype[].strict false +queryprofiletype[].matchaspath false +queryprofiletype[].inherit[] "ranking" +queryprofiletype[].field[].name "features" +queryprofiletype[].field[].type "query-profile:features_0_1" +queryprofiletype[].field[].overridable true +queryprofiletype[].field[].mandatory false +queryprofiletype[].field[].alias "rankfeature" +queryprofiletype[].id "features_0_1" +queryprofiletype[].strict false +queryprofiletype[].matchaspath false +queryprofiletype[].field[].name "query(W_0)" +queryprofiletype[].field[].type "tensor(hidden[9],x[9])" +queryprofiletype[].field[].overridable true +queryprofiletype[].field[].mandatory false +queryprofiletype[].field[].alias "" +queryprofiletype[].field[].name "query(W_1)" +queryprofiletype[].field[].type "tensor(hidden[9],out[9])" +queryprofiletype[].field[].overridable true +queryprofiletype[].field[].mandatory false +queryprofiletype[].field[].alias "" +queryprofiletype[].field[].name "query(W_out)" +queryprofiletype[].field[].type "tensor(out[9])" +queryprofiletype[].field[].overridable true +queryprofiletype[].field[].mandatory false +queryprofiletype[].field[].alias "" +queryprofiletype[].field[].name "query(b_0)" +queryprofiletype[].field[].type "tensor(hidden[9])" +queryprofiletype[].field[].overridable true +queryprofiletype[].field[].mandatory false +queryprofiletype[].field[].alias "" +queryprofiletype[].field[].name "query(b_1)" +queryprofiletype[].field[].type "tensor(out[9])" +queryprofiletype[].field[].overridable true +queryprofiletype[].field[].mandatory false +queryprofiletype[].field[].alias "" +queryprofiletype[].field[].name "query(b_out)" +queryprofiletype[].field[].type "tensor(out[1])" +queryprofiletype[].field[].overridable true +queryprofiletype[].field[].mandatory false +queryprofiletype[].field[].alias "" +enableGroupingSessionCache true diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/derived/AbstractExportingTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/derived/AbstractExportingTestCase.java index d67df3a5239..8ea53172200 100644 --- a/config-model/src/test/java/com/yahoo/searchdefinition/derived/AbstractExportingTestCase.java +++ b/config-model/src/test/java/com/yahoo/searchdefinition/derived/AbstractExportingTestCase.java @@ -10,6 +10,8 @@ import com.yahoo.searchdefinition.parser.ParseException; import ai.vespa.rankingexpression.importer.configmodelview.ImportedMlModels; import com.yahoo.vespa.configmodel.producers.DocumentManager; import com.yahoo.vespa.configmodel.producers.DocumentTypes; +import com.yahoo.vespa.model.container.search.QueryProfiles; +import com.yahoo.vespa.model.test.utils.DeployLoggerStub; import java.io.File; import java.io.IOException; @@ -53,6 +55,7 @@ public abstract class AbstractExportingTestCase extends SearchDefinitionTestCase String path = exportConfig(name, config); DerivedConfiguration.exportDocuments(new DocumentManager().produce(builder.getModel(), new DocumentmanagerConfig.Builder()), path); DerivedConfiguration.exportDocuments(new DocumentTypes().produce(builder.getModel(), new DocumenttypesConfig.Builder()), path); + DerivedConfiguration.exportQueryProfiles(builder.getQueryProfileRegistry(), path); return config; } diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/derived/NeuralNetTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/derived/NeuralNetTestCase.java index b299c7fa299..5229485c8f9 100644 --- a/config-model/src/test/java/com/yahoo/searchdefinition/derived/NeuralNetTestCase.java +++ b/config-model/src/test/java/com/yahoo/searchdefinition/derived/NeuralNetTestCase.java @@ -6,11 +6,14 @@ import org.junit.Test; import java.io.IOException; +import com.yahoo.component.ComponentId; + public class NeuralNetTestCase extends AbstractExportingTestCase { @Test public void testNeuralNet() throws IOException, ParseException { - assertCorrectDeriving("neuralnet"); + ComponentId.resetGlobalCountersForTests(); + DerivedConfiguration c = assertCorrectDeriving("neuralnet"); } } diff --git a/container-search/abi-spec.json b/container-search/abi-spec.json index 437db99c45b..6d0bce74d2c 100644 --- a/container-search/abi-spec.json +++ b/container-search/abi-spec.json @@ -6285,6 +6285,7 @@ "public boolean isOverridable(java.lang.String)", "public java.lang.Class getValueClass(java.lang.String)", "public com.yahoo.search.query.profile.types.QueryProfileType getType(java.lang.String)", + "public com.yahoo.search.query.profile.types.FieldType getFieldType(com.yahoo.processing.request.CompoundName)", "public com.yahoo.search.query.profile.types.FieldDescription getField(java.lang.String)", "public com.yahoo.search.query.profile.types.FieldDescription removeField(java.lang.String)", "public void addField(com.yahoo.search.query.profile.types.FieldDescription)", diff --git a/container-search/src/main/java/com/yahoo/search/query/Ranking.java b/container-search/src/main/java/com/yahoo/search/query/Ranking.java index 7444c94f491..830a3f4ef81 100644 --- a/container-search/src/main/java/com/yahoo/search/query/Ranking.java +++ b/container-search/src/main/java/com/yahoo/search/query/Ranking.java @@ -47,7 +47,7 @@ public class Ranking implements Cloneable { public static final String PROPERTIES = "properties"; static { - argumentType =new QueryProfileType(RANKING); + argumentType = new QueryProfileType(RANKING); argumentType.setStrict(true); argumentType.setBuiltin(true); argumentType.addField(new FieldDescription(LOCATION, "string", "location")); @@ -63,7 +63,7 @@ public class Ranking implements Cloneable { argumentType.addField(new FieldDescription(FEATURES, "query-profile", "rankfeature")); argumentType.addField(new FieldDescription(PROPERTIES, "query-profile", "rankproperty")); argumentType.freeze(); - argumentTypeName=new CompoundName(argumentType.getId().getName()); + argumentTypeName = new CompoundName(argumentType.getId().getName()); } public static QueryProfileType getArgumentType() { return argumentType; } diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/BackedOverridableQueryProfile.java b/container-search/src/main/java/com/yahoo/search/query/profile/BackedOverridableQueryProfile.java index 99f1e26b221..11864e60cec 100644 --- a/container-search/src/main/java/com/yahoo/search/query/profile/BackedOverridableQueryProfile.java +++ b/container-search/src/main/java/com/yahoo/search/query/profile/BackedOverridableQueryProfile.java @@ -135,8 +135,8 @@ public class BackedOverridableQueryProfile extends OverridableQueryProfile imple @Override public List<String> getDimensions() { - List<String> dimensions=super.getDimensions(); - if (dimensions!=null) return dimensions; + List<String> dimensions = super.getDimensions(); + if (dimensions != null) return dimensions; return backingProfile.getDimensions(); } diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfile.java b/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfile.java index e9ccdd22f98..7ae18f96d86 100644 --- a/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfile.java +++ b/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfile.java @@ -100,7 +100,7 @@ public class QueryProfile extends FreezableSimpleComponent implements Cloneable public QueryProfileType getType() { return type; } /** Sets the type of this, or set to null to not use any type checking in this profile */ - public void setType(QueryProfileType type) { this.type=type; } + public void setType(QueryProfileType type) { this.type = type; } /** Returns the virtual variants of this, or null if none */ public QueryProfileVariants getVariants() { return variants; } 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 5d4f39cecbf..05e3c4fe9a0 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 @@ -9,6 +9,7 @@ import com.yahoo.search.query.Properties; import com.yahoo.search.query.profile.compiled.CompiledQueryProfile; import com.yahoo.search.query.profile.compiled.DimensionalValue; 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 java.util.ArrayList; @@ -87,8 +88,10 @@ public class QueryProfileProperties extends Properties { // Check types if ( ! profile.getTypes().isEmpty()) { - for (int i = 0; i<name.size(); i++) { - QueryProfileType type = profile.getType(name.first(i), context); + QueryProfileType type = null; + for (int i = 0; i < name.size(); i++) { + if (type == null) // We're on the first iteration, or no type is explicitly specified + type = profile.getType(name.first(i), context); if (type == null) continue; String localName = name.get(i); FieldDescription fieldDescription = type.getField(localName); @@ -97,12 +100,19 @@ public class QueryProfileProperties extends Properties { // TODO: In addition to strictness, check legality along the way - if (i == name.size()-1 && fieldDescription != null) { // at the end of the path, check the assignment type - value = fieldDescription.getType().convertFrom(value, profile.getRegistry()); - if (value == null) - throw new IllegalArgumentException("'" + value + "' is not a " + - fieldDescription.getType().toInstanceDescription()); + if (fieldDescription != null) { + if (i == name.size() - 1) { // at the end of the path, check the assignment type + value = fieldDescription.getType().convertFrom(value, profile.getRegistry()); + if (value == null) + throw new IllegalArgumentException("'" + 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 + type = ((QueryProfileFieldType) fieldDescription.getType()).getQueryProfileType(); + } } + } } diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/config/QueryProfileXMLReader.java b/container-search/src/main/java/com/yahoo/search/query/profile/config/QueryProfileXMLReader.java index 33f07a58195..1b1cdce5890 100644 --- a/container-search/src/main/java/com/yahoo/search/query/profile/config/QueryProfileXMLReader.java +++ b/container-search/src/main/java/com/yahoo/search/query/profile/config/QueryProfileXMLReader.java @@ -33,7 +33,7 @@ public class QueryProfileXMLReader { * Reads all query profile xml files in a given directory, * and all type xml files from the immediate subdirectory "types/" (if any) * - * @throws RuntimeException if <code>directory</code> is not a readable directory, or if there is some error in the XML + * @throws IllegalArgumentException if the directory is not readable, or if there is some error in the XML */ public QueryProfileRegistry read(String directory) { List<NamedReader> queryProfileReaders = new ArrayList<>(); @@ -58,7 +58,7 @@ public class QueryProfileXMLReader { return read(queryProfileTypeReaders,queryProfileReaders); } catch (IOException e) { - throw new IllegalArgumentException("Could not read query profiles from '" + directory + "'",e); + throw new IllegalArgumentException("Could not read query profiles from '" + directory + "'", e); } finally { closeAll(queryProfileReaders); @@ -105,14 +105,14 @@ public class QueryProfileXMLReader { "' must be 'query-profile-type', not '" + root.getNodeName() + "'"); } - String idString=root.getAttribute("id"); + String idString = root.getAttribute("id"); if (idString == null || idString.equals("")) throw new IllegalArgumentException("'" + reader.getName() + "' has no 'id' attribute in the root element"); ComponentId id = new ComponentId(idString); - validateFileNameToId(reader.getName(),id,"query profile type"); + validateFileNameToId(reader.getName(), id,"query profile type"); QueryProfileType type = new QueryProfileType(id); - type.setMatchAsPath(XML.getChild(root,"match") != null); - type.setStrict(XML.getChild(root,"strict") != null); + type.setMatchAsPath(XML.getChild(root, "match") != null); + type.setStrict(XML.getChild(root, "strict") != null); registry.register(type); queryProfileTypeElements.add(root); } @@ -145,7 +145,7 @@ public class QueryProfileXMLReader { queryProfile.setType(type); } - Element dimensions = XML.getChild(root,"dimensions"); + Element dimensions = XML.getChild(root, "dimensions"); if (dimensions != null) queryProfile.setDimensions(toArray(XML.getValue(dimensions))); @@ -215,7 +215,7 @@ public class QueryProfileXMLReader { try { String fieldTypeName = field.getAttribute("type"); if (fieldTypeName == null) throw new IllegalArgumentException("Field '" + field + "' has no 'type' attribute"); - FieldType fieldType=FieldType.fromString(fieldTypeName,registry); + FieldType fieldType = FieldType.fromString(fieldTypeName, registry); type.addField(new FieldDescription(name, fieldType, field.getAttribute("alias"), 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 b8290fa092b..6c30f1a8b05 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 @@ -97,7 +97,7 @@ public class FieldDescription implements Comparable<FieldDescription> { this.type = type; // Forbidden until we can figure out the right semantics - if (name.isCompound() && ! aliases.isEmpty()) throw new IllegalArgumentException("Aliases is not allowed with compound names"); + if (name.isCompound() && ! aliases.isEmpty()) throw new IllegalArgumentException("Aliases are not allowed with compound names"); this.aliases = ImmutableList.copyOf(aliases); this.mandatory = mandatory; diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/types/QueryProfileType.java b/container-search/src/main/java/com/yahoo/search/query/profile/types/QueryProfileType.java index 07c9e4475ec..e3696916c53 100644 --- a/container-search/src/main/java/com/yahoo/search/query/profile/types/QueryProfileType.java +++ b/container-search/src/main/java/com/yahoo/search/query/profile/types/QueryProfileType.java @@ -6,6 +6,7 @@ import com.google.common.collect.ImmutableMap; import com.yahoo.component.ComponentId; import com.yahoo.component.provider.FreezableSimpleComponent; import com.yahoo.processing.request.CompoundName; +import com.yahoo.search.query.profile.OverridableQueryProfile; import com.yahoo.search.query.profile.QueryProfile; import java.util.ArrayList; @@ -23,6 +24,8 @@ import static com.yahoo.text.Lowercase.toLowerCase; */ public class QueryProfileType extends FreezableSimpleComponent { + private static final String simpleClassName = QueryProfileType.class.getSimpleName(); + private final CompoundName componentIdAsCompoundName; /** The fields of this query profile type */ private Map<String, FieldDescription> fields; @@ -217,25 +220,38 @@ public class QueryProfileType extends FreezableSimpleComponent { /** Returns the type of the given query profile type declared as a field in this */ public QueryProfileType getType(String localName) { - FieldDescription fieldDescription=getField(localName); - if (fieldDescription ==null) return null; + FieldDescription fieldDescription = getField(localName); + if (fieldDescription == null) return null; if ( ! (fieldDescription.getType() instanceof QueryProfileFieldType)) return null; return ((QueryProfileFieldType) fieldDescription.getType()).getQueryProfileType(); } + /** Returns the field type of the given name under this, of null if none */ + public FieldType getFieldType(CompoundName name) { + FieldDescription field = getField(name.first()); + if (field == null) return null; + + FieldType fieldType = field.getType(); + if (name.size() == 1) return fieldType; + + if ( ! (fieldType instanceof QueryProfileFieldType)) return null; + + return ((QueryProfileFieldType)fieldType).getQueryProfileType().getFieldType(name.rest()); + } + /** * Returns the description of the field with the given name in this type or an inherited type * (depth first left to right search). Returns null if the field is not defined in this or an inherited profile. */ public FieldDescription getField(String name) { - FieldDescription field=fields.get(name); - if ( field!=null ) return field; + FieldDescription field = fields.get(name); + if ( field != null ) return field; if ( isFrozen() ) return null; // Inherited are collapsed into this for (QueryProfileType inheritedType : this.inherited() ) { - field=inheritedType.getField(name); - if (field!=null) return field; + field = inheritedType.getField(name); + if (field != null) return field; } return null; @@ -276,7 +292,7 @@ public class QueryProfileType extends FreezableSimpleComponent { // Add (/to) a query profile type containing the rest of the name. // (we do not need the field description settings for intermediate query profile types // as the leaf entry will enforce them) - QueryProfileType type = getOrCreateQueryProfileType(name.first(), registry); + QueryProfileType type = extendOrCreateQueryProfileType(name.first(), registry); type.addField(fieldDescription.withName(name.rest()), registry); } else { @@ -288,27 +304,40 @@ public class QueryProfileType extends FreezableSimpleComponent { addAlias(alias, fieldDescription.getName()); } - private QueryProfileType getOrCreateQueryProfileType(String name, QueryProfileTypeRegistry registry) { + private QueryProfileType extendOrCreateQueryProfileType(String name, QueryProfileTypeRegistry registry) { + QueryProfileType type = null; FieldDescription fieldDescription = getField(name); if (fieldDescription != null) { - if ( ! ( fieldDescription.getType() instanceof QueryProfileFieldType)) + if ( ! (fieldDescription.getType() instanceof QueryProfileFieldType)) throw new IllegalArgumentException("Cannot use name '" + name + "' as a prefix because it is " + "already a " + fieldDescription.getType()); QueryProfileFieldType fieldType = (QueryProfileFieldType) fieldDescription.getType(); - QueryProfileType type = fieldType.getQueryProfileType(); - if (type == null) { // an as-yet untyped reference; add type - type = new QueryProfileType(name); - registry.register(type.getId(), type); - fields.put(name, fieldDescription.withType(new QueryProfileFieldType(type))); + type = fieldType.getQueryProfileType(); + } + + if (type == null) { + type = registry.getComponent(name); + if (type != null) { // found in registry but not already added in this type: extend it + type = new QueryProfileType(ComponentId.createAnonymousComponentId(type.getIdString()), + new HashMap<>(), + List.of(type)); } - return type; + } + + if (type == null) { // create it + type = new QueryProfileType(ComponentId.createAnonymousComponentId(name)); + } + + if (fieldDescription == null) { + fieldDescription = new FieldDescription(name, new QueryProfileFieldType(type)); } else { - QueryProfileType type = new QueryProfileType(name); - registry.register(type.getId(), type); - fields.put(name, new FieldDescription(name, new QueryProfileFieldType(type))); - return type; + fieldDescription = fieldDescription.withType(new QueryProfileFieldType(type)); } + + registry.register(type); + fields.put(name, fieldDescription); + return type; } private void addAlias(String alias, String field) { @@ -362,6 +391,7 @@ public class QueryProfileType extends FreezableSimpleComponent { return other.getId().equals(this.getId()); } + @Override public String toString() { return "query profile type '" + getId() + "'"; } diff --git a/container-search/src/test/java/com/yahoo/search/query/profile/config/test/XmlReadingTestCase.java b/container-search/src/test/java/com/yahoo/search/query/profile/config/test/XmlReadingTestCase.java index 0f4a22ef368..e9f7ff24d42 100644 --- a/container-search/src/test/java/com/yahoo/search/query/profile/config/test/XmlReadingTestCase.java +++ b/container-search/src/test/java/com/yahoo/search/query/profile/config/test/XmlReadingTestCase.java @@ -384,43 +384,64 @@ public class XmlReadingTestCase { assertNull(query.properties().get("profileRef.myProfile1Only")); // later assignment - query.properties().set("profileRef.name","newName"); - assertEquals("newName",query.properties().get("profileRef.name")); + query.properties().set("profileRef.name", "newName"); + assertEquals("newName", query.properties().get("profileRef.name")); // ...will not impact others - query=new Query(HttpRequest.createTestRequest("?query=test&profileRef=ref:MyProfile2", Method.GET),registry.getComponent("default")); - assertEquals("MyProfile2",query.properties().get("profileRef.name")); + query=new Query(HttpRequest.createTestRequest("?query=test&profileRef=ref:MyProfile2", Method.GET), registry.getComponent("default")); + assertEquals("MyProfile2", query.properties().get("profileRef.name")); } } @Test public void testRefOverrideTyped() { - CompiledQueryProfileRegistry registry=new QueryProfileXMLReader().read("src/test/java/com/yahoo/search/query/profile/config/test/refoverridetyped").compile(); + CompiledQueryProfileRegistry registry = new QueryProfileXMLReader().read("src/test/java/com/yahoo/search/query/profile/config/test/refoverridetyped").compile(); { // Original reference - Query query=new Query(HttpRequest.createTestRequest("?query=test", Method.GET),registry.getComponent("default")); - assertEquals(null,query.properties().get("profileRef")); - assertEquals("MyProfile1",query.properties().get("profileRef.name")); - assertEquals("myProfile1Only",query.properties().get("profileRef.myProfile1Only")); + Query query = new Query(HttpRequest.createTestRequest("?query=test", Method.GET), registry.getComponent("default")); + assertEquals(null, query.properties().get("profileRef")); + assertEquals("MyProfile1", query.properties().get("profileRef.name")); + assertEquals("myProfile1Only", query.properties().get("profileRef.myProfile1Only")); assertNull(query.properties().get("profileRef.myProfile2Only")); } { // Overridden reference - Query query=new Query(HttpRequest.createTestRequest("?query=test&profileRef=MyProfile2", Method.GET),registry.getComponent("default")); - assertEquals(null,query.properties().get("profileRef")); - assertEquals("MyProfile2",query.properties().get("profileRef.name")); - assertEquals("myProfile2Only",query.properties().get("profileRef.myProfile2Only")); + Query query = new Query(HttpRequest.createTestRequest("?query=test&profileRef=MyProfile2", Method.GET), registry.getComponent("default")); + assertEquals(null, query.properties().get("profileRef")); + assertEquals("MyProfile2", query.properties().get("profileRef.name")); + assertEquals("myProfile2Only", query.properties().get("profileRef.myProfile2Only")); assertNull(query.properties().get("profileRef.myProfile1Only")); // later assignment - query.properties().set("profileRef.name","newName"); - assertEquals("newName",query.properties().get("profileRef.name")); + query.properties().set("profileRef.name", "newName"); + assertEquals("newName", query.properties().get("profileRef.name")); // ...will not impact others - query=new Query(HttpRequest.createTestRequest("?query=test&profileRef=ref:MyProfile2", Method.GET),registry.getComponent("default")); - assertEquals("MyProfile2",query.properties().get("profileRef.name")); + query = new Query(HttpRequest.createTestRequest("?query=test&profileRef=ref:MyProfile2", Method.GET), registry.getComponent("default")); + assertEquals("MyProfile2", query.properties().get("profileRef.name")); } } + @Test + public void testTensorTypes() { + CompiledQueryProfileRegistry registry = new QueryProfileXMLReader().read("src/test/java/com/yahoo/search/query/profile/config/test/tensortypes").compile(); + + QueryProfileType type1 = registry.getTypeRegistry().getComponent("type1"); + assertEquals("tensor<float>(x[1])", type1.getFieldType(new CompoundName("ranking.features.query(tensor_1)")).stringValue()); + assertNull(type1.getFieldType(new CompoundName("ranking.features.query(tensor_2)"))); + assertNull(type1.getFieldType(new CompoundName("ranking.features.query(tensor_3)"))); + + QueryProfileType type2 = registry.getTypeRegistry().getComponent("type2"); + assertNull(type2.getFieldType(new CompoundName("ranking.features.query(tensor_1)"))); + assertEquals("tensor<float>(x[2])", type2.getFieldType(new CompoundName("ranking.features.query(tensor_2)")).stringValue()); + assertEquals("tensor<float>(x[3])", type2.getFieldType(new CompoundName("ranking.features.query(tensor_3)")).stringValue()); + + Query queryProfile1 = new Query("?query=test&ranking.features.query(tensor_1)=[1.200]", registry.getComponent("profile1")); + assertEquals("Is received as a tensor tensor", "tensor<float>(x[1]):[1.2]", queryProfile1.properties().get("ranking.features.query(tensor_1)").toString()); + + Query queryProfile2 = new Query("?query=test&ranking.features.query(tensor_1)=[1.200]", registry.getComponent("profile2")); + assertEquals("Is received as a string", "[1.200]", queryProfile2.properties().get("ranking.features.query(tensor_1)").toString()); + } + } diff --git a/container-search/src/test/java/com/yahoo/search/query/profile/config/test/tensortypes/profile1.xml b/container-search/src/test/java/com/yahoo/search/query/profile/config/test/tensortypes/profile1.xml new file mode 100644 index 00000000000..000fd3e1c5b --- /dev/null +++ b/container-search/src/test/java/com/yahoo/search/query/profile/config/test/tensortypes/profile1.xml @@ -0,0 +1,2 @@ +<query-profile id="profile1" type="type1"> +</query-profile> diff --git a/container-search/src/test/java/com/yahoo/search/query/profile/config/test/tensortypes/profile2.xml b/container-search/src/test/java/com/yahoo/search/query/profile/config/test/tensortypes/profile2.xml new file mode 100644 index 00000000000..f6539da23e8 --- /dev/null +++ b/container-search/src/test/java/com/yahoo/search/query/profile/config/test/tensortypes/profile2.xml @@ -0,0 +1,2 @@ +<query-profile id="profile2" type="type2"> +</query-profile> diff --git a/container-search/src/test/java/com/yahoo/search/query/profile/config/test/tensortypes/types/type1.xml b/container-search/src/test/java/com/yahoo/search/query/profile/config/test/tensortypes/types/type1.xml new file mode 100644 index 00000000000..3dfaab9c5f2 --- /dev/null +++ b/container-search/src/test/java/com/yahoo/search/query/profile/config/test/tensortypes/types/type1.xml @@ -0,0 +1,3 @@ +<query-profile-type id="type1"> + <field name="ranking.features.query(tensor_1)" type="tensor<float>(x[1])" /> +</query-profile-type> diff --git a/container-search/src/test/java/com/yahoo/search/query/profile/config/test/tensortypes/types/type2.xml b/container-search/src/test/java/com/yahoo/search/query/profile/config/test/tensortypes/types/type2.xml new file mode 100644 index 00000000000..ed7cf23e464 --- /dev/null +++ b/container-search/src/test/java/com/yahoo/search/query/profile/config/test/tensortypes/types/type2.xml @@ -0,0 +1,4 @@ +<query-profile-type id="type2"> + <field name="ranking.features.query(tensor_2)" type="tensor<float>(x[2])" /> + <field name="ranking.features.query(tensor_3)" type="tensor<float>(x[3])" /> +</query-profile-type> diff --git a/container-search/src/test/java/com/yahoo/search/query/profile/types/test/QueryProfileTypeTestCase.java b/container-search/src/test/java/com/yahoo/search/query/profile/types/test/QueryProfileTypeTestCase.java index c05c3589a30..3c200debcaf 100644 --- a/container-search/src/test/java/com/yahoo/search/query/profile/types/test/QueryProfileTypeTestCase.java +++ b/container-search/src/test/java/com/yahoo/search/query/profile/types/test/QueryProfileTypeTestCase.java @@ -397,7 +397,7 @@ public class QueryProfileTypeTestCase { @Test public void testTensorRankFeatureInRequest() throws UnsupportedEncodingException { - QueryProfile profile=new QueryProfile("test"); + QueryProfile profile = new QueryProfile("test"); profile.setType(type); registry.register(profile); @@ -447,25 +447,25 @@ public class QueryProfileTypeTestCase { */ @Test public void testTypedOverridingOfQueryProfileReferencesNonStrictThroughQueryNestedInAnUntypedProfile() { - QueryProfile topMap=new QueryProfile("topMap"); + QueryProfile topMap = new QueryProfile("topMap"); - QueryProfile subMap=new QueryProfile("topSubMap"); - topMap.set("subMap",subMap, registry); + QueryProfile subMap = new QueryProfile("topSubMap"); + topMap.set("subMap", subMap, registry); - QueryProfile test=new QueryProfile("test"); + QueryProfile test = new QueryProfile("test"); test.setType(type); - subMap.set("typeProfile",test, registry); + subMap.set("typeProfile", test, registry); - QueryProfile myUser=new QueryProfile("myUser"); + QueryProfile myUser = new QueryProfile("myUser"); myUser.setType(user); - myUser.set("myUserString","userValue1", registry); - myUser.set("myUserInteger",442, registry); - test.set("myUserQueryProfile",myUser, registry); + myUser.set("myUserString", "userValue1", registry); + myUser.set("myUserInteger", 442, registry); + test.set("myUserQueryProfile", myUser, registry); - QueryProfile newUser=new QueryProfile("newUser"); + QueryProfile newUser = new QueryProfile("newUser"); newUser.setType(user); - newUser.set("myUserString","newUserValue1", registry); - newUser.set("myUserInteger",845, registry); + newUser.set("myUserString", "newUserValue1", registry); + newUser.set("myUserInteger", 845, registry); registry.register(topMap); registry.register(subMap); |