From 388d71a130aa3cc30919a5a9f955e8d0acc2164a Mon Sep 17 00:00:00 2001 From: Arash M <27716912+am357@users.noreply.github.com> Date: Fri, 16 Sep 2022 16:40:03 -0700 Subject: [PATCH 1/3] Add an additional example for ExprFunction Adds an example for using a custom function for working with elements of lists and struct members. --- .../org/partiql/examples/MergeKeyValues.kt | 85 +++++++++++++++++++ .../partiql/examples/MergeKeyValuesTests.kt | 71 ++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 examples/src/kotlin/org/partiql/examples/MergeKeyValues.kt create mode 100644 examples/test/org/partiql/examples/MergeKeyValuesTests.kt diff --git a/examples/src/kotlin/org/partiql/examples/MergeKeyValues.kt b/examples/src/kotlin/org/partiql/examples/MergeKeyValues.kt new file mode 100644 index 0000000000..fcf56b6a9b --- /dev/null +++ b/examples/src/kotlin/org/partiql/examples/MergeKeyValues.kt @@ -0,0 +1,85 @@ +package org.partiql.examples + +import org.partiql.lang.eval.BindingCase +import org.partiql.lang.eval.BindingName +import org.partiql.lang.eval.EvaluationSession +import org.partiql.lang.eval.ExprFunction +import org.partiql.lang.eval.ExprValue +import org.partiql.lang.eval.ExprValueFactory +import org.partiql.lang.eval.ExprValueType +import org.partiql.lang.eval.StructOrdering +import org.partiql.lang.eval.namedValue +import org.partiql.lang.eval.stringValue +import org.partiql.lang.types.FunctionSignature +import org.partiql.lang.types.StaticType +import java.lang.Exception +import kotlin.collections.HashMap + +abstract class MergeKeysBaseExprFunction( + val valueFactory: ExprValueFactory, +) : ExprFunction + +/** + * For the Given [ExprValue] representing list of structs, merges key/values based on the given inputs in flatten list + * for values. + * + * E.g. + * Given: + * [ + * {'Name':'certificate','Values':['abc', 'cde']}, + * {'Name':'certificate','Values':['ghj', 'klu']}, + * {'Name':'test','Values':['ghj', 'klu']} + * ] + * 'Name' as mergeKey + * 'Values' as valueKey + * + * Expected: + * [ + * {'test': ['ghj', 'klu']}, + * {'certificate': ['abc', 'cde', 'ghj', 'klu']} + * ] + */ +class MergeKeyValues(valueFactory: ExprValueFactory) : + MergeKeysBaseExprFunction(valueFactory) { + override val signature = FunctionSignature( + name = "merge_key_values", + requiredParameters = listOf(StaticType.BAG, StaticType.STRING, StaticType.STRING), + returnType = StaticType.LIST + ) + + override fun callWithRequired(session: EvaluationSession, value: List): ExprValue { + val mergeKey = value[1].stringValue() + val valueKey = value [2].stringValue() + val result = HashMap>() + + value[0].forEach { + if (it.type != ExprValueType.STRUCT) { + throw Exception("All elements on input collection must be of type struct. Erroneous value: $it") + } + + val binding = it.bindings[BindingName(mergeKey, BindingCase.INSENSITIVE)] + val bindingValue = binding!!.stringValue() + val valueElem = it.bindings[BindingName(valueKey, BindingCase.INSENSITIVE)] + + if (valueElem != null) { + if (result[bindingValue] == null) { + result[bindingValue] = mutableListOf(valueElem) + } else { + result[bindingValue]!!.add(valueElem) + } + } + } + + val keys = result.keys.map { valueFactory.newString(it) } + val values = result.values.map { valueFactory.newList(it).flatten() } + + val listOfStructs = keys.zip(values) + .map { + valueFactory.newStruct( + listOf(valueFactory.newList(it.second).namedValue(it.first)).asSequence(), + StructOrdering.UNORDERED + ) + } + return valueFactory.newList(listOfStructs) + } +} diff --git a/examples/test/org/partiql/examples/MergeKeyValuesTests.kt b/examples/test/org/partiql/examples/MergeKeyValuesTests.kt new file mode 100644 index 0000000000..61fa5f4d97 --- /dev/null +++ b/examples/test/org/partiql/examples/MergeKeyValuesTests.kt @@ -0,0 +1,71 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at: + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + */ +package org.partiql.examples + +import com.amazon.ion.system.IonSystemBuilder +import junit.framework.TestCase.assertEquals +import junit.framework.TestCase.assertNotNull +import org.junit.Test +import org.partiql.lang.eval.EvaluationSession +import org.partiql.lang.eval.ExprValueFactory +import org.partiql.lang.eval.StructOrdering +import org.partiql.lang.eval.namedValue + +class MergeKeyValuesTests { + private val ion = IonSystemBuilder.standard().build() + private val factory = ExprValueFactory.standard(ion) + private val session = EvaluationSession.standard() + + @Test + fun testFunction() { + val fn = MergeKeyValues(factory) + + val ionValue1 = ion.newList(ion.newString("abc"), ion.newString("cde")) + val ionValue2 = ion.newList(ion.newString("ghj"), ion.newString("klu")) + val ionValue3 = ion.newList(ion.newString("ghj"), ion.newString("klu")) + + val list1 = listOf( + factory.newString("certificate").namedValue(factory.newSymbol("Name")), + factory.newFromIonValue(ionValue1).namedValue(factory.newSymbol("Values")), + ) + val list2 = listOf( + factory.newString("certificate").namedValue(factory.newSymbol("Name")), + factory.newFromIonValue(ionValue2).namedValue(factory.newSymbol("Values")), + ) + val list3 = listOf( + factory.newString("test").namedValue(factory.newSymbol("Name")), + factory.newFromIonValue(ionValue3).namedValue(factory.newSymbol("Values")), + ) + val res = fn.callWithRequired( + session, + listOf( + factory.newBag( + listOf( + factory.newStruct(list1.asSequence(), StructOrdering.UNORDERED), + factory.newStruct(list2.asSequence(), StructOrdering.UNORDERED), + factory.newStruct(list3.asSequence(), StructOrdering.UNORDERED) + ) + ), + factory.newString("Name"), + factory.newString("Values") + ) + ) + + assertNotNull(res) + assertEquals( + "[{'test': ['ghj', 'klu']}, {'certificate': ['abc', 'cde', 'ghj', 'klu']}]", + res.toString() + ) + } +} From 001dbd8483d782e402fef38179ae8d8394c9e1dd Mon Sep 17 00:00:00 2001 From: Arash Maymandi <27716912+am357@users.noreply.github.com> Date: Mon, 19 Sep 2022 14:35:50 -0700 Subject: [PATCH 2/3] Update examples/src/kotlin/org/partiql/examples/MergeKeyValues.kt Co-authored-by: Alan Cai --- examples/src/kotlin/org/partiql/examples/MergeKeyValues.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/kotlin/org/partiql/examples/MergeKeyValues.kt b/examples/src/kotlin/org/partiql/examples/MergeKeyValues.kt index fcf56b6a9b..0eb65c7a81 100644 --- a/examples/src/kotlin/org/partiql/examples/MergeKeyValues.kt +++ b/examples/src/kotlin/org/partiql/examples/MergeKeyValues.kt @@ -20,7 +20,7 @@ abstract class MergeKeysBaseExprFunction( ) : ExprFunction /** - * For the Given [ExprValue] representing list of structs, merges key/values based on the given inputs in flatten list + * For the given [ExprValue] representing list of structs, merges key/values based on the given inputs in flatten list * for values. * * E.g. From 147790965d4776a485cd893f2c97c84adfa1b4ea Mon Sep 17 00:00:00 2001 From: Arash M <27716912+am357@users.noreply.github.com> Date: Tue, 20 Sep 2022 17:28:06 -0700 Subject: [PATCH 3/3] Address Alan's comments --- .../org/partiql/examples/MergeKeyValues.kt | 16 +++-- .../partiql/examples/MergeKeyValuesTests.kt | 62 +++++++++++++++++-- 2 files changed, 68 insertions(+), 10 deletions(-) diff --git a/examples/src/kotlin/org/partiql/examples/MergeKeyValues.kt b/examples/src/kotlin/org/partiql/examples/MergeKeyValues.kt index 0eb65c7a81..8a8a7346be 100644 --- a/examples/src/kotlin/org/partiql/examples/MergeKeyValues.kt +++ b/examples/src/kotlin/org/partiql/examples/MergeKeyValues.kt @@ -20,7 +20,7 @@ abstract class MergeKeysBaseExprFunction( ) : ExprFunction /** - * For the given [ExprValue] representing list of structs, merges key/values based on the given inputs in flatten list + * For the Given [ExprValue] representing collection of structs, merges key/values based on the given inputs in flatten list * for values. * * E.g. @@ -43,16 +43,20 @@ class MergeKeyValues(valueFactory: ExprValueFactory) : MergeKeysBaseExprFunction(valueFactory) { override val signature = FunctionSignature( name = "merge_key_values", - requiredParameters = listOf(StaticType.BAG, StaticType.STRING, StaticType.STRING), + requiredParameters = listOf( + StaticType.unionOf(StaticType.BAG, StaticType.LIST, StaticType.SEXP), + StaticType.STRING, + StaticType.STRING + ), returnType = StaticType.LIST ) - override fun callWithRequired(session: EvaluationSession, value: List): ExprValue { - val mergeKey = value[1].stringValue() - val valueKey = value [2].stringValue() + override fun callWithRequired(session: EvaluationSession, required: List): ExprValue { + val mergeKey = required[1].stringValue() + val valueKey = required[2].stringValue() val result = HashMap>() - value[0].forEach { + required[0].forEach { if (it.type != ExprValueType.STRUCT) { throw Exception("All elements on input collection must be of type struct. Erroneous value: $it") } diff --git a/examples/test/org/partiql/examples/MergeKeyValuesTests.kt b/examples/test/org/partiql/examples/MergeKeyValuesTests.kt index 61fa5f4d97..1cf994f906 100644 --- a/examples/test/org/partiql/examples/MergeKeyValuesTests.kt +++ b/examples/test/org/partiql/examples/MergeKeyValuesTests.kt @@ -16,6 +16,7 @@ package org.partiql.examples import com.amazon.ion.system.IonSystemBuilder import junit.framework.TestCase.assertEquals import junit.framework.TestCase.assertNotNull +import org.junit.Assert.assertThrows import org.junit.Test import org.partiql.lang.eval.EvaluationSession import org.partiql.lang.eval.ExprValueFactory @@ -47,7 +48,7 @@ class MergeKeyValuesTests { factory.newString("test").namedValue(factory.newSymbol("Name")), factory.newFromIonValue(ionValue3).namedValue(factory.newSymbol("Values")), ) - val res = fn.callWithRequired( + val res1 = fn.callWithRequired( session, listOf( factory.newBag( @@ -62,10 +63,63 @@ class MergeKeyValuesTests { ) ) - assertNotNull(res) + val res2 = fn.callWithRequired( + session, + listOf( + factory.newSexp( + listOf( + factory.newStruct(list1.asSequence(), StructOrdering.UNORDERED), + factory.newStruct(list2.asSequence(), StructOrdering.UNORDERED), + factory.newStruct(list3.asSequence(), StructOrdering.UNORDERED) + ) + ), + factory.newString("Name"), + factory.newString("Values") + ) + ) + + val res3 = fn.callWithRequired( + session, + listOf( + factory.newList( + listOf( + factory.newStruct(list1.asSequence(), StructOrdering.UNORDERED), + factory.newStruct(list2.asSequence(), StructOrdering.UNORDERED), + factory.newStruct(list3.asSequence(), StructOrdering.UNORDERED) + ) + ), + factory.newString("Name"), + factory.newString("Values") + ) + ) + + setOf(res1, res2, res3).forEach { + assertNotNull(it) + assertEquals( + "[{'test': ['ghj', 'klu']}, {'certificate': ['abc', 'cde', 'ghj', 'klu']}]", + it.toString() + ) + } + + val ex = assertThrows(Exception::class.java) { + fn.callWithRequired( + session, + listOf( + factory.newList( + listOf( + factory.newInt(10), + factory.newStruct(list2.asSequence(), StructOrdering.UNORDERED), + ) + ), + factory.newString("Name"), + factory.newString("Values") + ) + ) + } + assertEquals( - "[{'test': ['ghj', 'klu']}, {'certificate': ['abc', 'cde', 'ghj', 'klu']}]", - res.toString() + "All elements on input collection must be of type struct. Erroneous value: 10", + ex.message ) } }