Make dates be ReadableDateTimes in scripts (#22948)
Instead of longs. If you want millis since epoch you can call doc.date_field.value.millis. Relates to #22875
This commit is contained in:
parent
bc884c1e7b
commit
0d6e622242
|
@ -30,7 +30,7 @@ public interface AtomicFieldData extends Accountable, Releasable {
|
||||||
/**
|
/**
|
||||||
* Returns a "scripting" based values.
|
* Returns a "scripting" based values.
|
||||||
*/
|
*/
|
||||||
ScriptDocValues getScriptValues();
|
ScriptDocValues<?> getScriptValues();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a String representation of the values.
|
* Return a String representation of the values.
|
||||||
|
|
|
@ -27,6 +27,7 @@ public interface IndexNumericFieldData extends IndexFieldData<AtomicNumericField
|
||||||
SHORT(false),
|
SHORT(false),
|
||||||
INT(false),
|
INT(false),
|
||||||
LONG(false),
|
LONG(false),
|
||||||
|
DATE(false),
|
||||||
HALF_FLOAT(true),
|
HALF_FLOAT(true),
|
||||||
FLOAT(true),
|
FLOAT(true),
|
||||||
DOUBLE(true);
|
DOUBLE(true);
|
||||||
|
|
|
@ -25,6 +25,8 @@ import org.apache.lucene.util.BytesRef;
|
||||||
import org.elasticsearch.common.geo.GeoHashUtils;
|
import org.elasticsearch.common.geo.GeoHashUtils;
|
||||||
import org.elasticsearch.common.geo.GeoPoint;
|
import org.elasticsearch.common.geo.GeoPoint;
|
||||||
import org.elasticsearch.common.geo.GeoUtils;
|
import org.elasticsearch.common.geo.GeoUtils;
|
||||||
|
import org.elasticsearch.common.logging.DeprecationLogger;
|
||||||
|
import org.elasticsearch.common.logging.ESLoggerFactory;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
import org.joda.time.DateTimeZone;
|
import org.joda.time.DateTimeZone;
|
||||||
import org.joda.time.MutableDateTime;
|
import org.joda.time.MutableDateTime;
|
||||||
|
@ -41,7 +43,6 @@ import java.util.function.UnaryOperator;
|
||||||
* and a <code>getValues</code> that return the relevant type that then can be used in scripts.
|
* and a <code>getValues</code> that return the relevant type that then can be used in scripts.
|
||||||
*/
|
*/
|
||||||
public abstract class ScriptDocValues<T> extends AbstractList<T> {
|
public abstract class ScriptDocValues<T> extends AbstractList<T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the current doc ID.
|
* Set the current doc ID.
|
||||||
*/
|
*/
|
||||||
|
@ -127,6 +128,7 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class Longs extends ScriptDocValues<Long> {
|
public static final class Longs extends ScriptDocValues<Long> {
|
||||||
|
protected static final DeprecationLogger deprecationLogger = new DeprecationLogger(ESLoggerFactory.getLogger(Longs.class));
|
||||||
|
|
||||||
private final SortedNumericDocValues values;
|
private final SortedNumericDocValues values;
|
||||||
private Dates dates;
|
private Dates dates;
|
||||||
|
@ -155,7 +157,9 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
|
||||||
return values.valueAt(0);
|
return values.valueAt(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public ReadableDateTime getDate() {
|
public ReadableDateTime getDate() {
|
||||||
|
deprecationLogger.deprecated("getDate on numeric fields is deprecated. Use a date field to get dates.");
|
||||||
if (dates == null) {
|
if (dates == null) {
|
||||||
dates = new Dates(values);
|
dates = new Dates(values);
|
||||||
dates.refreshArray();
|
dates.refreshArray();
|
||||||
|
@ -163,7 +167,9 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
|
||||||
return dates.getValue();
|
return dates.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public List<ReadableDateTime> getDates() {
|
public List<ReadableDateTime> getDates() {
|
||||||
|
deprecationLogger.deprecated("getDates on numeric fields is deprecated. Use a date field to get dates.");
|
||||||
if (dates == null) {
|
if (dates == null) {
|
||||||
dates = new Dates(values);
|
dates = new Dates(values);
|
||||||
dates.refreshArray();
|
dates.refreshArray();
|
||||||
|
@ -183,6 +189,8 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class Dates extends ScriptDocValues<ReadableDateTime> {
|
public static final class Dates extends ScriptDocValues<ReadableDateTime> {
|
||||||
|
protected static final DeprecationLogger deprecationLogger = new DeprecationLogger(ESLoggerFactory.getLogger(Dates.class));
|
||||||
|
|
||||||
private static final ReadableDateTime EPOCH = new DateTime(0, DateTimeZone.UTC);
|
private static final ReadableDateTime EPOCH = new DateTime(0, DateTimeZone.UTC);
|
||||||
|
|
||||||
private final SortedNumericDocValues values;
|
private final SortedNumericDocValues values;
|
||||||
|
@ -206,6 +214,24 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
|
||||||
return get(0);
|
return get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the first value. Added for backwards compatibility with 5.x when date fields were {@link Longs}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public ReadableDateTime getDate() {
|
||||||
|
deprecationLogger.deprecated("getDate is no longer necisary on date fields as the value is now a date.");
|
||||||
|
return getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all the values. Added for backwards compatibility with 5.x when date fields were {@link Longs}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public List<ReadableDateTime> getDates() {
|
||||||
|
deprecationLogger.deprecated("getDates is no longer necisary on date fields as the values are now dates.");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ReadableDateTime get(int index) {
|
public ReadableDateTime get(int index) {
|
||||||
if (index >= values.count()) {
|
if (index >= values.count()) {
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.elasticsearch.index.fielddata.plain;
|
||||||
|
|
||||||
import org.elasticsearch.index.fielddata.AtomicNumericFieldData;
|
import org.elasticsearch.index.fielddata.AtomicNumericFieldData;
|
||||||
import org.elasticsearch.index.fielddata.FieldData;
|
import org.elasticsearch.index.fielddata.FieldData;
|
||||||
|
import org.elasticsearch.index.fielddata.IndexNumericFieldData.NumericType;
|
||||||
import org.elasticsearch.index.fielddata.ScriptDocValues;
|
import org.elasticsearch.index.fielddata.ScriptDocValues;
|
||||||
import org.elasticsearch.index.fielddata.SortedBinaryDocValues;
|
import org.elasticsearch.index.fielddata.SortedBinaryDocValues;
|
||||||
import org.elasticsearch.index.fielddata.SortedNumericDoubleValues;
|
import org.elasticsearch.index.fielddata.SortedNumericDoubleValues;
|
||||||
|
@ -31,12 +32,14 @@ import org.elasticsearch.index.fielddata.SortedNumericDoubleValues;
|
||||||
abstract class AtomicLongFieldData implements AtomicNumericFieldData {
|
abstract class AtomicLongFieldData implements AtomicNumericFieldData {
|
||||||
|
|
||||||
private final long ramBytesUsed;
|
private final long ramBytesUsed;
|
||||||
/** True if this numeric data is for a boolean field, and so only has values 0 and 1. */
|
/**
|
||||||
private final boolean isBoolean;
|
* Type of this field. Used to expose appropriate types in {@link #getScriptValues()}.
|
||||||
|
*/
|
||||||
|
private final NumericType numericType;
|
||||||
|
|
||||||
AtomicLongFieldData(long ramBytesUsed, boolean isBoolean) {
|
AtomicLongFieldData(long ramBytesUsed, NumericType numericType) {
|
||||||
this.ramBytesUsed = ramBytesUsed;
|
this.ramBytesUsed = ramBytesUsed;
|
||||||
this.isBoolean = isBoolean;
|
this.numericType = numericType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -45,10 +48,13 @@ abstract class AtomicLongFieldData implements AtomicNumericFieldData {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final ScriptDocValues getScriptValues() {
|
public final ScriptDocValues<?> getScriptValues() {
|
||||||
if (isBoolean) {
|
switch (numericType) {
|
||||||
|
case DATE:
|
||||||
|
return new ScriptDocValues.Dates(getLongValues());
|
||||||
|
case BOOLEAN:
|
||||||
return new ScriptDocValues.Booleans(getLongValues());
|
return new ScriptDocValues.Booleans(getLongValues());
|
||||||
} else {
|
default:
|
||||||
return new ScriptDocValues.Longs(getLongValues());
|
return new ScriptDocValues.Longs(getLongValues());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,7 +96,7 @@ public class SortedNumericDVIndexFieldData extends DocValuesIndexFieldData imple
|
||||||
case DOUBLE:
|
case DOUBLE:
|
||||||
return new SortedNumericDoubleFieldData(reader, field);
|
return new SortedNumericDoubleFieldData(reader, field);
|
||||||
default:
|
default:
|
||||||
return new SortedNumericLongFieldData(reader, field, numericType == NumericType.BOOLEAN);
|
return new SortedNumericLongFieldData(reader, field, numericType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,8 +117,8 @@ public class SortedNumericDVIndexFieldData extends DocValuesIndexFieldData imple
|
||||||
final LeafReader reader;
|
final LeafReader reader;
|
||||||
final String field;
|
final String field;
|
||||||
|
|
||||||
SortedNumericLongFieldData(LeafReader reader, String field, boolean isBoolean) {
|
SortedNumericLongFieldData(LeafReader reader, String field, NumericType numericType) {
|
||||||
super(0L, isBoolean);
|
super(0L, numericType);
|
||||||
this.reader = reader;
|
this.reader = reader;
|
||||||
this.field = field;
|
this.field = field;
|
||||||
}
|
}
|
||||||
|
|
|
@ -376,7 +376,7 @@ public class DateFieldMapper extends FieldMapper {
|
||||||
@Override
|
@Override
|
||||||
public IndexFieldData.Builder fielddataBuilder() {
|
public IndexFieldData.Builder fielddataBuilder() {
|
||||||
failIfNoDocValues();
|
failIfNoDocValues();
|
||||||
return new DocValuesIndexFieldData.Builder().numericType(NumericType.LONG);
|
return new DocValuesIndexFieldData.Builder().numericType(NumericType.DATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -23,10 +23,10 @@ import org.elasticsearch.common.lucene.ScorerAware;
|
||||||
import org.elasticsearch.index.fielddata.SortingNumericDoubleValues;
|
import org.elasticsearch.index.fielddata.SortingNumericDoubleValues;
|
||||||
import org.elasticsearch.script.LeafSearchScript;
|
import org.elasticsearch.script.LeafSearchScript;
|
||||||
import org.elasticsearch.search.aggregations.AggregationExecutionException;
|
import org.elasticsearch.search.aggregations.AggregationExecutionException;
|
||||||
|
import org.joda.time.ReadableInstant;
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link SortingNumericDoubleValues} implementation which is based on a script
|
* {@link SortingNumericDoubleValues} implementation which is based on a script
|
||||||
|
@ -47,36 +47,49 @@ public class ScriptDoubleValues extends SortingNumericDoubleValues implements Sc
|
||||||
|
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
resize(0);
|
resize(0);
|
||||||
}
|
} else if (value instanceof Number) {
|
||||||
|
|
||||||
else if (value instanceof Number) {
|
|
||||||
resize(1);
|
resize(1);
|
||||||
values[0] = ((Number) value).doubleValue();
|
values[0] = ((Number) value).doubleValue();
|
||||||
}
|
} else if (value instanceof ReadableInstant) {
|
||||||
|
resize(1);
|
||||||
else if (value.getClass().isArray()) {
|
values[0] = ((ReadableInstant) value).getMillis();
|
||||||
|
} else if (value.getClass().isArray()) {
|
||||||
resize(Array.getLength(value));
|
resize(Array.getLength(value));
|
||||||
for (int i = 0; i < count(); ++i) {
|
for (int i = 0; i < count(); ++i) {
|
||||||
values[i] = ((Number) Array.get(value, i)).doubleValue();
|
values[i] = toDoubleValue(Array.get(value, i));
|
||||||
}
|
}
|
||||||
}
|
} else if (value instanceof Collection) {
|
||||||
|
|
||||||
else if (value instanceof Collection) {
|
|
||||||
resize(((Collection<?>) value).size());
|
resize(((Collection<?>) value).size());
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (Iterator<?> it = ((Collection<?>) value).iterator(); it.hasNext(); ++i) {
|
for (Object v : (Collection<?>) value) {
|
||||||
values[i] = ((Number) it.next()).doubleValue();
|
values[i++] = toDoubleValue(v);
|
||||||
}
|
}
|
||||||
assert i == count();
|
assert i == count();
|
||||||
}
|
} else {
|
||||||
|
resize(1);
|
||||||
else {
|
values[0] = toDoubleValue(value);
|
||||||
throw new AggregationExecutionException("Unsupported script value [" + value + "]");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sort();
|
sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static double toDoubleValue(Object o) {
|
||||||
|
if (o instanceof Number) {
|
||||||
|
return ((Number) o).doubleValue();
|
||||||
|
} else if (o instanceof ReadableInstant) {
|
||||||
|
// Dates are exposed in scripts as ReadableDateTimes but aggregations want them to be numeric
|
||||||
|
return ((ReadableInstant) o).getMillis();
|
||||||
|
} else if (o instanceof Boolean) {
|
||||||
|
// We do expose boolean fields as boolean in scripts, however aggregations still expect
|
||||||
|
// that scripts return the same internal representation as regular fields, so boolean
|
||||||
|
// values in scripts need to be converted to a number, and the value formatter will
|
||||||
|
// make sure of using true/false in the key_as_string field
|
||||||
|
return ((Boolean) o).booleanValue() ? 1.0 : 0.0;
|
||||||
|
} else {
|
||||||
|
throw new AggregationExecutionException("Unsupported script value [" + o + "], expected a number, date, or boolean");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setScorer(Scorer scorer) {
|
public void setScorer(Scorer scorer) {
|
||||||
script.setScorer(scorer);
|
script.setScorer(scorer);
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.elasticsearch.common.lucene.ScorerAware;
|
||||||
import org.elasticsearch.index.fielddata.SortingNumericDocValues;
|
import org.elasticsearch.index.fielddata.SortingNumericDocValues;
|
||||||
import org.elasticsearch.script.LeafSearchScript;
|
import org.elasticsearch.script.LeafSearchScript;
|
||||||
import org.elasticsearch.search.aggregations.AggregationExecutionException;
|
import org.elasticsearch.search.aggregations.AggregationExecutionException;
|
||||||
|
import org.joda.time.ReadableInstant;
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -77,6 +78,9 @@ public class ScriptLongValues extends SortingNumericDocValues implements ScorerA
|
||||||
private static long toLongValue(Object o) {
|
private static long toLongValue(Object o) {
|
||||||
if (o instanceof Number) {
|
if (o instanceof Number) {
|
||||||
return ((Number) o).longValue();
|
return ((Number) o).longValue();
|
||||||
|
} else if (o instanceof ReadableInstant) {
|
||||||
|
// Dates are exposed in scripts as ReadableDateTimes but aggregations want them to be numeric
|
||||||
|
return ((ReadableInstant) o).getMillis();
|
||||||
} else if (o instanceof Boolean) {
|
} else if (o instanceof Boolean) {
|
||||||
// We do expose boolean fields as boolean in scripts, however aggregations still expect
|
// We do expose boolean fields as boolean in scripts, however aggregations still expect
|
||||||
// that scripts return the same internal representation as regular fields, so boolean
|
// that scripts return the same internal representation as regular fields, so boolean
|
||||||
|
@ -84,7 +88,7 @@ public class ScriptLongValues extends SortingNumericDocValues implements ScorerA
|
||||||
// make sure of using true/false in the key_as_string field
|
// make sure of using true/false in the key_as_string field
|
||||||
return ((Boolean) o).booleanValue() ? 1L : 0L;
|
return ((Boolean) o).booleanValue() ? 1L : 0L;
|
||||||
} else {
|
} else {
|
||||||
throw new AggregationExecutionException("Unsupported script value [" + o + "], expected a number");
|
throw new AggregationExecutionException("Unsupported script value [" + o + "], expected a number, date, or boolean");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,10 +62,12 @@ public final class DocValueFieldsFetchSubPhase implements FetchSubPhase {
|
||||||
}
|
}
|
||||||
MappedFieldType fieldType = context.mapperService().fullName(field);
|
MappedFieldType fieldType = context.mapperService().fullName(field);
|
||||||
if (fieldType != null) {
|
if (fieldType != null) {
|
||||||
|
/* Because this is called once per document we end up creating a new ScriptDocValues for every document which is important
|
||||||
|
* because the values inside ScriptDocValues might be reused for different documents (Dates do this). */
|
||||||
AtomicFieldData data = context.fieldData().getForField(fieldType).load(hitContext.readerContext());
|
AtomicFieldData data = context.fieldData().getForField(fieldType).load(hitContext.readerContext());
|
||||||
ScriptDocValues values = data.getScriptValues();
|
ScriptDocValues<?> values = data.getScriptValues();
|
||||||
values.setNextDocId(hitContext.docId());
|
values.setNextDocId(hitContext.docId());
|
||||||
hitField.values().addAll(values.getValues());
|
hitField.values().addAll(values);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,8 @@ public final class ScriptFieldsFetchSubPhase implements FetchSubPhase {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (ScriptFieldsContext.ScriptField scriptField : context.scriptFields().fields()) {
|
for (ScriptFieldsContext.ScriptField scriptField : context.scriptFields().fields()) {
|
||||||
|
/* Because this is called once per document we end up creating new ScriptDocValues for every document which is important because
|
||||||
|
* the values inside ScriptDocValues might be reused for different documents (Dates do this). */
|
||||||
LeafSearchScript leafScript;
|
LeafSearchScript leafScript;
|
||||||
try {
|
try {
|
||||||
leafScript = scriptField.script().getLeafSearchScript(hitContext.readerContext());
|
leafScript = scriptField.script().getLeafSearchScript(hitContext.readerContext());
|
||||||
|
|
|
@ -80,6 +80,10 @@ public class ScriptDocValuesLongsTests extends ESTestCase {
|
||||||
Exception e = expectThrows(UnsupportedOperationException.class, () -> longs.getDates().add(new DateTime()));
|
Exception e = expectThrows(UnsupportedOperationException.class, () -> longs.getDates().add(new DateTime()));
|
||||||
assertEquals("doc values are unmodifiable", e.getMessage());
|
assertEquals("doc values are unmodifiable", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assertWarnings(
|
||||||
|
"getDate on numeric fields is deprecated. Use a date field to get dates.",
|
||||||
|
"getDates on numeric fields is deprecated. Use a date field to get dates.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private Longs wrap(long[][] values) {
|
private Longs wrap(long[][] values) {
|
||||||
|
|
|
@ -84,7 +84,7 @@ public class DateScriptMocks {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ExecutableScript newScript(Map<String, Object> params) {
|
public ExecutableScript newScript(Map<String, Object> params) {
|
||||||
return new PlusOneMonthScript((String) params.get("fieldname"));
|
return new PlusOneMonthScript();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -104,14 +104,9 @@ public class DateScriptMocks {
|
||||||
public static class PlusOneMonthScript extends AbstractSearchScript {
|
public static class PlusOneMonthScript extends AbstractSearchScript {
|
||||||
|
|
||||||
public static final String NAME = "date_plus_1_month";
|
public static final String NAME = "date_plus_1_month";
|
||||||
private String fieldname;
|
|
||||||
|
|
||||||
private Map<String, Object> vars = new HashMap<>();
|
private Map<String, Object> vars = new HashMap<>();
|
||||||
|
|
||||||
public PlusOneMonthScript(String fieldname) {
|
|
||||||
this.fieldname = fieldname;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setNextVar(String name, Object value) {
|
public void setNextVar(String name, Object value) {
|
||||||
vars.put(name, value);
|
vars.put(name, value);
|
||||||
|
|
|
@ -45,6 +45,7 @@ import org.elasticsearch.search.sort.SortOrder;
|
||||||
import org.elasticsearch.test.ESIntegTestCase;
|
import org.elasticsearch.test.ESIntegTestCase;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
import org.joda.time.DateTimeZone;
|
import org.joda.time.DateTimeZone;
|
||||||
|
import org.joda.time.ReadableDateTime;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -105,8 +106,8 @@ public class SearchFieldsIT extends ESIntegTestCase {
|
||||||
|
|
||||||
scripts.put("doc['date'].date.millis", vars -> {
|
scripts.put("doc['date'].date.millis", vars -> {
|
||||||
Map<?, ?> doc = (Map) vars.get("doc");
|
Map<?, ?> doc = (Map) vars.get("doc");
|
||||||
ScriptDocValues.Longs date = (ScriptDocValues.Longs) doc.get("date");
|
ScriptDocValues.Dates dates = (ScriptDocValues.Dates) doc.get("date");
|
||||||
return date.getDate().getMillis();
|
return dates.getValue().getMillis();
|
||||||
});
|
});
|
||||||
|
|
||||||
scripts.put("_fields['num1'].value", vars -> fieldsScript(vars, "num1"));
|
scripts.put("_fields['num1'].value", vars -> fieldsScript(vars, "num1"));
|
||||||
|
@ -777,6 +778,7 @@ public class SearchFieldsIT extends ESIntegTestCase {
|
||||||
|
|
||||||
client().admin().indices().preparePutMapping().setType("type1").setSource(mapping).execute().actionGet();
|
client().admin().indices().preparePutMapping().setType("type1").setSource(mapping).execute().actionGet();
|
||||||
|
|
||||||
|
ReadableDateTime date = new DateTime(2012, 3, 22, 0, 0, DateTimeZone.UTC);
|
||||||
client().prepareIndex("test", "type1", "1").setSource(jsonBuilder().startObject()
|
client().prepareIndex("test", "type1", "1").setSource(jsonBuilder().startObject()
|
||||||
.field("text_field", "foo")
|
.field("text_field", "foo")
|
||||||
.field("keyword_field", "foo")
|
.field("keyword_field", "foo")
|
||||||
|
@ -786,7 +788,7 @@ public class SearchFieldsIT extends ESIntegTestCase {
|
||||||
.field("long_field", 4L)
|
.field("long_field", 4L)
|
||||||
.field("float_field", 5.0f)
|
.field("float_field", 5.0f)
|
||||||
.field("double_field", 6.0d)
|
.field("double_field", 6.0d)
|
||||||
.field("date_field", Joda.forPattern("dateOptionalTime").printer().print(new DateTime(2012, 3, 22, 0, 0, DateTimeZone.UTC)))
|
.field("date_field", Joda.forPattern("dateOptionalTime").printer().print(date))
|
||||||
.field("boolean_field", true)
|
.field("boolean_field", true)
|
||||||
.field("ip_field", "::1")
|
.field("ip_field", "::1")
|
||||||
.endObject()).execute().actionGet();
|
.endObject()).execute().actionGet();
|
||||||
|
@ -820,7 +822,7 @@ public class SearchFieldsIT extends ESIntegTestCase {
|
||||||
assertThat(searchResponse.getHits().getAt(0).fields().get("long_field").value(), equalTo((Object) 4L));
|
assertThat(searchResponse.getHits().getAt(0).fields().get("long_field").value(), equalTo((Object) 4L));
|
||||||
assertThat(searchResponse.getHits().getAt(0).fields().get("float_field").value(), equalTo((Object) 5.0));
|
assertThat(searchResponse.getHits().getAt(0).fields().get("float_field").value(), equalTo((Object) 5.0));
|
||||||
assertThat(searchResponse.getHits().getAt(0).fields().get("double_field").value(), equalTo((Object) 6.0d));
|
assertThat(searchResponse.getHits().getAt(0).fields().get("double_field").value(), equalTo((Object) 6.0d));
|
||||||
assertThat(searchResponse.getHits().getAt(0).fields().get("date_field").value(), equalTo((Object) 1332374400000L));
|
assertThat(searchResponse.getHits().getAt(0).fields().get("date_field").value(), equalTo(date));
|
||||||
assertThat(searchResponse.getHits().getAt(0).fields().get("boolean_field").value(), equalTo((Object) true));
|
assertThat(searchResponse.getHits().getAt(0).fields().get("boolean_field").value(), equalTo((Object) true));
|
||||||
assertThat(searchResponse.getHits().getAt(0).fields().get("text_field").value(), equalTo("foo"));
|
assertThat(searchResponse.getHits().getAt(0).fields().get("text_field").value(), equalTo("foo"));
|
||||||
assertThat(searchResponse.getHits().getAt(0).fields().get("keyword_field").value(), equalTo("foo"));
|
assertThat(searchResponse.getHits().getAt(0).fields().get("keyword_field").value(), equalTo("foo"));
|
||||||
|
|
|
@ -5,3 +5,10 @@
|
||||||
|
|
||||||
The groovy scripting language was deprecated in elasticsearch 5.0 and is now removed.
|
The groovy scripting language was deprecated in elasticsearch 5.0 and is now removed.
|
||||||
Use painless instead.
|
Use painless instead.
|
||||||
|
|
||||||
|
==== Date fields now return dates
|
||||||
|
|
||||||
|
`doc.some_date_field.value` now returns `ReadableDateTime`s instead of
|
||||||
|
milliseconds since epoch as a `long`. The same is true for
|
||||||
|
`doc.some_date_field[some_number]`. Use `doc.some_date_field.value.millis` to
|
||||||
|
fetch the milliseconds since epoch if you need it.
|
||||||
|
|
|
@ -196,10 +196,17 @@ POST hockey/player/1/_update
|
||||||
|
|
||||||
[float]
|
[float]
|
||||||
[[modules-scripting-painless-dates]]
|
[[modules-scripting-painless-dates]]
|
||||||
=== Regular expressions
|
=== Dates
|
||||||
|
|
||||||
Dates are a little different to work with than regular values. Here is an
|
Date fields are exposed as
|
||||||
example returning the year of every player's birth:
|
<<painless-api-reference-org-joda-time-ReadableDateTime, `ReadableDateTime`>>s
|
||||||
|
so they support methods like
|
||||||
|
<<painless-api-reference-org-joda-time-ReadableDateTime-getYear-0, `getYear`>>,
|
||||||
|
and
|
||||||
|
<<painless-api-reference-org-joda-time-ReadableDateTime-getDayOfWeek-0, `getDayOfWeek`>>.
|
||||||
|
To get milliseconds since epoch call
|
||||||
|
<<painless-api-reference-org-joda-time-ReadableInstant-getMillis-0, `getMillis`>>.
|
||||||
|
For example, the following returns every hockey player's birth year:
|
||||||
|
|
||||||
[source,js]
|
[source,js]
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
|
@ -208,7 +215,7 @@ GET hockey/_search
|
||||||
"script_fields": {
|
"script_fields": {
|
||||||
"birth_year": {
|
"birth_year": {
|
||||||
"script": {
|
"script": {
|
||||||
"inline": "doc.born.date.year"
|
"inline": "doc.born.value.year"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -216,18 +223,6 @@ GET hockey/_search
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
// CONSOLE
|
// CONSOLE
|
||||||
|
|
||||||
The key here is that instead of indexing directly into `doc.born` like you would
|
|
||||||
a normal field you have to call `doc.born.date` to get a
|
|
||||||
<<painless-api-reference-org-joda-time-ReadableDateTime, `ReadableDateTime`>>.
|
|
||||||
From there you can call methods like
|
|
||||||
<<painless-api-reference-org-joda-time-ReadableDateTime-getYear-0, `getYear`>>,
|
|
||||||
and <<painless-api-reference-org-joda-time-ReadableDateTime-getDayOfWeek-0, `getDayOfWeek`>>.
|
|
||||||
In the example above `year` is a shortcut to `getYear()`.
|
|
||||||
|
|
||||||
If the date field is a list then `date` will always return the first date. To
|
|
||||||
access all the dates use `dates` instead of `date`.
|
|
||||||
|
|
||||||
|
|
||||||
[float]
|
[float]
|
||||||
[[modules-scripting-painless-regex]]
|
[[modules-scripting-painless-regex]]
|
||||||
=== Regular expressions
|
=== Regular expressions
|
||||||
|
|
|
@ -85,6 +85,14 @@ class org.elasticsearch.index.fielddata.ScriptDocValues.Longs -> org.elasticsear
|
||||||
List getDates()
|
List getDates()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class org.elasticsearch.index.fielddata.ScriptDocValues.Dates -> org.elasticsearch.index.fielddata.ScriptDocValues$Dates extends List,Collection,Iterable,Object {
|
||||||
|
org.joda.time.ReadableDateTime get(int)
|
||||||
|
org.joda.time.ReadableDateTime getValue()
|
||||||
|
List getValues()
|
||||||
|
org.joda.time.ReadableDateTime getDate()
|
||||||
|
List getDates()
|
||||||
|
}
|
||||||
|
|
||||||
class org.elasticsearch.index.fielddata.ScriptDocValues.Doubles -> org.elasticsearch.index.fielddata.ScriptDocValues$Doubles extends List,Collection,Iterable,Object {
|
class org.elasticsearch.index.fielddata.ScriptDocValues.Doubles -> org.elasticsearch.index.fielddata.ScriptDocValues$Doubles extends List,Collection,Iterable,Object {
|
||||||
Double get(int)
|
Double get(int)
|
||||||
double getValue()
|
double getValue()
|
||||||
|
|
|
@ -109,7 +109,7 @@ setup:
|
||||||
script_fields:
|
script_fields:
|
||||||
bar:
|
bar:
|
||||||
script:
|
script:
|
||||||
inline: "doc.date.date.dayOfWeek"
|
inline: "doc.date.value.dayOfWeek"
|
||||||
|
|
||||||
- match: { hits.hits.0.fields.bar.0: 7}
|
- match: { hits.hits.0.fields.bar.0: 7}
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ setup:
|
||||||
script:
|
script:
|
||||||
inline: >
|
inline: >
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
for (def date : doc.dates.dates) {
|
for (def date : doc.dates) {
|
||||||
b.append(" ").append(date.getDayOfWeek());
|
b.append(" ").append(date.getDayOfWeek());
|
||||||
}
|
}
|
||||||
return b.toString().trim()
|
return b.toString().trim()
|
||||||
|
|
|
@ -113,8 +113,8 @@ setup:
|
||||||
script_fields:
|
script_fields:
|
||||||
field:
|
field:
|
||||||
script:
|
script:
|
||||||
inline: "doc['date'].get(0)"
|
inline: "doc.date.get(0)"
|
||||||
- match: { hits.hits.0.fields.field.0: 1483272672000 }
|
- match: { hits.hits.0.fields.field.0: '2017-01-01T12:11:12.000Z' }
|
||||||
|
|
||||||
- do:
|
- do:
|
||||||
search:
|
search:
|
||||||
|
@ -122,8 +122,8 @@ setup:
|
||||||
script_fields:
|
script_fields:
|
||||||
field:
|
field:
|
||||||
script:
|
script:
|
||||||
inline: "doc['date'].value"
|
inline: "doc.date.value"
|
||||||
- match: { hits.hits.0.fields.field.0: 1483272672000 }
|
- match: { hits.hits.0.fields.field.0: '2017-01-01T12:11:12.000Z' }
|
||||||
|
|
||||||
---
|
---
|
||||||
"geo_point":
|
"geo_point":
|
||||||
|
|
Loading…
Reference in New Issue