Scripting: Add Multi-Valued Field Methods to Expressions
Add methods to operate on multi-valued fields in the expressions language. Note that users will still not be able to access individual values within a multi-valued field. The following methods will be included: * min * max * avg * median * count * sum Additionally, changes have been made to MultiValueMode to support the new median method. closes #11105
This commit is contained in:
parent
1d3a8ad36a
commit
a5c0ac0d67
|
@ -50,6 +50,8 @@ to. The `mode` option can have the following values:
|
||||||
number based array fields.
|
number based array fields.
|
||||||
`avg`:: Use the average of all values as sort value. Only applicable
|
`avg`:: Use the average of all values as sort value. Only applicable
|
||||||
for number based array fields.
|
for number based array fields.
|
||||||
|
`median`:: Use the median of all values as sort value. Only applicable
|
||||||
|
for number based array fields.
|
||||||
|
|
||||||
===== Sort mode example usage
|
===== Sort mode example usage
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.script.expression;
|
||||||
|
|
||||||
|
import org.apache.lucene.queries.function.ValueSource;
|
||||||
|
import org.apache.lucene.queries.function.docvalues.DoubleDocValues;
|
||||||
|
import org.elasticsearch.index.fielddata.AtomicNumericFieldData;
|
||||||
|
import org.elasticsearch.index.fielddata.SortedNumericDoubleValues;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FunctionValues to get the count of the number of values in a field for a document.
|
||||||
|
*/
|
||||||
|
public class CountMethodFunctionValues extends DoubleDocValues {
|
||||||
|
SortedNumericDoubleValues values;
|
||||||
|
|
||||||
|
CountMethodFunctionValues(ValueSource parent, AtomicNumericFieldData fieldData) {
|
||||||
|
super(parent);
|
||||||
|
|
||||||
|
values = fieldData.getDoubleValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double doubleVal(int doc) {
|
||||||
|
values.setDocument(doc);
|
||||||
|
return values.count();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.script.expression;
|
||||||
|
|
||||||
|
import org.apache.lucene.index.LeafReaderContext;
|
||||||
|
import org.apache.lucene.queries.function.FunctionValues;
|
||||||
|
import org.apache.lucene.queries.function.ValueSource;
|
||||||
|
import org.elasticsearch.index.fielddata.AtomicFieldData;
|
||||||
|
import org.elasticsearch.index.fielddata.AtomicNumericFieldData;
|
||||||
|
import org.elasticsearch.index.fielddata.IndexFieldData;
|
||||||
|
import org.elasticsearch.search.MultiValueMode;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A ValueSource to create FunctionValues to get the count of the number of values in a field for a document.
|
||||||
|
*/
|
||||||
|
public class CountMethodValueSource extends ValueSource {
|
||||||
|
protected IndexFieldData<?> fieldData;
|
||||||
|
|
||||||
|
protected CountMethodValueSource(IndexFieldData<?> fieldData) {
|
||||||
|
Objects.requireNonNull(fieldData);
|
||||||
|
|
||||||
|
this.fieldData = fieldData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FunctionValues getValues(Map context, LeafReaderContext leaf) throws IOException {
|
||||||
|
AtomicFieldData leafData = fieldData.load(leaf);
|
||||||
|
assert(leafData instanceof AtomicNumericFieldData);
|
||||||
|
|
||||||
|
return new CountMethodFunctionValues(this, (AtomicNumericFieldData)leafData);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
FieldDataValueSource that = (FieldDataValueSource) o;
|
||||||
|
|
||||||
|
return fieldData.equals(that.fieldData);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return fieldData.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String description() {
|
||||||
|
return "count: field(" + fieldData.getFieldNames().toString() + ")";
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,13 +25,14 @@ import java.util.TimeZone;
|
||||||
|
|
||||||
import org.apache.lucene.queries.function.ValueSource;
|
import org.apache.lucene.queries.function.ValueSource;
|
||||||
import org.elasticsearch.index.fielddata.AtomicNumericFieldData;
|
import org.elasticsearch.index.fielddata.AtomicNumericFieldData;
|
||||||
|
import org.elasticsearch.search.MultiValueMode;
|
||||||
|
|
||||||
class DateMethodFunctionValues extends FieldDataFunctionValues {
|
class DateMethodFunctionValues extends FieldDataFunctionValues {
|
||||||
private final int calendarType;
|
private final int calendarType;
|
||||||
private final Calendar calendar;
|
private final Calendar calendar;
|
||||||
|
|
||||||
DateMethodFunctionValues(ValueSource parent, AtomicNumericFieldData data, int calendarType) {
|
DateMethodFunctionValues(ValueSource parent, MultiValueMode multiValueMode, AtomicNumericFieldData data, int calendarType) {
|
||||||
super(parent, data);
|
super(parent, multiValueMode, data);
|
||||||
|
|
||||||
this.calendarType = calendarType;
|
this.calendarType = calendarType;
|
||||||
calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"), Locale.ROOT);
|
calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"), Locale.ROOT);
|
||||||
|
|
|
@ -29,14 +29,15 @@ import org.apache.lucene.queries.function.FunctionValues;
|
||||||
import org.elasticsearch.index.fielddata.AtomicFieldData;
|
import org.elasticsearch.index.fielddata.AtomicFieldData;
|
||||||
import org.elasticsearch.index.fielddata.AtomicNumericFieldData;
|
import org.elasticsearch.index.fielddata.AtomicNumericFieldData;
|
||||||
import org.elasticsearch.index.fielddata.IndexFieldData;
|
import org.elasticsearch.index.fielddata.IndexFieldData;
|
||||||
|
import org.elasticsearch.search.MultiValueMode;
|
||||||
|
|
||||||
class DateMethodValueSource extends FieldDataValueSource {
|
class DateMethodValueSource extends FieldDataValueSource {
|
||||||
|
|
||||||
protected final String methodName;
|
protected final String methodName;
|
||||||
protected final int calendarType;
|
protected final int calendarType;
|
||||||
|
|
||||||
DateMethodValueSource(IndexFieldData<?> indexFieldData, String methodName, int calendarType) {
|
DateMethodValueSource(IndexFieldData<?> indexFieldData, MultiValueMode multiValueMode, String methodName, int calendarType) {
|
||||||
super(indexFieldData);
|
super(indexFieldData, multiValueMode);
|
||||||
|
|
||||||
Objects.requireNonNull(methodName);
|
Objects.requireNonNull(methodName);
|
||||||
|
|
||||||
|
@ -44,6 +45,19 @@ class DateMethodValueSource extends FieldDataValueSource {
|
||||||
this.calendarType = calendarType;
|
this.calendarType = calendarType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FunctionValues getValues(Map context, LeafReaderContext leaf) throws IOException {
|
||||||
|
AtomicFieldData leafData = fieldData.load(leaf);
|
||||||
|
assert(leafData instanceof AtomicNumericFieldData);
|
||||||
|
|
||||||
|
return new DateMethodFunctionValues(this, multiValueMode, (AtomicNumericFieldData)leafData, calendarType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String description() {
|
||||||
|
return methodName + ": field(" + fieldData.getFieldNames().toString() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
|
@ -64,17 +78,4 @@ class DateMethodValueSource extends FieldDataValueSource {
|
||||||
result = 31 * result + calendarType;
|
result = 31 * result + calendarType;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public FunctionValues getValues(Map context, LeafReaderContext leaf) throws IOException {
|
|
||||||
AtomicFieldData leafData = fieldData.load(leaf);
|
|
||||||
assert(leafData instanceof AtomicNumericFieldData);
|
|
||||||
|
|
||||||
return new DateMethodFunctionValues(this, (AtomicNumericFieldData)leafData, calendarType);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String description() {
|
|
||||||
return methodName + ": field(" + fieldData.getFieldNames().toString() + ")";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ import org.elasticsearch.script.CompiledScript;
|
||||||
import org.elasticsearch.script.ExecutableScript;
|
import org.elasticsearch.script.ExecutableScript;
|
||||||
import org.elasticsearch.script.ScriptEngineService;
|
import org.elasticsearch.script.ScriptEngineService;
|
||||||
import org.elasticsearch.script.SearchScript;
|
import org.elasticsearch.script.SearchScript;
|
||||||
|
import org.elasticsearch.search.MultiValueMode;
|
||||||
import org.elasticsearch.search.lookup.SearchLookup;
|
import org.elasticsearch.search.lookup.SearchLookup;
|
||||||
|
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
|
@ -60,6 +61,13 @@ public class ExpressionScriptEngineService extends AbstractComponent implements
|
||||||
protected static final String GET_MINUTES_METHOD = "getMinutes";
|
protected static final String GET_MINUTES_METHOD = "getMinutes";
|
||||||
protected static final String GET_SECONDS_METHOD = "getSeconds";
|
protected static final String GET_SECONDS_METHOD = "getSeconds";
|
||||||
|
|
||||||
|
protected static final String MINIMUM_METHOD = "min";
|
||||||
|
protected static final String MAXIMUM_METHOD = "max";
|
||||||
|
protected static final String AVERAGE_METHOD = "avg";
|
||||||
|
protected static final String MEDIAN_METHOD = "median";
|
||||||
|
protected static final String SUM_METHOD = "sum";
|
||||||
|
protected static final String COUNT_METHOD = "count";
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ExpressionScriptEngineService(Settings settings) {
|
public ExpressionScriptEngineService(Settings settings) {
|
||||||
super(settings);
|
super(settings);
|
||||||
|
@ -156,7 +164,7 @@ public class ExpressionScriptEngineService extends AbstractComponent implements
|
||||||
|
|
||||||
IndexFieldData<?> fieldData = lookup.doc().fieldDataService().getForField((NumberFieldMapper)field);
|
IndexFieldData<?> fieldData = lookup.doc().fieldDataService().getForField((NumberFieldMapper)field);
|
||||||
if (methodname == null) {
|
if (methodname == null) {
|
||||||
bindings.add(variable, new FieldDataValueSource(fieldData));
|
bindings.add(variable, new FieldDataValueSource(fieldData, MultiValueMode.MIN));
|
||||||
} else {
|
} else {
|
||||||
bindings.add(variable, getMethodValueSource(field, fieldData, fieldname, methodname));
|
bindings.add(variable, getMethodValueSource(field, fieldData, fieldname, methodname));
|
||||||
}
|
}
|
||||||
|
@ -180,6 +188,18 @@ public class ExpressionScriptEngineService extends AbstractComponent implements
|
||||||
return getDateMethodValueSource(field, fieldData, fieldName, methodName, Calendar.MINUTE);
|
return getDateMethodValueSource(field, fieldData, fieldName, methodName, Calendar.MINUTE);
|
||||||
case GET_SECONDS_METHOD:
|
case GET_SECONDS_METHOD:
|
||||||
return getDateMethodValueSource(field, fieldData, fieldName, methodName, Calendar.SECOND);
|
return getDateMethodValueSource(field, fieldData, fieldName, methodName, Calendar.SECOND);
|
||||||
|
case MINIMUM_METHOD:
|
||||||
|
return new FieldDataValueSource(fieldData, MultiValueMode.MIN);
|
||||||
|
case MAXIMUM_METHOD:
|
||||||
|
return new FieldDataValueSource(fieldData, MultiValueMode.MAX);
|
||||||
|
case AVERAGE_METHOD:
|
||||||
|
return new FieldDataValueSource(fieldData, MultiValueMode.AVG);
|
||||||
|
case MEDIAN_METHOD:
|
||||||
|
return new FieldDataValueSource(fieldData, MultiValueMode.MEDIAN);
|
||||||
|
case SUM_METHOD:
|
||||||
|
return new FieldDataValueSource(fieldData, MultiValueMode.SUM);
|
||||||
|
case COUNT_METHOD:
|
||||||
|
return new CountMethodValueSource(fieldData);
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Member method [" + methodName + "] does not exist.");
|
throw new IllegalArgumentException("Member method [" + methodName + "] does not exist.");
|
||||||
}
|
}
|
||||||
|
@ -190,7 +210,7 @@ public class ExpressionScriptEngineService extends AbstractComponent implements
|
||||||
throw new IllegalArgumentException("Member method [" + methodName + "] can only be used with a date field type, not the field [" + fieldName + "].");
|
throw new IllegalArgumentException("Member method [" + methodName + "] can only be used with a date field type, not the field [" + fieldName + "].");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DateMethodValueSource(fieldData, methodName, calendarType);
|
return new DateMethodValueSource(fieldData, MultiValueMode.MIN, methodName, calendarType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -31,9 +31,9 @@ import org.elasticsearch.search.MultiValueMode;
|
||||||
class FieldDataFunctionValues extends DoubleDocValues {
|
class FieldDataFunctionValues extends DoubleDocValues {
|
||||||
NumericDoubleValues dataAccessor;
|
NumericDoubleValues dataAccessor;
|
||||||
|
|
||||||
FieldDataFunctionValues(ValueSource parent, AtomicNumericFieldData d) {
|
FieldDataFunctionValues(ValueSource parent, MultiValueMode m, AtomicNumericFieldData d) {
|
||||||
super(parent);
|
super(parent);
|
||||||
dataAccessor = MultiValueMode.MIN.select(d.getDoubleValues(), 0d);
|
dataAccessor = m.select(d.getDoubleValues(), 0d);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.apache.lucene.queries.function.ValueSource;
|
||||||
import org.elasticsearch.index.fielddata.AtomicFieldData;
|
import org.elasticsearch.index.fielddata.AtomicFieldData;
|
||||||
import org.elasticsearch.index.fielddata.AtomicNumericFieldData;
|
import org.elasticsearch.index.fielddata.AtomicNumericFieldData;
|
||||||
import org.elasticsearch.index.fielddata.IndexFieldData;
|
import org.elasticsearch.index.fielddata.IndexFieldData;
|
||||||
|
import org.elasticsearch.search.MultiValueMode;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -36,18 +37,14 @@ import java.util.Objects;
|
||||||
class FieldDataValueSource extends ValueSource {
|
class FieldDataValueSource extends ValueSource {
|
||||||
|
|
||||||
protected IndexFieldData<?> fieldData;
|
protected IndexFieldData<?> fieldData;
|
||||||
|
protected MultiValueMode multiValueMode;
|
||||||
|
|
||||||
protected FieldDataValueSource(IndexFieldData<?> d) {
|
protected FieldDataValueSource(IndexFieldData<?> d, MultiValueMode m) {
|
||||||
Objects.requireNonNull(d);
|
Objects.requireNonNull(d);
|
||||||
|
Objects.requireNonNull(m);
|
||||||
|
|
||||||
fieldData = d;
|
fieldData = d;
|
||||||
}
|
multiValueMode = m;
|
||||||
|
|
||||||
@Override
|
|
||||||
public FunctionValues getValues(Map context, LeafReaderContext leaf) throws IOException {
|
|
||||||
AtomicFieldData leafData = fieldData.load(leaf);
|
|
||||||
assert(leafData instanceof AtomicNumericFieldData);
|
|
||||||
return new FieldDataFunctionValues(this, (AtomicNumericFieldData)leafData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -57,12 +54,23 @@ class FieldDataValueSource extends ValueSource {
|
||||||
|
|
||||||
FieldDataValueSource that = (FieldDataValueSource) o;
|
FieldDataValueSource that = (FieldDataValueSource) o;
|
||||||
|
|
||||||
return fieldData.equals(that.fieldData);
|
if (!fieldData.equals(that.fieldData)) return false;
|
||||||
|
return multiValueMode == that.multiValueMode;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return fieldData.hashCode();
|
int result = fieldData.hashCode();
|
||||||
|
result = 31 * result + multiValueMode.hashCode();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FunctionValues getValues(Map context, LeafReaderContext leaf) throws IOException {
|
||||||
|
AtomicFieldData leafData = fieldData.load(leaf);
|
||||||
|
assert(leafData instanceof AtomicNumericFieldData);
|
||||||
|
return new FieldDataFunctionValues(this, multiValueMode, (AtomicNumericFieldData)leafData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
package org.elasticsearch.search;
|
package org.elasticsearch.search;
|
||||||
|
|
||||||
|
import javafx.collections.transformation.SortedList;
|
||||||
import org.apache.lucene.index.*;
|
import org.apache.lucene.index.*;
|
||||||
import org.apache.lucene.search.DocIdSet;
|
import org.apache.lucene.search.DocIdSet;
|
||||||
import org.apache.lucene.search.DocIdSetIterator;
|
import org.apache.lucene.search.DocIdSetIterator;
|
||||||
|
@ -105,6 +106,46 @@ public enum MultiValueMode {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Median of the values.
|
||||||
|
*
|
||||||
|
* Note that apply/reduce do not work with MED since median cannot be derived from
|
||||||
|
* an accumulator algorithm without using internal memory.
|
||||||
|
*/
|
||||||
|
MEDIAN {
|
||||||
|
@Override
|
||||||
|
protected long pick(SortedNumericDocValues values, long missingValue) {
|
||||||
|
int count = values.count();
|
||||||
|
if (count > 0) {
|
||||||
|
if (count % 2 == 0) {
|
||||||
|
count /= 2;
|
||||||
|
return (values.valueAt(count - 1) + values.valueAt(count))/2;
|
||||||
|
} else {
|
||||||
|
count /= 2;
|
||||||
|
return values.valueAt(count);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return missingValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected double pick(SortedNumericDoubleValues values, double missingValue) {
|
||||||
|
int count = values.count();
|
||||||
|
if (count > 0) {
|
||||||
|
if (count % 2 == 0) {
|
||||||
|
count /= 2;
|
||||||
|
return (values.valueAt(count - 1) + values.valueAt(count))/2;
|
||||||
|
} else {
|
||||||
|
count /= 2;
|
||||||
|
return values.valueAt(count);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return missingValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pick the lowest value.
|
* Pick the lowest value.
|
||||||
*/
|
*/
|
||||||
|
@ -288,7 +329,9 @@ public enum MultiValueMode {
|
||||||
* @param b another argument
|
* @param b another argument
|
||||||
* @return the result of the function.
|
* @return the result of the function.
|
||||||
*/
|
*/
|
||||||
public abstract double apply(double a, double b);
|
public double apply(double a, double b) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies the sort mode and returns the result. This method is meant to be
|
* Applies the sort mode and returns the result. This method is meant to be
|
||||||
|
@ -302,7 +345,9 @@ public enum MultiValueMode {
|
||||||
* @param b another argument
|
* @param b another argument
|
||||||
* @return the result of the function.
|
* @return the result of the function.
|
||||||
*/
|
*/
|
||||||
public abstract long apply(long a, long b);
|
public long apply(long a, long b) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
public int applyOrd(int ord1, int ord2) {
|
public int applyOrd(int ord1, int ord2) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
|
|
|
@ -125,6 +125,72 @@ public class ExpressionScriptTests extends ElasticsearchIntegrationTest {
|
||||||
assertEquals(1983.0, hits.getAt(1).field("foo").getValue());
|
assertEquals(1983.0, hits.getAt(1).field("foo").getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testMultiValueMethods() throws Exception {
|
||||||
|
ElasticsearchAssertions.assertAcked(prepareCreate("test").addMapping("doc", "double0", "type=double", "double1", "type=double"));
|
||||||
|
ensureGreen("test");
|
||||||
|
indexRandom(true,
|
||||||
|
client().prepareIndex("test", "doc", "1").setSource("double0", "5.0", "double0", "1.0", "double0", "1.5", "double1", "1.2", "double1", "2.4"),
|
||||||
|
client().prepareIndex("test", "doc", "2").setSource("double0", "5.0", "double1", "3.0"),
|
||||||
|
client().prepareIndex("test", "doc", "3").setSource("double0", "5.0", "double0", "1.0", "double0", "1.5", "double0", "-1.5", "double1", "4.0"));
|
||||||
|
|
||||||
|
|
||||||
|
SearchResponse rsp = buildRequest("doc['double0'].count() + doc['double1'].count()").get();
|
||||||
|
assertSearchResponse(rsp);
|
||||||
|
SearchHits hits = rsp.getHits();
|
||||||
|
assertEquals(3, hits.getTotalHits());
|
||||||
|
assertEquals(5.0, hits.getAt(0).field("foo").getValue());
|
||||||
|
assertEquals(2.0, hits.getAt(1).field("foo").getValue());
|
||||||
|
assertEquals(5.0, hits.getAt(2).field("foo").getValue());
|
||||||
|
|
||||||
|
rsp = buildRequest("doc['double0'].sum()").get();
|
||||||
|
assertSearchResponse(rsp);
|
||||||
|
hits = rsp.getHits();
|
||||||
|
assertEquals(3, hits.getTotalHits());
|
||||||
|
assertEquals(7.5, hits.getAt(0).field("foo").getValue());
|
||||||
|
assertEquals(5.0, hits.getAt(1).field("foo").getValue());
|
||||||
|
assertEquals(6.0, hits.getAt(2).field("foo").getValue());
|
||||||
|
|
||||||
|
rsp = buildRequest("doc['double0'].avg() + doc['double1'].avg()").get();
|
||||||
|
assertSearchResponse(rsp);
|
||||||
|
hits = rsp.getHits();
|
||||||
|
assertEquals(3, hits.getTotalHits());
|
||||||
|
assertEquals(4.3, hits.getAt(0).field("foo").getValue());
|
||||||
|
assertEquals(8.0, hits.getAt(1).field("foo").getValue());
|
||||||
|
assertEquals(5.5, hits.getAt(2).field("foo").getValue());
|
||||||
|
|
||||||
|
rsp = buildRequest("doc['double0'].median()").get();
|
||||||
|
assertSearchResponse(rsp);
|
||||||
|
hits = rsp.getHits();
|
||||||
|
assertEquals(3, hits.getTotalHits());
|
||||||
|
assertEquals(1.5, hits.getAt(0).field("foo").getValue());
|
||||||
|
assertEquals(5.0, hits.getAt(1).field("foo").getValue());
|
||||||
|
assertEquals(1.25, hits.getAt(2).field("foo").getValue());
|
||||||
|
|
||||||
|
rsp = buildRequest("doc['double0'].min()").get();
|
||||||
|
assertSearchResponse(rsp);
|
||||||
|
hits = rsp.getHits();
|
||||||
|
assertEquals(3, hits.getTotalHits());
|
||||||
|
assertEquals(1.0, hits.getAt(0).field("foo").getValue());
|
||||||
|
assertEquals(5.0, hits.getAt(1).field("foo").getValue());
|
||||||
|
assertEquals(-1.5, hits.getAt(2).field("foo").getValue());
|
||||||
|
|
||||||
|
rsp = buildRequest("doc['double0'].max()").get();
|
||||||
|
assertSearchResponse(rsp);
|
||||||
|
hits = rsp.getHits();
|
||||||
|
assertEquals(3, hits.getTotalHits());
|
||||||
|
assertEquals(5.0, hits.getAt(0).field("foo").getValue());
|
||||||
|
assertEquals(5.0, hits.getAt(1).field("foo").getValue());
|
||||||
|
assertEquals(5.0, hits.getAt(2).field("foo").getValue());
|
||||||
|
|
||||||
|
rsp = buildRequest("doc['double0'].sum()/doc['double0'].count()").get();
|
||||||
|
assertSearchResponse(rsp);
|
||||||
|
hits = rsp.getHits();
|
||||||
|
assertEquals(3, hits.getTotalHits());
|
||||||
|
assertEquals(2.5, hits.getAt(0).field("foo").getValue());
|
||||||
|
assertEquals(5.0, hits.getAt(1).field("foo").getValue());
|
||||||
|
assertEquals(1.5, hits.getAt(2).field("foo").getValue());
|
||||||
|
}
|
||||||
|
|
||||||
public void testInvalidDateMethodCall() throws Exception {
|
public void testInvalidDateMethodCall() throws Exception {
|
||||||
ElasticsearchAssertions.assertAcked(prepareCreate("test").addMapping("doc", "double", "type=double"));
|
ElasticsearchAssertions.assertAcked(prepareCreate("test").addMapping("doc", "double", "type=double"));
|
||||||
ensureGreen("test");
|
ensureGreen("test");
|
||||||
|
|
|
@ -32,6 +32,8 @@ import org.elasticsearch.test.ElasticsearchTestCase;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class MultiValueModeTests extends ElasticsearchTestCase {
|
public class MultiValueModeTests extends ElasticsearchTestCase {
|
||||||
|
|
||||||
|
@ -122,6 +124,9 @@ public class MultiValueModeTests extends ElasticsearchTestCase {
|
||||||
private void verify(SortedNumericDocValues values, int maxDoc) {
|
private void verify(SortedNumericDocValues values, int maxDoc) {
|
||||||
for (long missingValue : new long[] { 0, randomLong() }) {
|
for (long missingValue : new long[] { 0, randomLong() }) {
|
||||||
for (MultiValueMode mode : MultiValueMode.values()) {
|
for (MultiValueMode mode : MultiValueMode.values()) {
|
||||||
|
if (MultiValueMode.MEDIAN.equals(mode)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
final NumericDocValues selected = mode.select(values, missingValue);
|
final NumericDocValues selected = mode.select(values, missingValue);
|
||||||
for (int i = 0; i < maxDoc; ++i) {
|
for (int i = 0; i < maxDoc; ++i) {
|
||||||
final long actual = selected.get(i);
|
final long actual = selected.get(i);
|
||||||
|
@ -147,6 +152,9 @@ public class MultiValueModeTests extends ElasticsearchTestCase {
|
||||||
private void verify(SortedNumericDocValues values, int maxDoc, FixedBitSet rootDocs, FixedBitSet innerDocs) throws IOException {
|
private void verify(SortedNumericDocValues values, int maxDoc, FixedBitSet rootDocs, FixedBitSet innerDocs) throws IOException {
|
||||||
for (long missingValue : new long[] { 0, randomLong() }) {
|
for (long missingValue : new long[] { 0, randomLong() }) {
|
||||||
for (MultiValueMode mode : MultiValueMode.values()) {
|
for (MultiValueMode mode : MultiValueMode.values()) {
|
||||||
|
if (MultiValueMode.MEDIAN.equals(mode)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
final NumericDocValues selected = mode.select(values, missingValue, rootDocs, new BitDocIdSet(innerDocs), maxDoc);
|
final NumericDocValues selected = mode.select(values, missingValue, rootDocs, new BitDocIdSet(innerDocs), maxDoc);
|
||||||
int prevRoot = -1;
|
int prevRoot = -1;
|
||||||
for (int root = rootDocs.nextSetBit(0); root != -1; root = root + 1 < maxDoc ? rootDocs.nextSetBit(root + 1) : -1) {
|
for (int root = rootDocs.nextSetBit(0); root != -1; root = root + 1 < maxDoc ? rootDocs.nextSetBit(root + 1) : -1) {
|
||||||
|
@ -239,6 +247,9 @@ public class MultiValueModeTests extends ElasticsearchTestCase {
|
||||||
private void verify(SortedNumericDoubleValues values, int maxDoc) {
|
private void verify(SortedNumericDoubleValues values, int maxDoc) {
|
||||||
for (long missingValue : new long[] { 0, randomLong() }) {
|
for (long missingValue : new long[] { 0, randomLong() }) {
|
||||||
for (MultiValueMode mode : MultiValueMode.values()) {
|
for (MultiValueMode mode : MultiValueMode.values()) {
|
||||||
|
if (MultiValueMode.MEDIAN.equals(mode)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
final NumericDoubleValues selected = mode.select(values, missingValue);
|
final NumericDoubleValues selected = mode.select(values, missingValue);
|
||||||
for (int i = 0; i < maxDoc; ++i) {
|
for (int i = 0; i < maxDoc; ++i) {
|
||||||
final double actual = selected.get(i);
|
final double actual = selected.get(i);
|
||||||
|
@ -264,6 +275,9 @@ public class MultiValueModeTests extends ElasticsearchTestCase {
|
||||||
private void verify(SortedNumericDoubleValues values, int maxDoc, FixedBitSet rootDocs, FixedBitSet innerDocs) throws IOException {
|
private void verify(SortedNumericDoubleValues values, int maxDoc, FixedBitSet rootDocs, FixedBitSet innerDocs) throws IOException {
|
||||||
for (long missingValue : new long[] { 0, randomLong() }) {
|
for (long missingValue : new long[] { 0, randomLong() }) {
|
||||||
for (MultiValueMode mode : MultiValueMode.values()) {
|
for (MultiValueMode mode : MultiValueMode.values()) {
|
||||||
|
if (MultiValueMode.MEDIAN.equals(mode)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
final NumericDoubleValues selected = mode.select(values, missingValue, rootDocs, new BitDocIdSet(innerDocs), maxDoc);
|
final NumericDoubleValues selected = mode.select(values, missingValue, rootDocs, new BitDocIdSet(innerDocs), maxDoc);
|
||||||
int prevRoot = -1;
|
int prevRoot = -1;
|
||||||
for (int root = rootDocs.nextSetBit(0); root != -1; root = root + 1 < maxDoc ? rootDocs.nextSetBit(root + 1) : -1) {
|
for (int root = rootDocs.nextSetBit(0); root != -1; root = root + 1 < maxDoc ? rootDocs.nextSetBit(root + 1) : -1) {
|
||||||
|
|
Loading…
Reference in New Issue