SOLR-10303: Renamed to DatePartEvaluator and adding support for Instant, Date, LocalDateTime

This commit is contained in:
Gethin James 2017-03-17 15:30:12 +01:00 committed by Joel Bernstein
parent 24ab117a41
commit b13945b1ef
3 changed files with 91 additions and 26 deletions

View File

@ -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.CosineEvaluator;
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.EqualsEvaluator;
import org.apache.solr.client.solrj.io.eval.ExclusiveOrEvaluator;
@ -256,8 +256,8 @@ public class StreamHandler extends RequestHandlerBase implements SolrCoreAware,
;
// Date evaluators
for (DateEvaluator.FUNCTION function:DateEvaluator.FUNCTION.values()) {
streamFactory.withFunctionName(function.toString(), DateEvaluator.class);
for (DatePartEvaluator.FUNCTION function: DatePartEvaluator.FUNCTION.values()) {
streamFactory.withFunctionName(function.toString(), DatePartEvaluator.class);
}
// This pulls all the overrides and additions from the config

View File

@ -24,6 +24,7 @@ import java.time.ZoneOffset;
import java.time.format.DateTimeParseException;
import java.time.temporal.IsoFields;
import java.util.Arrays;
import java.util.Date;
import java.util.Locale;
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
*/
public class DateEvaluator extends NumberEvaluator {
public class DatePartEvaluator extends NumberEvaluator {
public enum FUNCTION {year, month, day, dayofyear, dayofquarter, hour, minute, quarter, week, second, epoch};
private FUNCTION function;
private String fieldName;
public DateEvaluator(StreamExpression expression, StreamFactory factory) throws IOException {
public DatePartEvaluator(StreamExpression expression, StreamFactory factory) throws IOException {
super(expression, factory);
String functionName = expression.getFunctionName();
@ -65,24 +66,46 @@ public class DateEvaluator extends NumberEvaluator {
}
}
//TODO: Support non-string, eg. java.util.date or instant
@Override
public Number evaluate(Tuple tuple) throws IOException {
try {
String dateStr = (String) tuple.get(fieldName);
if (dateStr != null && !dateStr.isEmpty()) {
Instant instant = Instant.parse(dateStr);
if (function.equals(FUNCTION.epoch)) return instant.toEpochMilli();
Object fieldValue = tuple.get(fieldName);
Instant instant = null;
LocalDateTime date = null;
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);
}
} 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 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;
}

View File

@ -18,12 +18,17 @@
package org.apache.solr.client.solrj.io.stream.eval;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Map;
import java.util.TimeZone;
import org.apache.commons.collections.map.HashedMap;
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.stream.expr.Explanation;
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
*/
public class DateEvaluatorTest {
public class DatePartEvaluatorTest {
StreamFactory factory;
Map<String, Object> values;
public DateEvaluatorTest() {
public DatePartEvaluatorTest() {
super();
factory = new StreamFactory();
factory.withFunctionName("nope", DateEvaluator.class);
for (DateEvaluator.FUNCTION function : DateEvaluator.FUNCTION.values()) {
factory.withFunctionName(function.toString(), DateEvaluator.class);
factory.withFunctionName("nope", DatePartEvaluator.class);
for (DatePartEvaluator.FUNCTION function : DatePartEvaluator.FUNCTION.values()) {
factory.withFunctionName(function.toString(), DatePartEvaluator.class);
}
values = new HashedMap();
}
@ -104,7 +109,7 @@ public class DateEvaluatorTest {
Object result = evaluator.evaluate(new Tuple(values));
assertTrue(false);
} 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 {
@ -155,7 +160,7 @@ public class DateEvaluatorTest {
testFunction("epoch(a)", new Date(820454399990l).toInstant().toString(), 820454399990l);
//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)");
values.clear();
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);
values.clear();
values.put("a", value);
@ -177,13 +219,13 @@ public class DateEvaluatorTest {
@Test
public void testExplain() throws IOException {
StreamExpression express = StreamExpressionParser.parse("month('myfield')");
DateEvaluator dateEvaluator = new DateEvaluator(express,factory);
Explanation explain = dateEvaluator.toExplanation(factory);
DatePartEvaluator datePartEvaluator = new DatePartEvaluator(express,factory);
Explanation explain = datePartEvaluator.toExplanation(factory);
assertEquals("month(myfield)", explain.getExpression());
express = StreamExpressionParser.parse("day(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbb)");
dateEvaluator = new DateEvaluator(express,factory);
explain = dateEvaluator.toExplanation(factory);
datePartEvaluator = new DatePartEvaluator(express,factory);
explain = datePartEvaluator.toExplanation(factory);
assertEquals("day(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbb)", explain.getExpression());
}
}