Add support for booleans in scripts (#20950)

* Scripting: Add support for booleans in scripts

Since 2.0, booleans have been represented as numeric fields (longs).
However, in scripts, this is odd, since you expect doing a comparison
against a boolean to work. While languages like groovy will auto convert
between booleans and longs, painless does not.

This changes the doc values accessor for boolean fields in scripts to
return Boolean objects instead of Long objects.

closes #20949

* Make Booleans final and remove wrapping of `this` for getValues()
This commit is contained in:
Ryan Ernst 2016-10-17 11:11:42 -07:00 committed by GitHub
parent 5ec2ba3166
commit 3d3dd7185d
5 changed files with 69 additions and 34 deletions

View File

@ -291,4 +291,38 @@ public interface ScriptDocValues<T> extends List<T> {
return geohashDistance(geohash); return geohashDistance(geohash);
} }
} }
final class Booleans extends AbstractList<Boolean> implements ScriptDocValues<Boolean> {
private final SortedNumericDocValues values;
public Booleans(SortedNumericDocValues values) {
this.values = values;
}
@Override
public void setNextDocId(int docId) {
values.setDocument(docId);
}
@Override
public List<Boolean> getValues() {
return this;
}
public boolean getValue() {
return values.count() != 0 && values.valueAt(0) == 1;
}
@Override
public Boolean get(int index) {
return values.valueAt(index) == 1;
}
@Override
public int size() {
return values.count();
}
}
} }

View File

@ -19,28 +19,24 @@
package org.elasticsearch.index.fielddata.plain; package org.elasticsearch.index.fielddata.plain;
import org.apache.lucene.index.DocValues;
import org.apache.lucene.index.SortedNumericDocValues;
import org.apache.lucene.util.Accountable;
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.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;
import java.util.Collection;
import java.util.Collections;
/** /**
* Specialization of {@link AtomicNumericFieldData} for integers. * Specialization of {@link AtomicNumericFieldData} for integers.
*/ */
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;
AtomicLongFieldData(long ramBytesUsed) { AtomicLongFieldData(long ramBytesUsed, boolean isBoolean) {
this.ramBytesUsed = ramBytesUsed; this.ramBytesUsed = ramBytesUsed;
this.isBoolean = isBoolean;
} }
@Override @Override
@ -50,7 +46,11 @@ abstract class AtomicLongFieldData implements AtomicNumericFieldData {
@Override @Override
public final ScriptDocValues getScriptValues() { public final ScriptDocValues getScriptValues() {
return new ScriptDocValues.Longs(getLongValues()); if (isBoolean) {
return new ScriptDocValues.Booleans(getLongValues());
} else {
return new ScriptDocValues.Longs(getLongValues());
}
} }
@Override @Override
@ -63,24 +63,6 @@ abstract class AtomicLongFieldData implements AtomicNumericFieldData {
return FieldData.castToDouble(getLongValues()); return FieldData.castToDouble(getLongValues());
} }
public static AtomicNumericFieldData empty(final int maxDoc) {
return new AtomicLongFieldData(0) {
@Override
public SortedNumericDocValues getLongValues() {
return DocValues.emptySortedNumeric(maxDoc);
}
@Override
public Collection<Accountable> getChildResources() {
return Collections.emptyList();
}
};
}
@Override @Override
public void close() { public void close() {}
}
} }

View File

@ -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); return new SortedNumericLongFieldData(reader, field, numericType == NumericType.BOOLEAN);
} }
} }
@ -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) { SortedNumericLongFieldData(LeafReader reader, String field, boolean isBoolean) {
super(0L); super(0L, isBoolean);
this.reader = reader; this.reader = reader;
this.field = field; this.field = field;
} }

View File

@ -101,6 +101,12 @@ class org.elasticsearch.index.fielddata.ScriptDocValues.GeoPoints -> org.elastic
double geohashDistanceWithDefault(String,double) double geohashDistanceWithDefault(String,double)
} }
class org.elasticsearch.index.fielddata.ScriptDocValues.Booleans -> org.elasticsearch.index.fielddata.ScriptDocValues$Booleans extends List,Collection,Iterable,Object {
Boolean get(int)
boolean getValue()
List getValues()
}
# for testing. # for testing.
# currently FeatureTest exposes overloaded constructor, field load store, and overloaded static methods # currently FeatureTest exposes overloaded constructor, field load store, and overloaded static methods
class org.elasticsearch.painless.FeatureTest -> org.elasticsearch.painless.FeatureTest extends Object { class org.elasticsearch.painless.FeatureTest -> org.elasticsearch.painless.FeatureTest extends Object {

View File

@ -6,19 +6,19 @@
index: test index: test
type: test type: test
id: 1 id: 1
body: { "test": "value beck", "num1": 1.0 } body: { "test": "value beck", "num1": 1.0, "bool": true }
- do: - do:
index: index:
index: test index: test
type: test type: test
id: 2 id: 2
body: { "test": "value beck", "num1": 2.0 } body: { "test": "value beck", "num1": 2.0, "bool": false }
- do: - do:
index: index:
index: test index: test
type: test type: test
id: 3 id: 3
body: { "test": "value beck", "num1": 3.0 } body: { "test": "value beck", "num1": 3.0, "bool": true }
- do: - do:
indices.refresh: {} indices.refresh: {}
@ -95,6 +95,19 @@
- match: { hits.hits.1.fields.sNum1.0: 2.0 } - match: { hits.hits.1.fields.sNum1.0: 2.0 }
- match: { hits.hits.2.fields.sNum1.0: 3.0 } - match: { hits.hits.2.fields.sNum1.0: 3.0 }
- do:
index: test
search:
body:
query:
script:
script:
inline: "doc['bool'].value == false"
lang: painless
- match: { hits.total: 1 }
- match: { hits.hits.0._id: "2" }
--- ---
"Custom Script Boost": "Custom Script Boost":