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.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

View File

@ -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;
} }

View File

@ -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());
} }
} }