Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Script: ulong via fields API #76519

Merged
merged 16 commits into from
Aug 17, 2021
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,18 @@

# API
class org.elasticsearch.script.Field {
org.elasticsearch.script.Converter BigInteger
org.elasticsearch.script.Converter Long
String getName()
boolean isEmpty()
List getValues()
def getValue(def)
double getDouble(double)
long getLong(long)
org.elasticsearch.script.Field as(org.elasticsearch.script.Converter)
}

class org.elasticsearch.script.Converter {
}

class org.elasticsearch.script.DocBasedScript {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,69 @@ setup:
script: "field('sval.keyword').getValue('b') == 'a'"
- match: { hits.total: 1 }
- match: { hits.hits.0._id: d3 }

---
"missing field":
- do:
indices.create:
index: test
body:
settings:
number_of_shards: 2
mappings:
properties:
dval:
type: double
- do:
index:
index: test
id: d1
body: {"foo": 25, "dval": 1.0 }
- do:
index:
index: test
id: d2
body: {"foo": 9223372036854775807, "dval": 2.0 }
- do:
index:
index: test
id: d3
body: { "bar": "abc", "dval": 3.0 }
- do:
indices.refresh: {}
- do:
search:
rest_total_hits_as_int: true
index: test
body:
query: { "match_all": {} }
sort: [ { dval: asc } ]
script_fields:
missing_field:
script:
source: "field('foo').as(Field.BigInteger).getValue(BigInteger.TEN).add(BigInteger.ONE)"
- match: { hits.total: 3 }
- match: { hits.hits.0._id: d1 }
- match: { hits.hits.0.fields.missing_field.0: 26 }
- match: { hits.hits.1._id: d2 }
- match: { hits.hits.1.fields.missing_field.0: 9223372036854775808 }
- match: { hits.hits.2._id: d3 }
- match: { hits.hits.2.fields.missing_field.0: 11 }
- do:
search:
rest_total_hits_as_int: true
index: test
body:
query: { "match_all": {} }
sort: [ { dval: asc } ]
script_fields:
missing_field:
script:
source: "field('foo').as(Field.BigInteger).as(Field.Long).getValue(10L) + 2L"
- match: { hits.total: 3 }
- match: { hits.hits.0._id: d1 }
- match: { hits.hits.0.fields.missing_field.0: 27 }
- match: { hits.hits.1._id: d2 }
- match: { hits.hits.1.fields.missing_field.0: -9223372036854775809 }
- match: { hits.hits.2._id: d3 }
- match: { hits.hits.2.fields.missing_field.0: 12 }
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
import org.elasticsearch.common.geo.GeoUtils;
import org.elasticsearch.common.time.DateUtils;
import org.elasticsearch.geometry.utils.Geohash;
import org.elasticsearch.script.Field;
import org.elasticsearch.script.FieldValues;
import org.elasticsearch.script.InvalidConversion;
import org.elasticsearch.script.JodaCompatibleZonedDateTime;

import java.io.IOException;
Expand All @@ -25,6 +28,8 @@
import java.util.AbstractList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.UnaryOperator;

/**
Expand All @@ -35,7 +40,7 @@
* return as a single {@link ScriptDocValues} instance can be reused to return
* values form multiple documents.
*/
public abstract class ScriptDocValues<T> extends AbstractList<T> {
public abstract class ScriptDocValues<T> extends AbstractList<T> implements FieldValues<T> {

/**
* Set the current doc ID.
Expand Down Expand Up @@ -68,6 +73,31 @@ public final void sort(Comparator<? super T> c) {
throw new UnsupportedOperationException("doc values are unmodifiable");
}

public abstract Field<T> toField(String fieldName);

public List<T> getValues() {
return this;
}

public T getNonPrimitiveValue() {
return get(0);
}

public long getLongValue() {
throw new InvalidConversion(this.getClass(), long.class);
}

public double getDoubleValue() {
throw new InvalidConversion(this.getClass(), double.class);
}

protected void throwIfEmpty() {
if (size() == 0) {
throw new IllegalStateException("A document doesn't have a value for a field! " +
"Use doc[<field>].size()==0 to check if a document is missing a field!");
}
}

public static final class Longs extends ScriptDocValues<Long> {
private final SortedNumericDocValues in;
private long[] values = new long[0];
Expand Down Expand Up @@ -107,17 +137,31 @@ public long getValue() {

@Override
public Long get(int index) {
if (count == 0) {
throw new IllegalStateException("A document doesn't have a value for a field! " +
"Use doc[<field>].size()==0 to check if a document is missing a field!");
}
throwIfEmpty();
return values[index];
}

@Override
public int size() {
return count;
}

@Override
public long getLongValue() {
throwIfEmpty();
return values[0];
}

@Override
public double getDoubleValue() {
throwIfEmpty();
return values[0];
}

@Override
public Field<Long> toField(String fieldName) {
return new Field.LongField(fieldName, this);
}
}

public static final class Dates extends ScriptDocValues<JodaCompatibleZonedDateTime> {
Expand Down Expand Up @@ -192,6 +236,29 @@ void refreshArray() throws IOException {
}
}
}

@Override
public long getLongValue() {
throwIfEmpty();
Instant dt = dates[0].toInstant();
if (isNanos) {
return TimeUnit.SECONDS.toNanos(dt.getEpochSecond()) + dt.getNano();
}
return dt.toEpochMilli();
}

@Override
public double getDoubleValue() {
return getLongValue();
}

@Override
public Field<JodaCompatibleZonedDateTime> toField(String fieldName) {
if (isNanos) {
return new Field.DateNanosField(fieldName, this);
}
return new Field.DateMillisField(fieldName, this);
}
}

public static final class Doubles extends ScriptDocValues<Double> {
Expand Down Expand Up @@ -246,6 +313,22 @@ public Double get(int index) {
public int size() {
return count;
}

@Override
public long getLongValue() {
return (long) getDoubleValue();
}

@Override
public double getDoubleValue() {
throwIfEmpty();
return values[0];
}

@Override
public Field<Double> toField(String fieldName) {
return new Field.DoubleField(fieldName, this);
}
}

public abstract static class Geometry<T> extends ScriptDocValues<T> {
Expand Down Expand Up @@ -436,6 +519,11 @@ public double getMercatorHeight() {
public GeoBoundingBox getBoundingBox() {
return size() == 0 ? null : boundingBox;
}

@Override
public Field<GeoPoint> toField(String fieldName) {
return new Field.GeoPointField(fieldName, this);
}
}

public static final class Booleans extends ScriptDocValues<Boolean> {
Expand Down Expand Up @@ -496,6 +584,22 @@ private static boolean[] grow(boolean[] array, int minSize) {
return array;
}

@Override
public long getLongValue() {
throwIfEmpty();
return values[0] ? 1L : 0L;
}

@Override
public double getDoubleValue() {
throwIfEmpty();
return values[0] ? 1.0D : 0.0D;
}

@Override
public Field<Boolean> toField(String fieldName) {
return new Field.BooleanField(fieldName, this);
}
}

abstract static class BinaryScriptDocValues<T> extends ScriptDocValues<T> {
Expand Down Expand Up @@ -568,6 +672,21 @@ protected String bytesToString(BytesRef bytes) {
public final String getValue() {
return get(0);
}

@Override
public long getLongValue() {
return Long.parseLong(get(0));
}

@Override
public double getDoubleValue() {
return Double.parseDouble(get(0));
}

@Override
public Field<String> toField(String fieldName) {
return new Field.StringField(fieldName, this);
}
}

public static final class BytesRefs extends BinaryScriptDocValues<BytesRef> {
Expand All @@ -594,5 +713,9 @@ public BytesRef getValue() {
return get(0);
}

@Override
public Field<BytesRef> toField(String fieldName) {
return new Field.BytesRefField(fieldName, this);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,11 @@ public String get(int index) {
public int size() {
return count;
}

@Override
public org.elasticsearch.script.Field<String> toField(String fieldName) {
return new org.elasticsearch.script.Field.IpField(fieldName, this);
}
}

@Override
Expand Down
31 changes: 31 additions & 0 deletions server/src/main/java/org/elasticsearch/script/Converter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

package org.elasticsearch.script;

/**
* Converts between one scripting {@link Field} type and another, {@code CF}, with a different underlying
* value type, {@code CT}.
*/
public interface Converter<CT, CF extends Field<CT>> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe these should be TC for target class and FC for Field class?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed.

/**
* Convert {@code sourceField} to a new field-type. Conversions come from user scripts so {@code covert} may
* be called on a {@link Field}'s own type.
*/
CF convert(Field<?> sourceField);

/**
* The destination {@link Field} class.
*/
Class<CF> getFieldClass();

/**
* The target value type.
*/
Class<CT> getTargetClass();
}
Loading