Skip to content

Commit

Permalink
ESQL: LEAST and GREATEST functions
Browse files Browse the repository at this point in the history
Adds `LEAST` and `GREATEST` functions to find the min or max of
the values in many colunms.
  • Loading branch information
nik9000 committed Aug 17, 2023
1 parent 44e6134 commit ad83b30
Show file tree
Hide file tree
Showing 23 changed files with 1,402 additions and 86 deletions.
4 changes: 4 additions & 0 deletions docs/reference/esql/esql-functions.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ these functions:
* <<esql-date_trunc>>
* <<esql-e>>
* <<esql-floor>>
* <<esql-greatest>>
* <<esql-is_finite>>
* <<esql-is_infinite>>
* <<esql-is_nan>>
* <<esql-least>>
* <<esql-length>>
* <<esql-log10>>
* <<esql-ltrim>>
Expand Down Expand Up @@ -84,9 +86,11 @@ include::functions/date_parse.asciidoc[]
include::functions/date_trunc.asciidoc[]
include::functions/e.asciidoc[]
include::functions/floor.asciidoc[]
include::functions/greatest.asciidoc[]
include::functions/is_finite.asciidoc[]
include::functions/is_infinite.asciidoc[]
include::functions/is_nan.asciidoc[]
include::functions/least.asciidoc[]
include::functions/length.asciidoc[]
include::functions/log10.asciidoc[]
include::functions/ltrim.asciidoc[]
Expand Down
16 changes: 16 additions & 0 deletions docs/reference/esql/functions/greatest.asciidoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[[esql-greatest]]
=== `GREATEST`

Returns the maximum value from many columns.

[source.merge.styled,esql]
----
include::{esql-specs}/math.csv-spec[tag=greatest]
----
[%header.monospaced.styled,format=dsv,separator=|]
|===
include::{esql-specs}/math.csv-spec[tag=greatest-result]
|===

NOTE: When run on `keyword` or `text` fields, this'll return the last string
in alphabetical order.
16 changes: 16 additions & 0 deletions docs/reference/esql/functions/least.asciidoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[[esql-least]]
=== `LEAST`

Returns the minimum value from many columns.

[source.merge.styled,esql]
----
include::{esql-specs}/math.csv-spec[tag=least]
----
[%header.monospaced.styled,format=dsv,separator=|]
|===
include::{esql-specs}/math.csv-spec[tag=least-result]
|===

NOTE: When run on `keyword` or `text` fields, this'll return the first string
in alphabetical order.
Original file line number Diff line number Diff line change
Expand Up @@ -988,3 +988,41 @@ row d = 1/0.0 | eval s = is_infinite(sqrt(d));
d:double | s:boolean
Infinity | true
;

least
// tag::least[]
ROW a = 10, b=20
| EVAL l = LEAST(a, b);
// end::least[]

// tag::least-result[]
a:integer | b:integer | l:integer
10 | 20 | 10
// end::least-result[]
;

leastNull
ROW l=LEAST(10, 5, null);

l:integer
null
;

greatest
// tag::greatest[]
ROW a = 10, b=20
| EVAL g = GREATEST(a, b);
// end::greatest[]

// tag::greatest-result[]
a:integer | b:integer | g:integer
10 | 20 | 20
// end::greatest-result[]
;

greatestNull
ROW g=GREATEST(10, 5, null);

g:integer
null
;
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ date_parse |date_parse(arg1, arg2)
date_trunc |date_trunc(arg1, arg2)
e |e()
floor |floor(arg1)
greatest |greatest(arg1...)
is_finite |is_finite(arg1)
is_infinite |is_infinite(arg1)
is_nan |is_nan(arg1)
least |least(arg1...)
length |length(arg1)
log10 |log10(arg1)
ltrim |ltrim(arg1)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// 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; you may not use this file except in compliance with the Elastic License
// 2.0.
package org.elasticsearch.xpack.esql.expression.function.scalar.math;

import java.lang.Override;
import java.lang.String;
import java.util.Arrays;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.compute.data.Block;
import org.elasticsearch.compute.data.BytesRefBlock;
import org.elasticsearch.compute.data.BytesRefVector;
import org.elasticsearch.compute.data.Page;
import org.elasticsearch.compute.operator.EvalOperator;

/**
* {@link EvalOperator.ExpressionEvaluator} implementation for {@link Greatest}.
* This class is generated. Do not edit it.
*/
public final class GreatestBytesRefEvaluator implements EvalOperator.ExpressionEvaluator {
private final EvalOperator.ExpressionEvaluator[] values;

public GreatestBytesRefEvaluator(EvalOperator.ExpressionEvaluator[] values) {
this.values = values;
}

@Override
public Block eval(Page page) {
BytesRefBlock[] valuesBlocks = new BytesRefBlock[values.length];
for (int i = 0; i < valuesBlocks.length; i++) {
Block block = values[i].eval(page);
if (block.areAllValuesNull()) {
return Block.constantNullBlock(page.getPositionCount());
}
valuesBlocks[i] = (BytesRefBlock) block;
}
BytesRefVector[] valuesVectors = new BytesRefVector[values.length];
for (int i = 0; i < valuesBlocks.length; i++) {
valuesVectors[i] = valuesBlocks[i].asVector();
if (valuesVectors[i] == null) {
return eval(page.getPositionCount(), valuesBlocks);
}
}
return eval(page.getPositionCount(), valuesVectors).asBlock();
}

public BytesRefBlock eval(int positionCount, BytesRefBlock[] valuesBlocks) {
BytesRefBlock.Builder result = BytesRefBlock.newBlockBuilder(positionCount);
BytesRef[] valuesValues = new BytesRef[values.length];
BytesRef[] valuesScratch = new BytesRef[values.length];
for (int i = 0; i < values.length; i++) {
valuesScratch[i] = new BytesRef();
}
position: for (int p = 0; p < positionCount; p++) {
for (int i = 0; i < valuesBlocks.length; i++) {
if (valuesBlocks[i].isNull(p) || valuesBlocks[i].getValueCount(p) != 1) {
result.appendNull();
continue position;
}
}
// unpack valuesBlocks into valuesValues
for (int i = 0; i < valuesBlocks.length; i++) {
int o = valuesBlocks[i].getFirstValueIndex(p);
valuesValues[i] = valuesBlocks[i].getBytesRef(o, valuesScratch[i]);
}
result.appendBytesRef(Greatest.process(valuesValues));
}
return result.build();
}

public BytesRefVector eval(int positionCount, BytesRefVector[] valuesVectors) {
BytesRefVector.Builder result = BytesRefVector.newVectorBuilder(positionCount);
BytesRef[] valuesValues = new BytesRef[values.length];
BytesRef[] valuesScratch = new BytesRef[values.length];
for (int i = 0; i < values.length; i++) {
valuesScratch[i] = new BytesRef();
}
position: for (int p = 0; p < positionCount; p++) {
// unpack valuesVectors into valuesValues
for (int i = 0; i < valuesVectors.length; i++) {
valuesValues[i] = valuesVectors[i].getBytesRef(p, valuesScratch[i]);
}
result.appendBytesRef(Greatest.process(valuesValues));
}
return result.build();
}

@Override
public String toString() {
return "GreatestBytesRefEvaluator[" + "values=" + Arrays.toString(values) + "]";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// 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; you may not use this file except in compliance with the Elastic License
// 2.0.
package org.elasticsearch.xpack.esql.expression.function.scalar.math;

import java.lang.Override;
import java.lang.String;
import java.util.Arrays;
import org.elasticsearch.compute.data.Block;
import org.elasticsearch.compute.data.DoubleBlock;
import org.elasticsearch.compute.data.DoubleVector;
import org.elasticsearch.compute.data.Page;
import org.elasticsearch.compute.operator.EvalOperator;

/**
* {@link EvalOperator.ExpressionEvaluator} implementation for {@link Greatest}.
* This class is generated. Do not edit it.
*/
public final class GreatestDoubleEvaluator implements EvalOperator.ExpressionEvaluator {
private final EvalOperator.ExpressionEvaluator[] values;

public GreatestDoubleEvaluator(EvalOperator.ExpressionEvaluator[] values) {
this.values = values;
}

@Override
public Block eval(Page page) {
DoubleBlock[] valuesBlocks = new DoubleBlock[values.length];
for (int i = 0; i < valuesBlocks.length; i++) {
Block block = values[i].eval(page);
if (block.areAllValuesNull()) {
return Block.constantNullBlock(page.getPositionCount());
}
valuesBlocks[i] = (DoubleBlock) block;
}
DoubleVector[] valuesVectors = new DoubleVector[values.length];
for (int i = 0; i < valuesBlocks.length; i++) {
valuesVectors[i] = valuesBlocks[i].asVector();
if (valuesVectors[i] == null) {
return eval(page.getPositionCount(), valuesBlocks);
}
}
return eval(page.getPositionCount(), valuesVectors).asBlock();
}

public DoubleBlock eval(int positionCount, DoubleBlock[] valuesBlocks) {
DoubleBlock.Builder result = DoubleBlock.newBlockBuilder(positionCount);
double[] valuesValues = new double[values.length];
position: for (int p = 0; p < positionCount; p++) {
for (int i = 0; i < valuesBlocks.length; i++) {
if (valuesBlocks[i].isNull(p) || valuesBlocks[i].getValueCount(p) != 1) {
result.appendNull();
continue position;
}
}
// unpack valuesBlocks into valuesValues
for (int i = 0; i < valuesBlocks.length; i++) {
int o = valuesBlocks[i].getFirstValueIndex(p);
valuesValues[i] = valuesBlocks[i].getDouble(o);
}
result.appendDouble(Greatest.process(valuesValues));
}
return result.build();
}

public DoubleVector eval(int positionCount, DoubleVector[] valuesVectors) {
DoubleVector.Builder result = DoubleVector.newVectorBuilder(positionCount);
double[] valuesValues = new double[values.length];
position: for (int p = 0; p < positionCount; p++) {
// unpack valuesVectors into valuesValues
for (int i = 0; i < valuesVectors.length; i++) {
valuesValues[i] = valuesVectors[i].getDouble(p);
}
result.appendDouble(Greatest.process(valuesValues));
}
return result.build();
}

@Override
public String toString() {
return "GreatestDoubleEvaluator[" + "values=" + Arrays.toString(values) + "]";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// 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; you may not use this file except in compliance with the Elastic License
// 2.0.
package org.elasticsearch.xpack.esql.expression.function.scalar.math;

import java.lang.Override;
import java.lang.String;
import java.util.Arrays;
import org.elasticsearch.compute.data.Block;
import org.elasticsearch.compute.data.IntBlock;
import org.elasticsearch.compute.data.IntVector;
import org.elasticsearch.compute.data.Page;
import org.elasticsearch.compute.operator.EvalOperator;

/**
* {@link EvalOperator.ExpressionEvaluator} implementation for {@link Greatest}.
* This class is generated. Do not edit it.
*/
public final class GreatestIntEvaluator implements EvalOperator.ExpressionEvaluator {
private final EvalOperator.ExpressionEvaluator[] values;

public GreatestIntEvaluator(EvalOperator.ExpressionEvaluator[] values) {
this.values = values;
}

@Override
public Block eval(Page page) {
IntBlock[] valuesBlocks = new IntBlock[values.length];
for (int i = 0; i < valuesBlocks.length; i++) {
Block block = values[i].eval(page);
if (block.areAllValuesNull()) {
return Block.constantNullBlock(page.getPositionCount());
}
valuesBlocks[i] = (IntBlock) block;
}
IntVector[] valuesVectors = new IntVector[values.length];
for (int i = 0; i < valuesBlocks.length; i++) {
valuesVectors[i] = valuesBlocks[i].asVector();
if (valuesVectors[i] == null) {
return eval(page.getPositionCount(), valuesBlocks);
}
}
return eval(page.getPositionCount(), valuesVectors).asBlock();
}

public IntBlock eval(int positionCount, IntBlock[] valuesBlocks) {
IntBlock.Builder result = IntBlock.newBlockBuilder(positionCount);
int[] valuesValues = new int[values.length];
position: for (int p = 0; p < positionCount; p++) {
for (int i = 0; i < valuesBlocks.length; i++) {
if (valuesBlocks[i].isNull(p) || valuesBlocks[i].getValueCount(p) != 1) {
result.appendNull();
continue position;
}
}
// unpack valuesBlocks into valuesValues
for (int i = 0; i < valuesBlocks.length; i++) {
int o = valuesBlocks[i].getFirstValueIndex(p);
valuesValues[i] = valuesBlocks[i].getInt(o);
}
result.appendInt(Greatest.process(valuesValues));
}
return result.build();
}

public IntVector eval(int positionCount, IntVector[] valuesVectors) {
IntVector.Builder result = IntVector.newVectorBuilder(positionCount);
int[] valuesValues = new int[values.length];
position: for (int p = 0; p < positionCount; p++) {
// unpack valuesVectors into valuesValues
for (int i = 0; i < valuesVectors.length; i++) {
valuesValues[i] = valuesVectors[i].getInt(p);
}
result.appendInt(Greatest.process(valuesValues));
}
return result.build();
}

@Override
public String toString() {
return "GreatestIntEvaluator[" + "values=" + Arrays.toString(values) + "]";
}
}
Loading

0 comments on commit ad83b30

Please sign in to comment.