diff options
8 files changed, 144 insertions, 69 deletions
diff --git a/container-search/abi-spec.json b/container-search/abi-spec.json index 94baaf4ef52..b1b80eac3a4 100644 --- a/container-search/abi-spec.json +++ b/container-search/abi-spec.json @@ -5224,7 +5224,7 @@ "public void setType(java.lang.String)", "public boolean equals(java.lang.Object)", "public int hashCode()", - "public java.lang.Object clone()", + "public com.yahoo.search.query.Model clone()", "public com.yahoo.search.query.Model cloneFor(com.yahoo.search.Query)", "public com.yahoo.search.Query getParent()", "public void setParent(com.yahoo.search.Query)", @@ -5238,7 +5238,8 @@ "public com.yahoo.search.searchchain.Execution getExecution()", "public static com.yahoo.search.query.Model getFrom(com.yahoo.search.Query)", "public java.lang.String toString()", - "public void prepare(com.yahoo.search.query.Ranking)" + "public void prepare(com.yahoo.search.query.Ranking)", + "public bridge synthetic java.lang.Object clone()" ], "fields": [ "public static final java.lang.String MODEL", @@ -5390,14 +5391,17 @@ "public com.yahoo.search.query.ranking.MatchPhase getMatchPhase()", "public com.yahoo.search.query.ranking.Matching getMatching()", "public com.yahoo.search.query.ranking.SoftTimeout getSoftTimeout()", - "public java.lang.Object clone()", - "public boolean equals(java.lang.Object)", - "public int hashCode()", "public com.yahoo.search.query.Sorting getSorting()", "public void setSorting(com.yahoo.search.query.Sorting)", "public void setSorting(java.lang.String)", "public static com.yahoo.search.query.Ranking getFrom(com.yahoo.search.Query)", - "public void prepare()" + "public void prepare()", + "public com.yahoo.search.Query getParent()", + "public com.yahoo.search.query.Ranking clone()", + "public com.yahoo.search.query.Ranking cloneFor(com.yahoo.search.Query)", + "public boolean equals(java.lang.Object)", + "public int hashCode()", + "public bridge synthetic java.lang.Object clone()" ], "fields": [ "public static final com.yahoo.processing.request.CompoundName RANKFEATURES", @@ -6853,6 +6857,7 @@ ], "methods": [ "public void <init>()", + "public void <init>(com.yahoo.search.query.Ranking)", "public void put(java.lang.String, double)", "public void put(java.lang.String, com.yahoo.tensor.Tensor)", "public void put(java.lang.String, java.lang.String)", @@ -6867,6 +6872,7 @@ "public boolean equals(java.lang.Object)", "public int hashCode()", "public com.yahoo.search.query.ranking.RankFeatures clone()", + "public com.yahoo.search.query.ranking.RankFeatures cloneFor(com.yahoo.search.query.Ranking)", "public java.lang.String toString()", "public bridge synthetic java.lang.Object clone()" ], 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 fbbe665db7f..9d2c7f6eec9 100644 --- a/container-search/src/main/java/com/yahoo/search/Query.java +++ b/container-search/src/main/java/com/yahoo/search/Query.java @@ -995,7 +995,7 @@ public class Query extends com.yahoo.processing.Request implements Cloneable { private void copyPropertiesTo(Query clone) { clone.model = model.cloneFor(clone); clone.select = select.cloneFor(clone); - clone.ranking = (Ranking) ranking.clone(); + clone.ranking = ranking.cloneFor(clone); clone.presentation = (Presentation) presentation.clone(); clone.context = getContext(true).cloneFor(clone); diff --git a/container-search/src/main/java/com/yahoo/search/query/Model.java b/container-search/src/main/java/com/yahoo/search/query/Model.java index 81efadbba0a..0cc7a50141f 100644 --- a/container-search/src/main/java/com/yahoo/search/query/Model.java +++ b/container-search/src/main/java/com/yahoo/search/query/Model.java @@ -24,6 +24,7 @@ import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; +import java.util.Objects; import java.util.Set; import static com.yahoo.text.Lowercase.toLowerCase; @@ -333,7 +334,7 @@ public class Model implements Cloneable { } @Override - public Object clone() { + public Model clone() { try { Model clone = (Model)super.clone(); if (queryTree != null) @@ -349,19 +350,18 @@ public class Model implements Cloneable { } } - public Model cloneFor(Query q) { - Model model = (Model)this.clone(); - model.setParent(q); + public Model cloneFor(Query query) { + Model model = this.clone(); + model.setParent(query); return model; } - /** returns the query owning this, never null */ + /** Returns the query owning this, never null */ public Query getParent() { return parent; } /** Assigns the query owning this */ public void setParent(Query parent) { - if (parent == null) throw new NullPointerException("A query models owner cannot be null"); - this.parent = parent; + this.parent = Objects.requireNonNull(parent, "A query models parent cannot be null"); } /** Sets the set of sources this query will search from a comma-separated string of source names */ 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 a5f4d3e37ff..19c1034667a 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 @@ -16,6 +16,8 @@ import com.yahoo.search.query.ranking.RankProperties; import com.yahoo.search.query.ranking.SoftTimeout; import com.yahoo.search.result.ErrorMessage; +import java.util.Objects; + /** * The ranking (hit ordering) settings of a query * @@ -69,7 +71,7 @@ public class Ranking implements Cloneable { } public static QueryProfileType getArgumentType() { return argumentType; } - private final Query parent; + private Query parent; /** The location of the query is used for distance ranking */ private Location location = null; @@ -91,7 +93,7 @@ public class Ranking implements Cloneable { private RankProperties rankProperties = new RankProperties(); - private RankFeatures rankFeatures = new RankFeatures(); + private RankFeatures rankFeatures; private MatchPhase matchPhase = new MatchPhase(); @@ -101,6 +103,7 @@ public class Ranking implements Cloneable { public Ranking(Query parent) { this.parent = parent; + this.rankFeatures = new RankFeatures(this); } /** @@ -201,15 +204,59 @@ public class Ranking implements Cloneable { /** Returns the soft timeout settings of this. This is never null. */ public SoftTimeout getSoftTimeout() { return softTimeout; } + /** Returns the sorting spec of this query, or null if none is set */ + public Sorting getSorting() { return sorting; } + + /** Sets how this query should be sorted. Set to null to turn off explicit sorting. */ + public void setSorting(Sorting sorting) { this.sorting = sorting; } + + /** Sets sorting from a string. See {@link Sorting} on syntax */ + public void setSorting(String sortingString) { + if (sortingString == null) + setSorting((Sorting)null); + else + setSorting(new Sorting(sortingString)); + } + + public static Ranking getFrom(Query q) { + return (Ranking) q.properties().get(argumentTypeName); + } + + public void prepare() { + rankFeatures.prepare(rankProperties); + matchPhase.prepare(rankProperties); + matching.prepare(rankProperties); + softTimeout.prepare(rankProperties); + prepareNow(freshness); + if (rerankCount != null) + rankProperties.put("vespa.hitcollector.heapsize", rerankCount); + } + + private void prepareNow(Freshness freshness) { + if (freshness == null) return; + // TODO: See what freshness is doing with the internal props and simplify + if (rankProperties.get("vespa.now") == null || rankProperties.get("vespa.now").isEmpty()) { + rankProperties.put("vespa.now", "" + freshness.getRefTime()); + } + } + + /** Assigns the query owning this */ + private void setParent(Query parent) { + this.parent = Objects.requireNonNull(parent, "A ranking objects parent cannot be null"); + } + + /** Returns the query owning this, never null */ + public Query getParent() { return parent; } + @Override - public Object clone() { + public Ranking clone() { try { Ranking clone = (Ranking) super.clone(); if (sorting != null) clone.sorting = this.sorting.clone(); clone.rankProperties = this.rankProperties.clone(); - clone.rankFeatures = this.rankFeatures.clone(); + clone.rankFeatures = this.rankFeatures.cloneFor(clone); clone.matchPhase = this.matchPhase.clone(); clone.matching = this.matching.clone(); clone.softTimeout = this.softTimeout.clone(); @@ -220,6 +267,12 @@ public class Ranking implements Cloneable { } } + public Ranking cloneFor(Query parent) { + Ranking ranking = this.clone(); + ranking.setParent(parent); + return ranking; + } + @Override public boolean equals(Object o) { if (o == this) return true; @@ -238,49 +291,7 @@ public class Ranking implements Cloneable { @Override public int hashCode() { - int hash = 0; - hash += 11 * rankFeatures.hashCode(); - hash += 13 * rankProperties.hashCode(); - hash += 17 * matchPhase.hashCode(); - hash += 19 * softTimeout.hashCode(); - hash += 23 * matching.hashCode(); - return Ranking.class.hashCode() + QueryHelper.combineHash(sorting,location,profile,hash); - } - - /** Returns the sorting spec of this query, or null if none is set */ - public Sorting getSorting() { return sorting; } - - /** Sets how this query should be sorted. Set to null to turn off explicit sorting. */ - public void setSorting(Sorting sorting) { this.sorting = sorting; } - - /** Sets sorting from a string. See {@link Sorting} on syntax */ - public void setSorting(String sortingString) { - if (sortingString == null) - setSorting((Sorting)null); - else - setSorting(new Sorting(sortingString)); - } - - public static Ranking getFrom(Query q) { - return (Ranking) q.properties().get(argumentTypeName); - } - - public void prepare() { - rankFeatures.prepare(rankProperties); - matchPhase.prepare(rankProperties); - matching.prepare(rankProperties); - softTimeout.prepare(rankProperties); - prepareNow(freshness); - if (rerankCount != null) - rankProperties.put("vespa.hitcollector.heapsize", rerankCount); - } - - private void prepareNow(Freshness freshness) { - if (freshness == null) return; - // TODO: See what freshness is doing with the internal props and simplify - if (rankProperties.get("vespa.now") == null || rankProperties.get("vespa.now").isEmpty()) { - rankProperties.put("vespa.now", "" + freshness.getRefTime()); - } + return Objects.hash(rankFeatures, rankProperties, matchPhase, softTimeout, matching, sorting, location, profile); } } 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 a0f38f61672..f30a3cc5ae6 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 @@ -4,10 +4,8 @@ package com.yahoo.search.query.profile.types; import com.google.common.collect.ImmutableList; import com.yahoo.processing.request.CompoundName; import com.yahoo.search.query.profile.QueryProfile; -import com.yahoo.tensor.TensorType; import java.util.Arrays; -import java.util.Collections; import java.util.List; /** diff --git a/container-search/src/main/java/com/yahoo/search/query/ranking/RankFeatures.java b/container-search/src/main/java/com/yahoo/search/query/ranking/RankFeatures.java index 7d43c09f6fb..1a4ecb4ecd8 100644 --- a/container-search/src/main/java/com/yahoo/search/query/ranking/RankFeatures.java +++ b/container-search/src/main/java/com/yahoo/search/query/ranking/RankFeatures.java @@ -2,6 +2,8 @@ package com.yahoo.search.query.ranking; import com.yahoo.fs4.MapEncoder; +import com.yahoo.search.Query; +import com.yahoo.search.query.Ranking; import com.yahoo.tensor.Tensor; import com.yahoo.text.JSON; @@ -20,13 +22,21 @@ import java.util.OptionalDouble; */ public class RankFeatures implements Cloneable { + private final Ranking parent; private final Map<String, Object> features; + /** @deprecated pass a query */ + @Deprecated // TODO: Remove on Vespa 8 public RankFeatures() { - this(new LinkedHashMap<>()); + this(new Ranking(new Query())); + } + + public RankFeatures(Ranking parent) { + this(parent, new LinkedHashMap<>()); } - private RankFeatures(Map<String, Object> features) { + private RankFeatures(Ranking parent, Map<String, Object> features) { + this.parent = parent; this.features = features; } @@ -148,7 +158,11 @@ public class RankFeatures implements Cloneable { @Override public RankFeatures clone() { - return new RankFeatures(new LinkedHashMap<>(features)); + return new RankFeatures(parent, new LinkedHashMap<>(features)); + } + + public RankFeatures cloneFor(Ranking parent) { + return new RankFeatures(parent, new LinkedHashMap<>(features)); } @Override 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 a1556aac189..20678f3b7bb 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 @@ -20,6 +20,7 @@ import com.yahoo.search.query.profile.types.FieldType; import com.yahoo.search.query.profile.types.QueryProfileType; import com.yahoo.search.query.profile.types.QueryProfileTypeRegistry; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import java.net.URLEncoder; @@ -423,6 +424,49 @@ public class QueryProfileTypeTestCase { assertEquals(Tensor.from(tensorString), query.getRanking().getFeatures().getTensor("query(myTensor1)").get()); } + @Test + public void testTensorRankFeatureSetProgrammatically() { + QueryProfile profile = new QueryProfile("test"); + profile.setType(testtype); + registry.register(profile); + + CompiledQueryProfileRegistry cRegistry = registry.compile(); + String tensorString = "{{a:a1, b:b1}:1.0, {a:a2, b:b1}:2.0}}"; + Query query = new Query(HttpRequest.createTestRequest("?", com.yahoo.jdisc.http.HttpRequest.Method.GET), + cRegistry.getComponent("test")); + query.properties().set("ranking.features.query(myTensor1)", Tensor.from(tensorString)); + assertEquals(Tensor.from(tensorString), query.getRanking().getFeatures().getTensor("query(myTensor1)").get()); + } + + @Test + @Ignore + public void testTensorRankFeatureSetProgrammaticallyWithWrongType() { + QueryProfile profile = new QueryProfile("test"); + profile.setType(testtype); + registry.register(profile); + + CompiledQueryProfileRegistry cRegistry = registry.compile(); + String tensorString = "tensor(x[3]):[0.1, 0.2, 0.3]"; + Query query = new Query(HttpRequest.createTestRequest("?", com.yahoo.jdisc.http.HttpRequest.Method.GET), + cRegistry.getComponent("test")); + try { + query.getRanking().getFeatures().put("query(myTensor1)",Tensor.from(tensorString)); + fail("Expected exception"); + } + catch (IllegalArgumentException e) { + assertEquals("'query(myTensor1)' must be of type tensor(a{},b{}) but was of type tensor(x[3])", + e.getMessage()); + } + try { + query.properties().set("ranking.features.query(myTensor1)", Tensor.from(tensorString)); + fail("Expected exception"); + } + catch (IllegalArgumentException e) { + assertEquals("'query(myTensor1)' must be of type tensor(a{},b{}) but was of type tensor(x[3])", + e.getMessage()); + } + } + // Expected to work exactly as testTensorRankFeatureInRequest @Test public void testTensorRankFeatureInRequestWithInheritedQueryProfileType() { diff --git a/container-search/src/test/java/com/yahoo/search/query/test/RankFeaturesTestCase.java b/container-search/src/test/java/com/yahoo/search/query/test/RankFeaturesTestCase.java index 8848e33c365..0644a2b02e8 100644 --- a/container-search/src/test/java/com/yahoo/search/query/test/RankFeaturesTestCase.java +++ b/container-search/src/test/java/com/yahoo/search/query/test/RankFeaturesTestCase.java @@ -2,6 +2,8 @@ package com.yahoo.search.query.test; import com.yahoo.io.GrowableByteBuffer; +import com.yahoo.search.Query; +import com.yahoo.search.query.Ranking; import com.yahoo.search.query.ranking.RankFeatures; import com.yahoo.search.query.ranking.RankProperties; import com.yahoo.tensor.Tensor; @@ -36,11 +38,11 @@ public class RankFeaturesTestCase { @Test @SuppressWarnings("deprecation") public void requireThatRankFeaturesUsingDoubleAndDoubleToStringEncodeTheSameWay() { - RankFeatures withDouble = new RankFeatures(); + RankFeatures withDouble = new RankFeatures(new Ranking(new Query())); withDouble.put("query(myDouble)", 3.8); assertEquals(3.8, withDouble.getDouble("query(myDouble)").getAsDouble(), 0.000001); - RankFeatures withString = new RankFeatures(); + RankFeatures withString = new RankFeatures(new Ranking(new Query())); withString.put("query(myDouble)", String.valueOf(3.8)); RankProperties withDoubleP = new RankProperties(); @@ -99,7 +101,7 @@ public class RankFeaturesTestCase { } private static RankProperties createRankPropertiesWithTensors(List<Entry> entries) { - RankFeatures features = new RankFeatures(); + RankFeatures features = new RankFeatures(new Ranking(new Query())); for (Entry entry : entries) { features.put(entry.key, entry.tensor); } |