This is an automated email from the ASF dual-hosted git repository.
cpoerschke pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/main by this push:
new e413210646b SOLR-16994: facilitate date-related FieldValueFeature
extensions (docValues=True only) (#1950)
e413210646b is described below
commit e413210646bfdf1d5bc342d5b52ae5a4f0239c70
Author: Christine Poerschke <[email protected]>
AuthorDate: Mon Oct 2 11:48:23 2023 +0100
SOLR-16994: facilitate date-related FieldValueFeature extensions
(docValues=True only) (#1950)
---
.../apache/solr/ltr/feature/FieldValueFeature.java | 13 +++
.../solr/ltr/feature/TestFieldValueFeature.java | 109 +++++++++++++++++++++
2 files changed, 122 insertions(+)
diff --git
a/solr/modules/ltr/src/java/org/apache/solr/ltr/feature/FieldValueFeature.java
b/solr/modules/ltr/src/java/org/apache/solr/ltr/feature/FieldValueFeature.java
index bab34fa8d3e..583e080954c 100644
---
a/solr/modules/ltr/src/java/org/apache/solr/ltr/feature/FieldValueFeature.java
+++
b/solr/modules/ltr/src/java/org/apache/solr/ltr/feature/FieldValueFeature.java
@@ -129,6 +129,17 @@ public class FieldValueFeature extends Feature {
}
}
+ /**
+ * Override this method in sub classes that wish to use not an absolute
time but an interval
+ * such as document age or remaining shelf life relative to a specific
date or relative to now.
+ *
+ * @param val value of the field
+ * @return value after transformation
+ */
+ protected long readNumericDocValuesDate(long val) {
+ return val;
+ }
+
/**
* Return a FeatureScorer that uses docValues or storedFields if no
docValues are present
*
@@ -261,6 +272,8 @@ public class FieldValueFeature extends Feature {
} else if (NumberType.DOUBLE.equals(numberType)) {
// handle double value conversion
return (float) Double.longBitsToDouble(docValues.longValue());
+ } else if (NumberType.DATE.equals(numberType)) {
+ return readNumericDocValuesDate(docValues.longValue());
}
// just take the long value
return docValues.longValue();
diff --git
a/solr/modules/ltr/src/test/org/apache/solr/ltr/feature/TestFieldValueFeature.java
b/solr/modules/ltr/src/test/org/apache/solr/ltr/feature/TestFieldValueFeature.java
index 8bed4efe7ed..b10d9d7f952 100644
---
a/solr/modules/ltr/src/test/org/apache/solr/ltr/feature/TestFieldValueFeature.java
+++
b/solr/modules/ltr/src/test/org/apache/solr/ltr/feature/TestFieldValueFeature.java
@@ -17,8 +17,10 @@
package org.apache.solr.ltr.feature;
import java.io.IOException;
+import java.time.Instant;
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.concurrent.TimeUnit;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
@@ -692,6 +694,113 @@ public class TestFieldValueFeature extends TestRerankBase
{
}
}
+ public static class RelativeDateFieldValueFeature extends FieldValueFeature {
+
+ private boolean since = false;
+ private boolean until = false;
+
+ public boolean getSince() {
+ return this.since;
+ }
+
+ public void setSince(boolean since) {
+ this.since = since;
+ }
+
+ public boolean getUntil() {
+ return this.until;
+ }
+
+ public void setUntil(boolean until) {
+ this.until = until;
+ }
+
+ public RelativeDateFieldValueFeature(String name, Map<String, Object>
params) {
+ super(name, params);
+ }
+
+ @Override
+ protected void validate() throws FeatureException {
+ if (since != until) {
+ return;
+ }
+ throw new FeatureException(
+ getClass().getSimpleName() + ": exactly one of 'since' and 'until'
must be provided");
+ }
+
+ @Override
+ public FeatureWeight createWeight(
+ IndexSearcher searcher,
+ boolean needsScores,
+ SolrQueryRequest request,
+ Query originalQuery,
+ Map<String, String[]> efi)
+ throws IOException {
+ return new FieldValueFeatureWeight(searcher, request, originalQuery,
efi) {
+ private final long timeZero =
Instant.parse("2000-01-01T00:00:00.000Z").toEpochMilli();
+
+ @Override
+ public long readNumericDocValuesDate(long val) {
+ if (since) return TimeUnit.MILLISECONDS.toMinutes(val -
this.timeZero);
+ if (until) return TimeUnit.MILLISECONDS.toMinutes(this.timeZero -
val);
+ return 0;
+ }
+ };
+ }
+ }
+
+ @Test
+ public void testRelativeDateFieldValueFeature() throws Exception {
+ final String field = "dvDateField";
+ for (boolean since : new boolean[] {false, true}) {
+ final String[][] inputsAndTests = {
+ new String[] {
+ "2000-01-01T00:00:00.000Z",
+ "/response/docs/[0]/=={'[fv]':'"
+ + FeatureLoggerTestUtils.toFeatureVector(field, "0.0")
+ + "'}"
+ },
+ new String[] {
+ "2000-01-01T00:01:02.003Z",
+ "/response/docs/[0]/=={'[fv]':'"
+ + FeatureLoggerTestUtils.toFeatureVector(field, (since ? "1.0" :
"-1.0"))
+ + "'}"
+ },
+ new String[] {
+ "2000-01-01T01:02:03.004Z",
+ "/response/docs/[0]/=={'[fv]':'"
+ + FeatureLoggerTestUtils.toFeatureVector(field, (since ? "62.0"
: "-62.0"))
+ + "'}"
+ }
+ };
+
+ final String fstore = "testRelativeDateFieldValueFeature" + field + "_"
+ since;
+ final String model = fstore + "-model";
+ loadFeature(
+ field,
+ RelativeDateFieldValueFeature.class.getName(),
+ fstore,
+ "{\"field\":\"" + field + "\", \"" + (since ? "since" : "until") +
"\": true}");
+ loadModel(
+ model,
+ LinearModel.class.getName(),
+ new String[] {field},
+ fstore,
+ "{\"weights\":{\"" + field + "\":1.0}}");
+
+ for (String[] inputAndTest : inputsAndTests) {
+ assertU(adoc("id", "21", field, inputAndTest[0]));
+ assertU(commit());
+
+ final SolrQuery query = new SolrQuery("id:21");
+ query.add("rq", "{!ltr model=" + model + " reRankDocs=4}");
+ query.add("fl", "[fv]");
+
+ assertJQ("/query" + query.toQueryString(), inputAndTest[1]);
+ }
+ }
+ }
+
/**
* This class is used to track which specific FieldValueFeature is used so
that we can test,
* whether the fallback mechanism works correctly.