mirror of https://github.com/apache/lucene.git
SOLR-10303: Renamed to DatePartEvaluator and adding support for Instant, Date, LocalDateTime
This commit is contained in:
parent
24ab117a41
commit
b13945b1ef
|
@ -43,7 +43,7 @@ import org.apache.solr.client.solrj.io.eval.CeilingEvaluator;
|
||||||
import org.apache.solr.client.solrj.io.eval.CoalesceEvaluator;
|
import org.apache.solr.client.solrj.io.eval.CoalesceEvaluator;
|
||||||
import org.apache.solr.client.solrj.io.eval.CosineEvaluator;
|
import org.apache.solr.client.solrj.io.eval.CosineEvaluator;
|
||||||
import org.apache.solr.client.solrj.io.eval.CubedRootEvaluator;
|
import org.apache.solr.client.solrj.io.eval.CubedRootEvaluator;
|
||||||
import org.apache.solr.client.solrj.io.eval.DateEvaluator;
|
import org.apache.solr.client.solrj.io.eval.DatePartEvaluator;
|
||||||
import org.apache.solr.client.solrj.io.eval.DivideEvaluator;
|
import org.apache.solr.client.solrj.io.eval.DivideEvaluator;
|
||||||
import org.apache.solr.client.solrj.io.eval.EqualsEvaluator;
|
import org.apache.solr.client.solrj.io.eval.EqualsEvaluator;
|
||||||
import org.apache.solr.client.solrj.io.eval.ExclusiveOrEvaluator;
|
import org.apache.solr.client.solrj.io.eval.ExclusiveOrEvaluator;
|
||||||
|
@ -256,8 +256,8 @@ public class StreamHandler extends RequestHandlerBase implements SolrCoreAware,
|
||||||
;
|
;
|
||||||
|
|
||||||
// Date evaluators
|
// Date evaluators
|
||||||
for (DateEvaluator.FUNCTION function:DateEvaluator.FUNCTION.values()) {
|
for (DatePartEvaluator.FUNCTION function: DatePartEvaluator.FUNCTION.values()) {
|
||||||
streamFactory.withFunctionName(function.toString(), DateEvaluator.class);
|
streamFactory.withFunctionName(function.toString(), DatePartEvaluator.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This pulls all the overrides and additions from the config
|
// This pulls all the overrides and additions from the config
|
||||||
|
|
|
@ -24,6 +24,7 @@ import java.time.ZoneOffset;
|
||||||
import java.time.format.DateTimeParseException;
|
import java.time.format.DateTimeParseException;
|
||||||
import java.time.temporal.IsoFields;
|
import java.time.temporal.IsoFields;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import org.apache.solr.client.solrj.io.Tuple;
|
import org.apache.solr.client.solrj.io.Tuple;
|
||||||
|
@ -35,14 +36,14 @@ import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;
|
||||||
/**
|
/**
|
||||||
* Provides numeric Date/Time stream evaluators
|
* Provides numeric Date/Time stream evaluators
|
||||||
*/
|
*/
|
||||||
public class DateEvaluator extends NumberEvaluator {
|
public class DatePartEvaluator extends NumberEvaluator {
|
||||||
|
|
||||||
public enum FUNCTION {year, month, day, dayofyear, dayofquarter, hour, minute, quarter, week, second, epoch};
|
public enum FUNCTION {year, month, day, dayofyear, dayofquarter, hour, minute, quarter, week, second, epoch};
|
||||||
|
|
||||||
private FUNCTION function;
|
private FUNCTION function;
|
||||||
private String fieldName;
|
private String fieldName;
|
||||||
|
|
||||||
public DateEvaluator(StreamExpression expression, StreamFactory factory) throws IOException {
|
public DatePartEvaluator(StreamExpression expression, StreamFactory factory) throws IOException {
|
||||||
super(expression, factory);
|
super(expression, factory);
|
||||||
|
|
||||||
String functionName = expression.getFunctionName();
|
String functionName = expression.getFunctionName();
|
||||||
|
@ -65,24 +66,46 @@ public class DateEvaluator extends NumberEvaluator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Support non-string, eg. java.util.date or instant
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Number evaluate(Tuple tuple) throws IOException {
|
public Number evaluate(Tuple tuple) throws IOException {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String dateStr = (String) tuple.get(fieldName);
|
Object fieldValue = tuple.get(fieldName);
|
||||||
if (dateStr != null && !dateStr.isEmpty()) {
|
Instant instant = null;
|
||||||
Instant instant = Instant.parse(dateStr);
|
LocalDateTime date = null;
|
||||||
if (function.equals(FUNCTION.epoch)) return instant.toEpochMilli();
|
|
||||||
|
|
||||||
LocalDateTime date = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);
|
if (fieldValue == null) return null;
|
||||||
|
|
||||||
|
if (fieldValue instanceof String) {
|
||||||
|
instant = getInstant((String)fieldValue);
|
||||||
|
} else if (fieldValue instanceof Instant) {
|
||||||
|
instant = (Instant) fieldValue;
|
||||||
|
} else if (fieldValue instanceof Date) {
|
||||||
|
instant = ((Date) fieldValue).toInstant();
|
||||||
|
} else if (fieldValue instanceof LocalDateTime) {
|
||||||
|
date = ((LocalDateTime) fieldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instant != null) {
|
||||||
|
if (function.equals(FUNCTION.epoch)) return instant.toEpochMilli();
|
||||||
|
date = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (date != null) {
|
||||||
return evaluate(date);
|
return evaluate(date);
|
||||||
}
|
}
|
||||||
} catch (ClassCastException | DateTimeParseException e) {
|
|
||||||
|
} catch (DateTimeParseException e) {
|
||||||
throw new IOException(String.format(Locale.ROOT,"Invalid field %s - The field must be a string formatted in the ISO_INSTANT date format.",fieldName));
|
throw new IOException(String.format(Locale.ROOT,"Invalid field %s - The field must be a string formatted in the ISO_INSTANT date format.",fieldName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw new IOException(String.format(Locale.ROOT,"Invalid field %s - The field must be a string formatted ISO_INSTANT or of type Instant,Date or LocalDateTime.",fieldName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Instant getInstant(String dateStr) {
|
||||||
|
if (dateStr != null && !dateStr.isEmpty()) {
|
||||||
|
return Instant.parse(dateStr);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,12 +18,17 @@
|
||||||
package org.apache.solr.client.solrj.io.stream.eval;
|
package org.apache.solr.client.solrj.io.stream.eval;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
import org.apache.commons.collections.map.HashedMap;
|
import org.apache.commons.collections.map.HashedMap;
|
||||||
import org.apache.solr.client.solrj.io.Tuple;
|
import org.apache.solr.client.solrj.io.Tuple;
|
||||||
import org.apache.solr.client.solrj.io.eval.DateEvaluator;
|
import org.apache.solr.client.solrj.io.eval.DatePartEvaluator;
|
||||||
import org.apache.solr.client.solrj.io.eval.StreamEvaluator;
|
import org.apache.solr.client.solrj.io.eval.StreamEvaluator;
|
||||||
import org.apache.solr.client.solrj.io.stream.expr.Explanation;
|
import org.apache.solr.client.solrj.io.stream.expr.Explanation;
|
||||||
import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
|
import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
|
||||||
|
@ -39,20 +44,20 @@ import static junit.framework.Assert.assertTrue;
|
||||||
/**
|
/**
|
||||||
* Tests numeric Date/Time stream evaluators
|
* Tests numeric Date/Time stream evaluators
|
||||||
*/
|
*/
|
||||||
public class DateEvaluatorTest {
|
public class DatePartEvaluatorTest {
|
||||||
|
|
||||||
|
|
||||||
StreamFactory factory;
|
StreamFactory factory;
|
||||||
Map<String, Object> values;
|
Map<String, Object> values;
|
||||||
|
|
||||||
public DateEvaluatorTest() {
|
public DatePartEvaluatorTest() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
factory = new StreamFactory();
|
factory = new StreamFactory();
|
||||||
|
|
||||||
factory.withFunctionName("nope", DateEvaluator.class);
|
factory.withFunctionName("nope", DatePartEvaluator.class);
|
||||||
for (DateEvaluator.FUNCTION function : DateEvaluator.FUNCTION.values()) {
|
for (DatePartEvaluator.FUNCTION function : DatePartEvaluator.FUNCTION.values()) {
|
||||||
factory.withFunctionName(function.toString(), DateEvaluator.class);
|
factory.withFunctionName(function.toString(), DatePartEvaluator.class);
|
||||||
}
|
}
|
||||||
values = new HashedMap();
|
values = new HashedMap();
|
||||||
}
|
}
|
||||||
|
@ -104,7 +109,7 @@ public class DateEvaluatorTest {
|
||||||
Object result = evaluator.evaluate(new Tuple(values));
|
Object result = evaluator.evaluate(new Tuple(values));
|
||||||
assertTrue(false);
|
assertTrue(false);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertEquals("Invalid field a - The field must be a string formatted in the ISO_INSTANT date format.", e.getMessage());
|
assertEquals("Invalid field a - The field must be a string formatted ISO_INSTANT or of type Instant,Date or LocalDateTime.", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -155,7 +160,7 @@ public class DateEvaluatorTest {
|
||||||
testFunction("epoch(a)", new Date(820454399990l).toInstant().toString(), 820454399990l);
|
testFunction("epoch(a)", new Date(820454399990l).toInstant().toString(), 820454399990l);
|
||||||
|
|
||||||
//Additionally test all functions to make sure they return a non-null number
|
//Additionally test all functions to make sure they return a non-null number
|
||||||
for (DateEvaluator.FUNCTION function : DateEvaluator.FUNCTION.values()) {
|
for (DatePartEvaluator.FUNCTION function : DatePartEvaluator.FUNCTION.values()) {
|
||||||
StreamEvaluator evaluator = factory.constructEvaluator(function+"(a)");
|
StreamEvaluator evaluator = factory.constructEvaluator(function+"(a)");
|
||||||
values.clear();
|
values.clear();
|
||||||
values.put("a", "2017-03-17T10:30:45Z");
|
values.put("a", "2017-03-17T10:30:45Z");
|
||||||
|
@ -165,7 +170,44 @@ public class DateEvaluatorTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testFunction(String expression, String value, Number expected) throws Exception {
|
@Test
|
||||||
|
public void testFunctionsOnDate() throws Exception {
|
||||||
|
Calendar calendar = new GregorianCalendar(2017,12,5, 23, 59);
|
||||||
|
calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
|
Date aDate = calendar.getTime();
|
||||||
|
testFunction("year(a)", aDate, calendar.get(Calendar.YEAR));
|
||||||
|
testFunction("month(a)", aDate, calendar.get(Calendar.MONTH)+1);
|
||||||
|
testFunction("day(a)", aDate, calendar.get(Calendar.DAY_OF_MONTH));
|
||||||
|
testFunction("hour(a)", aDate, calendar.get(Calendar.HOUR_OF_DAY));
|
||||||
|
testFunction("minute(a)", aDate, calendar.get(Calendar.MINUTE));
|
||||||
|
testFunction("epoch(a)", aDate, aDate.getTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFunctionsOnInstant() throws Exception {
|
||||||
|
Calendar calendar = new GregorianCalendar(2017,12,5, 23, 59);
|
||||||
|
calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
|
Date aDate = calendar.getTime();
|
||||||
|
Instant instant = aDate.toInstant();
|
||||||
|
testFunction("year(a)", instant, calendar.get(Calendar.YEAR));
|
||||||
|
testFunction("month(a)", instant, calendar.get(Calendar.MONTH)+1);
|
||||||
|
testFunction("day(a)", instant, calendar.get(Calendar.DAY_OF_MONTH));
|
||||||
|
testFunction("hour(a)", instant, calendar.get(Calendar.HOUR_OF_DAY));
|
||||||
|
testFunction("minute(a)", instant, calendar.get(Calendar.MINUTE));
|
||||||
|
testFunction("epoch(a)", instant, aDate.getTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFunctionsLocalDateTime() throws Exception {
|
||||||
|
LocalDateTime localDateTime = LocalDateTime.of(2017,12,5, 23, 59);
|
||||||
|
testFunction("year(a)", localDateTime, 2017);
|
||||||
|
testFunction("month(a)", localDateTime, 12);
|
||||||
|
testFunction("day(a)", localDateTime, 5);
|
||||||
|
testFunction("hour(a)", localDateTime, 23);
|
||||||
|
testFunction("minute(a)", localDateTime, 59);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testFunction(String expression, Object value, Number expected) throws Exception {
|
||||||
StreamEvaluator evaluator = factory.constructEvaluator(expression);
|
StreamEvaluator evaluator = factory.constructEvaluator(expression);
|
||||||
values.clear();
|
values.clear();
|
||||||
values.put("a", value);
|
values.put("a", value);
|
||||||
|
@ -177,13 +219,13 @@ public class DateEvaluatorTest {
|
||||||
@Test
|
@Test
|
||||||
public void testExplain() throws IOException {
|
public void testExplain() throws IOException {
|
||||||
StreamExpression express = StreamExpressionParser.parse("month('myfield')");
|
StreamExpression express = StreamExpressionParser.parse("month('myfield')");
|
||||||
DateEvaluator dateEvaluator = new DateEvaluator(express,factory);
|
DatePartEvaluator datePartEvaluator = new DatePartEvaluator(express,factory);
|
||||||
Explanation explain = dateEvaluator.toExplanation(factory);
|
Explanation explain = datePartEvaluator.toExplanation(factory);
|
||||||
assertEquals("month(myfield)", explain.getExpression());
|
assertEquals("month(myfield)", explain.getExpression());
|
||||||
|
|
||||||
express = StreamExpressionParser.parse("day(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbb)");
|
express = StreamExpressionParser.parse("day(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbb)");
|
||||||
dateEvaluator = new DateEvaluator(express,factory);
|
datePartEvaluator = new DatePartEvaluator(express,factory);
|
||||||
explain = dateEvaluator.toExplanation(factory);
|
explain = datePartEvaluator.toExplanation(factory);
|
||||||
assertEquals("day(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbb)", explain.getExpression());
|
assertEquals("day(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbb)", explain.getExpression());
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue