Allow terms aggregations on pure boolean scripts. (#22201)

The way aggregations on scripts work is by hiding scripts behind the same API
that we use for regular fields. However, there is no native support for boolean
fields, those need to be exposed as integers, with `0` standing for `false` and
`1` for true.

Relates #20941
This commit is contained in:
Adrien Grand 2016-12-21 16:48:53 +01:00 committed by GitHub
parent 0e9186e137
commit 3b3f9216db
2 changed files with 39 additions and 8 deletions

View File

@ -50,15 +50,10 @@ public class ScriptLongValues extends SortingNumericDocValues implements ScorerA
resize(0); resize(0);
} }
else if (value instanceof Number) {
resize(1);
values[0] = ((Number) value).longValue();
}
else if (value.getClass().isArray()) { 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)).longValue(); values[i] = toLongValue(Array.get(value, i));
} }
} }
@ -66,18 +61,33 @@ public class ScriptLongValues extends SortingNumericDocValues implements ScorerA
resize(((Collection<?>) value).size()); resize(((Collection<?>) value).size());
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] = toLongValue(it.next());
} }
assert i == count(); assert i == count();
} }
else { else {
throw new AggregationExecutionException("Unsupported script value [" + value + "]"); resize(1);
values[0] = toLongValue(value);
} }
sort(); sort();
} }
private static long toLongValue(Object o) {
if (o instanceof Number) {
return ((Number) o).longValue();
} 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() ? 1L : 0L;
} else {
throw new AggregationExecutionException("Unsupported script value [" + o + "], expected a number");
}
}
@Override @Override
public void setScorer(Scorer scorer) { public void setScorer(Scorer scorer) {
script.setScorer(scorer); script.setScorer(scorer);

View File

@ -108,6 +108,27 @@ public class ScriptValuesTests extends ESTestCase {
} }
} }
public void testBooleans() {
final Object[][] values = new Boolean[randomInt(10)][];
for (int i = 0; i < values.length; ++i) {
Boolean[] booleans = new Boolean[randomInt(8)];
for (int j = 0; j < booleans.length; ++j) {
booleans[j] = randomBoolean();
}
Arrays.sort(booleans);
values[i] = booleans;
}
FakeSearchScript script = new FakeSearchScript(values);
ScriptLongValues scriptValues = new ScriptLongValues(script);
for (int i = 0; i < values.length; ++i) {
scriptValues.setDocument(i);
assertEquals(values[i].length, scriptValues.count());
for (int j = 0; j < values[i].length; ++j) {
assertEquals(values[i][j], scriptValues.valueAt(j) == 1L);
}
}
}
public void testDoubles() { public void testDoubles() {
final Object[][] values = new Double[randomInt(10)][]; final Object[][] values = new Double[randomInt(10)][];
for (int i = 0; i < values.length; ++i) { for (int i = 0; i < values.length; ++i) {