diff --git a/evadb/functions/function_bootstrap_queries.py b/evadb/functions/function_bootstrap_queries.py index 0bf7c4ed9..f8186d4dd 100644 --- a/evadb/functions/function_bootstrap_queries.py +++ b/evadb/functions/function_bootstrap_queries.py @@ -214,6 +214,30 @@ EvaDB_INSTALLATION_DIR ) +Upper_function_query = """CREATE FUNCTION IF NOT EXISTS UPPER + INPUT (input ANYTYPE) + OUTPUT (output NDARRAY STR(ANYDIM)) + IMPL '{}/functions/helpers/upper.py'; + """.format( + EvaDB_INSTALLATION_DIR +) + +Lower_function_query = """CREATE FUNCTION IF NOT EXISTS LOWER + INPUT (input ANYTYPE) + OUTPUT (output NDARRAY STR(ANYDIM)) + IMPL '{}/functions/helpers/lower.py'; + """.format( + EvaDB_INSTALLATION_DIR +) + +Concat_function_query = """CREATE FUNCTION IF NOT EXISTS CONCAT + INPUT (input ANYTYPE) + OUTPUT (output NDARRAY STR(ANYDIM)) + IMPL '{}/functions/helpers/concat.py'; + """.format( + EvaDB_INSTALLATION_DIR +) + def init_builtin_functions(db: EvaDBDatabase, mode: str = "debug") -> None: """Load the built-in functions into the system during system bootstrapping. @@ -261,6 +285,9 @@ def init_builtin_functions(db: EvaDBDatabase, mode: str = "debug") -> None: Yolo_function_query, stablediffusion_function_query, dalle_function_query, + Upper_function_query, + Lower_function_query, + Concat_function_query, ] # if mode is 'debug', add debug functions diff --git a/evadb/functions/helpers/concat.py b/evadb/functions/helpers/concat.py new file mode 100644 index 000000000..7fdfcc5f2 --- /dev/null +++ b/evadb/functions/helpers/concat.py @@ -0,0 +1,38 @@ +# coding=utf-8 +# Copyright 2018-2023 EvaDB +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License 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. +import pandas as pd + +from evadb.functions.abstract.abstract_function import AbstractFunction + + +class Concat(AbstractFunction): + def setup(self): + pass + + @property + def name(self): + return "CONCAT" + + def forward(self, df: pd.DataFrame) -> pd.DataFrame: + """ + Concats all the df values into one. + + Returns: + ret (pd.DataFrame): Concatenated string. + """ + + ret = pd.DataFrame() + ret["output"] = pd.Series(df.fillna("").values.tolist()).str.join("") + return ret diff --git a/evadb/functions/helpers/lower.py b/evadb/functions/helpers/lower.py new file mode 100644 index 000000000..d8aced28e --- /dev/null +++ b/evadb/functions/helpers/lower.py @@ -0,0 +1,38 @@ +# coding=utf-8 +# Copyright 2018-2023 EvaDB +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License 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. +import pandas as pd + +from evadb.functions.abstract.abstract_function import AbstractFunction + + +class Lower(AbstractFunction): + def setup(self): + pass + + @property + def name(self): + return "LOWER" + + def forward(self, df: pd.DataFrame) -> pd.DataFrame: + """ + Converts the string to Lower Case. + + Returns: + ret (pd.DataFrame): String converted to Lower Case. + """ + + ret = pd.DataFrame() + ret["output"] = df.map(lambda s: s.lower() if type(s) is str else s) + return ret diff --git a/evadb/functions/helpers/upper.py b/evadb/functions/helpers/upper.py new file mode 100644 index 000000000..e1d3a7e5c --- /dev/null +++ b/evadb/functions/helpers/upper.py @@ -0,0 +1,38 @@ +# coding=utf-8 +# Copyright 2018-2023 EvaDB +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License 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. +import pandas as pd + +from evadb.functions.abstract.abstract_function import AbstractFunction + + +class Upper(AbstractFunction): + def setup(self): + pass + + @property + def name(self): + return "UPPER" + + def forward(self, df: pd.DataFrame) -> pd.DataFrame: + """ + Converts the string to Uppercase. + + Returns: + ret (pd.DataFrame): String converted to Upper Case. + """ + + ret = pd.DataFrame() + ret["output"] = df.map(lambda s: s.upper() if type(s) is str else s) + return ret diff --git a/test/integration_tests/long/functions/helpers/test_str_helper_functions.py b/test/integration_tests/long/functions/helpers/test_str_helper_functions.py new file mode 100644 index 000000000..3f5a49979 --- /dev/null +++ b/test/integration_tests/long/functions/helpers/test_str_helper_functions.py @@ -0,0 +1,108 @@ +# coding=utf-8 +# Copyright 2018-2023 EvaDB +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License 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. + +import unittest +from test.util import get_evadb_for_testing + +from evadb.server.command_handler import execute_query_fetch_all + + +class StrHelperTest(unittest.TestCase): + def setUp(self) -> None: + self.evadb = get_evadb_for_testing() + self.evadb.catalog().reset() + create_table_query = """CREATE TABLE IF NOT EXISTS Test ( + input TEXT); + """ + execute_query_fetch_all(self.evadb, create_table_query) + + test_prompts = ["EvaDB"] + + for prompt in test_prompts: + insert_query = f"""INSERT INTO Test (input) VALUES ('{prompt}')""" + execute_query_fetch_all(self.evadb, insert_query) + + def tearDown(self) -> None: + execute_query_fetch_all(self.evadb, "DROP TABLE IF EXISTS Test;") + + def test_upper_function(self): + function_name = "UPPER" + execute_query_fetch_all(self.evadb, f"DROP FUNCTION IF EXISTS {function_name};") + create_function_query = f"""CREATE FUNCTION IF NOT EXISTS {function_name} + INPUT (inp ANYTYPE) + OUTPUT (output NDARRAY STR(ANYDIM)) + TYPE HelperFunction + IMPL 'evadb/functions/helpers/upper.py'; + """ + execute_query_fetch_all(self.evadb, create_function_query) + query = "SELECT UPPER(input) FROM Test;" + output_batch = execute_query_fetch_all(self.evadb, query) + self.assertEqual(len(output_batch), 1) + self.assertEqual(output_batch.frames["upper.output"][0], "EVADB") + + query = "SELECT UPPER('test5')" + output_batch = execute_query_fetch_all(self.evadb, query) + self.assertEqual(len(output_batch), 1) + self.assertEqual(output_batch.frames["upper.output"][0], "TEST5") + + def test_lower_function(self): + function_name = "LOWER" + execute_query_fetch_all(self.evadb, f"DROP FUNCTION IF EXISTS {function_name};") + create_function_query = f"""CREATE FUNCTION IF NOT EXISTS {function_name} + INPUT (inp ANYTYPE) + OUTPUT (output NDARRAY STR(ANYDIM)) + TYPE HelperFunction + IMPL 'evadb/functions/helpers/lower.py'; + """ + execute_query_fetch_all(self.evadb, create_function_query) + query = "SELECT LOWER(input) FROM Test;" + output_batch = execute_query_fetch_all(self.evadb, query) + self.assertEqual(len(output_batch), 1) + self.assertEqual(output_batch.frames["lower.output"][0], "evadb") + + query = "SELECT LOWER('TEST5')" + output_batch = execute_query_fetch_all(self.evadb, query) + self.assertEqual(len(output_batch), 1) + self.assertEqual(output_batch.frames["lower.output"][0], "test5") + + def test_concat_function(self): + function_name = "CONCAT" + execute_query_fetch_all(self.evadb, f"DROP FUNCTION IF EXISTS {function_name};") + create_function_query = f"""CREATE FUNCTION IF NOT EXISTS {function_name} + INPUT (inp ANYTYPE) + OUTPUT (output NDARRAY STR(ANYDIM)) + TYPE HelperFunction + IMPL 'evadb/functions/helpers/concat.py'; + """ + execute_query_fetch_all(self.evadb, create_function_query) + + execute_query_fetch_all(self.evadb, "DROP FUNCTION IF EXISTS UPPER;") + create_function_query = """CREATE FUNCTION IF NOT EXISTS UPPER + INPUT (inp ANYTYPE) + OUTPUT (output NDARRAY STR(ANYDIM)) + TYPE HelperFunction + IMPL 'evadb/functions/helpers/upper.py'; + """ + + execute_query_fetch_all(self.evadb, create_function_query) + query = "SELECT CONCAT(UPPER('Eva'), 'DB');" + output_batch = execute_query_fetch_all(self.evadb, query) + self.assertEqual(len(output_batch), 1) + self.assertEqual(output_batch.frames["concat.output"][0], "EVADB") + + query = "SELECT CONCAT(input, '.com') FROM Test;" + output_batch = execute_query_fetch_all(self.evadb, query) + self.assertEqual(len(output_batch), 1) + self.assertEqual(output_batch.frames["concat.output"][0], "EvaDB.com")