Allow scripts to return more than 4 values in aggregations.

A missing call to ArrayUtil.grow prevented the array that stores the values
from growing in case the number of values returned by the script was higher
than the original size of the array.

Close #5414
This commit is contained in:
Adrien Grand 2014-03-13 10:05:41 +01:00
parent 2aaf81f8ef
commit b7de1becf4
3 changed files with 167 additions and 3 deletions

View File

@ -36,7 +36,7 @@ public class ScriptDoubleValues extends DoubleValues implements ScriptValues {
final SearchScript script; final SearchScript script;
private Object value; private Object value;
private double[] values = new double[4]; private double[] values = new double[1];
private int valueCount; private int valueCount;
private int valueOffset; private int valueOffset;
@ -76,6 +76,7 @@ public class ScriptDoubleValues extends DoubleValues implements ScriptValues {
else if (value instanceof Collection) { else if (value instanceof Collection) {
valueCount = ((Collection<?>) value).size(); valueCount = ((Collection<?>) value).size();
values = ArrayUtil.grow(values, valueCount);
int i = 0; int i = 0;
for (Iterator<?> it = ((Collection<?>) value).iterator(); it.hasNext(); ++i) { for (Iterator<?> it = ((Collection<?>) value).iterator(); it.hasNext(); ++i) {
values[i] = ((Number) it.next()).doubleValue(); values[i] = ((Number) it.next()).doubleValue();

View File

@ -36,7 +36,7 @@ public class ScriptLongValues extends LongValues implements ScriptValues {
final SearchScript script; final SearchScript script;
private Object value; private Object value;
private long[] values = new long[4]; private long[] values = new long[1];
private int valueCount; private int valueCount;
private int valueOffset; private int valueOffset;
@ -69,12 +69,13 @@ public class ScriptLongValues extends LongValues implements ScriptValues {
valueCount = Array.getLength(value); valueCount = Array.getLength(value);
values = ArrayUtil.grow(values, valueCount); values = ArrayUtil.grow(values, valueCount);
for (int i = 0; i < valueCount; ++i) { for (int i = 0; i < valueCount; ++i) {
values[i] = ((Number) Array.get(value, i++)).longValue(); values[i] = ((Number) Array.get(value, i)).longValue();
} }
} }
else if (value instanceof Collection) { else if (value instanceof Collection) {
valueCount = ((Collection<?>) value).size(); valueCount = ((Collection<?>) value).size();
values = ArrayUtil.grow(values, valueCount);
int i = 0; int i = 0;
for (Iterator<?> it = ((Collection<?>) value).iterator(); it.hasNext(); ++i) { for (Iterator<?> it = ((Collection<?>) value).iterator(); it.hasNext(); ++i) {
values[i] = ((Number) it.next()).longValue(); values[i] = ((Number) it.next()).longValue();

View File

@ -0,0 +1,162 @@
/*
* 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.search.aggregations.support;
import com.carrotsearch.randomizedtesting.generators.RandomStrings;
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.search.Scorer;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.script.SearchScript;
import org.elasticsearch.search.aggregations.support.bytes.ScriptBytesValues;
import org.elasticsearch.search.aggregations.support.numeric.ScriptDoubleValues;
import org.elasticsearch.search.aggregations.support.numeric.ScriptLongValues;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.junit.Test;
import java.util.Arrays;
import java.util.Map;
public class ScriptValuesTests extends ElasticsearchTestCase {
private static class FakeSearchScript implements SearchScript {
private final Object[][] values;
int index;
FakeSearchScript(Object[][] values) {
this.values = values;
index = -1;
}
@Override
public void setNextVar(String name, Object value) {
}
@Override
public Object run() {
// Script values are supposed to support null, single values, arrays and collections
final Object[] values = this.values[index];
if (values.length <= 1 && randomBoolean()) {
return values.length == 0 ? null : values[0];
}
return randomBoolean() ? values : Arrays.asList(values);
}
@Override
public Object unwrap(Object value) {
throw new UnsupportedOperationException();
}
@Override
public void setNextReader(AtomicReaderContext reader) {
}
@Override
public void setScorer(Scorer scorer) {
}
@Override
public void setNextDocId(int doc) {
index = doc;
}
@Override
public void setNextSource(Map<String, Object> source) {
}
@Override
public void setNextScore(float score) {
}
@Override
public float runAsFloat() {
throw new UnsupportedOperationException();
}
@Override
public long runAsLong() {
throw new UnsupportedOperationException();
}
@Override
public double runAsDouble() {
throw new UnsupportedOperationException();
}
}
@Test
public void longs() {
final Object[][] values = new Long[randomInt(10)][];
for (int i = 0; i < values.length; ++i) {
values[i] = new Long[randomInt(8)];
for (int j = 0; j < values[i].length; ++j) {
values[i][j] = randomLong();
}
}
FakeSearchScript script = new FakeSearchScript(values);
ScriptLongValues scriptValues = new ScriptLongValues(script);
for (int i = 0; i < values.length; ++i) {
assertEquals(values[i].length, scriptValues.setDocument(i));
for (int j = 0; j < values[i].length; ++j) {
assertEquals(values[i][j], scriptValues.nextValue());
}
}
}
@Test
public void doubles() {
final Object[][] values = new Double[randomInt(10)][];
for (int i = 0; i < values.length; ++i) {
values[i] = new Double[randomInt(8)];
for (int j = 0; j < values[i].length; ++j) {
values[i][j] = randomDouble();
}
}
FakeSearchScript script = new FakeSearchScript(values);
ScriptDoubleValues scriptValues = new ScriptDoubleValues(script);
for (int i = 0; i < values.length; ++i) {
assertEquals(values[i].length, scriptValues.setDocument(i));
for (int j = 0; j < values[i].length; ++j) {
assertEquals(values[i][j], scriptValues.nextValue());
}
}
}
@Test
public void bytes() {
final String[][] values = new String[randomInt(10)][];
for (int i = 0; i < values.length; ++i) {
values[i] = new String[randomInt(8)];
for (int j = 0; j < values[i].length; ++j) {
values[i][j] = RandomStrings.randomAsciiOfLength(getRandom(), 5);
}
}
FakeSearchScript script = new FakeSearchScript(values);
ScriptBytesValues scriptValues = new ScriptBytesValues(script);
for (int i = 0; i < values.length; ++i) {
assertEquals(values[i].length, scriptValues.setDocument(i));
for (int j = 0; j < values[i].length; ++j) {
assertEquals(new BytesRef(values[i][j]), scriptValues.nextValue());
}
}
}
}