Skip to content

Commit 5c06aad

Browse files
committed
Add support for list sort ascending null last
This fixes #768
1 parent 7482b7b commit 5c06aad

File tree

8 files changed

+107
-1
lines changed

8 files changed

+107
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
sidebar_position: 8
3+
---
4+
5+
# List Functions
6+
7+
## List Sort Ascending Null Last
8+
9+
*This function has no substrait equivalent*
10+
11+
Sorts a list of values in ascending order, placing any null values at the end of the result.
12+
13+
Values are ordered according to their natural ascending order (e.g., numerically or lexicographically).
14+
Nulls are not compared to other values directly; they are always considered greater for the purpose of ordering.
15+
Any value that is not a list will return the result as null.
16+
17+
### SQL Usage
18+
19+
```sql
20+
SELECT list_sort_asc_null_last(list(orderkey, userkey)) FROM ...
21+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Licensed under the Apache License, Version 2.0 (the "License")
2+
// you may not use this file except in compliance with the License.
3+
// You may obtain a copy of the License at
4+
//
5+
// http://www.apache.org/licenses/LICENSE-2.0
6+
//
7+
// Unless required by applicable law or agreed to in writing, software
8+
// distributed under the License is distributed on an "AS IS" BASIS,
9+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
// See the License for the specific language governing permissions and
11+
// limitations under the License.
12+
13+
using FlowtideDotNet.Core.ColumnStore;
14+
using FlowtideDotNet.Core.ColumnStore.Comparers;
15+
using FlowtideDotNet.Core.ColumnStore.DataValues;
16+
using FlowtideDotNet.Substrait.FunctionExtensions;
17+
using System;
18+
using System.Collections.Generic;
19+
using System.Linq;
20+
using System.Text;
21+
using System.Threading.Tasks;
22+
23+
namespace FlowtideDotNet.Core.Compute.Columnar.Functions
24+
{
25+
internal static class BuiltInListFunctions
26+
{
27+
public static void AddBuiltInListFunctions(IFunctionsRegister functionsRegister)
28+
{
29+
functionsRegister.RegisterScalarMethod(FunctionsList.Uri, FunctionsList.ListSortAscendingNullLast, typeof(BuiltInListFunctions), nameof(ListSortAscendingNullLast));
30+
}
31+
32+
private static IDataValue ListSortAscendingNullLast<T>(T value)
33+
where T : IDataValue
34+
{
35+
if (value.Type == ArrowTypeId.List)
36+
{
37+
var list = value.AsList;
38+
IDataValue[] newList = new IDataValue[list.Count];
39+
for (int i = 0; i < list.Count; i++)
40+
{
41+
newList[i] = list.GetAt(i);
42+
}
43+
44+
Array.Sort(newList, SortFieldCompareCompiler.CompareAscendingNullsLastImplementation);
45+
return new ListValue(newList);
46+
}
47+
return NullValue.Instance;
48+
}
49+
}
50+
}

src/FlowtideDotNet.Core/Compute/Internal/BuiltinFunctions.cs

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public static void RegisterFunctions(FunctionsRegister functionsRegister)
3030
Columnar.Functions.BuiltInRoundingFunctions.AddRoundingFunctions(functionsRegister);
3131
BuiltInCheckFunctions.RegisterCheckFunctions(functionsRegister);
3232
Columnar.Functions.BuiltInStructFunctions.AddBuiltInStructFunctions(functionsRegister);
33+
Columnar.Functions.BuiltInListFunctions.AddBuiltInListFunctions(functionsRegister);
3334

3435
BuiltInComparisonFunctions.AddComparisonFunctions(functionsRegister);
3536
BuiltInBooleanFunctions.AddBooleanFunctions(functionsRegister);

src/FlowtideDotNet.Substrait/FunctionExtensions/FunctionsList.cs

+1
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@ public static class FunctionsList
1717
public const string Uri = "/functions_list.yaml";
1818
public const string ListAgg = "list_agg";
1919
public const string ListUnionDistinctAgg = "list_union_distinct_agg";
20+
public const string ListSortAscendingNullLast = "list_sort_asc_null_last";
2021
}
2122
}

src/FlowtideDotNet.Substrait/Sql/Internal/BuiltInSqlFunctions.cs

+2
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,8 @@ public static void AddBuiltInFunctions(SqlFunctionRegister sqlFunctionRegister)
875875

876876
RegisterOneVariableScalarFunction(sqlFunctionRegister, "floor_timestamp_day", FunctionsDatetime.Uri, FunctionsDatetime.FloorTimestampDay);
877877

878+
RegisterOneVariableScalarFunction(sqlFunctionRegister, "list_sort_asc_null_last", FunctionsList.Uri, FunctionsList.ListSortAscendingNullLast);
879+
878880
// Table functions
879881
UnnestSqlFunction.AddUnnest(sqlFunctionRegister);
880882

tests/FlowtideDotNet.AcceptanceTests/ListTests.cs

+21
Original file line numberDiff line numberDiff line change
@@ -189,5 +189,26 @@ FROM testdata
189189
new {list = new List<object>(){ 2, "a", "c" }}
190190
});
191191
}
192+
193+
[Fact]
194+
public async Task ListSortAscendingNullLast()
195+
{
196+
GenerateData();
197+
await StartStream(@"
198+
INSERT INTO output
199+
SELECT
200+
list_sort_asc_null_last(list(orderkey, userkey))
201+
FROM orders
202+
");
203+
204+
await WaitForUpdate();
205+
206+
var expectedList = Orders.Select(x => new
207+
{
208+
list = (new List<int>() { x.OrderKey, x.UserKey }).OrderBy(x => x).ToList()
209+
} );
210+
211+
AssertCurrentDataEqual(expectedList);
212+
}
192213
}
193214
}

tests/FlowtideDotNet.ComputeTests/Internal/Parser/Tests/DataTypeValueWriter.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ internal static class DataTypeValueWriter
2020
{
2121
public static IDataValue WriteDataValue(string value, string dataType)
2222
{
23-
if (value == "Null")
23+
if (value.Equals("Null", StringComparison.OrdinalIgnoreCase))
2424
{
2525
return NullValue.Instance;
2626
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
### SUBSTRAIT_SCALAR_TEST: v1.0
2+
### SUBSTRAIT_INCLUDE: '/extensions/functions_list.yaml'
3+
4+
# basic: Basic examples without any special cases
5+
list_sort_asc_null_last([3, 2, 1]::LIST<i32>) = [1, 2, 3]::LIST<i32>
6+
list_sort_asc_null_last(['c', 'a']::LIST<str>) = ['a', 'c']::LIST<str>
7+
8+
# null_input: Examples with null as input
9+
list_sort_asc_null_last([3, 2, null]::LIST<i32>) = [2, 3, null]::LIST<i32>
10+
list_sort_asc_null_last([null, 3, 2]::LIST<i32>) = [2, 3, null]::LIST<i32>

0 commit comments

Comments
 (0)