From d4d97d7b9a1f37ee59729080edfbfde55fb0a57a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=87=8C=E6=B6=9B?= Date: Tue, 28 Sep 2021 16:24:06 +0800 Subject: [PATCH 1/3] add new function mapContainsKeyLike fixed error because of inconsistent offsets --- src/Functions/MatchImpl.h | 5 + src/Functions/like.cpp | 9 -- src/Functions/map.cpp | 122 ++++++++++++++++++ ...2030_function_mapContainsKeyLike.reference | 6 + .../02030_function_mapContainsKeyLike.sql | 12 ++ 5 files changed, 145 insertions(+), 9 deletions(-) create mode 100644 tests/queries/0_stateless/02030_function_mapContainsKeyLike.reference create mode 100644 tests/queries/0_stateless/02030_function_mapContainsKeyLike.sql diff --git a/src/Functions/MatchImpl.h b/src/Functions/MatchImpl.h index 7dc0712023f..4849fe6e12d 100644 --- a/src/Functions/MatchImpl.h +++ b/src/Functions/MatchImpl.h @@ -419,4 +419,9 @@ struct MatchImpl } }; +struct NameLike +{ + static constexpr auto name = "like"; +}; + } diff --git a/src/Functions/like.cpp b/src/Functions/like.cpp index 1ac9a8d7dab..5d2d5de1be2 100644 --- a/src/Functions/like.cpp +++ b/src/Functions/like.cpp @@ -5,19 +5,10 @@ namespace DB { -namespace -{ - -struct NameLike -{ - static constexpr auto name = "like"; -}; using LikeImpl = MatchImpl; using FunctionLike = FunctionsStringSearch; -} - void registerFunctionLike(FunctionFactory & factory) { factory.registerFunction(); diff --git a/src/Functions/map.cpp b/src/Functions/map.cpp index 5517dced3e0..f84312ba6dc 100644 --- a/src/Functions/map.cpp +++ b/src/Functions/map.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -15,6 +16,8 @@ #include #include #include "array/arrayIndex.h" +#include "Functions/MatchImpl.h" +#include "Functions/FunctionsStringSearch.h" namespace DB @@ -28,6 +31,9 @@ namespace ErrorCodes namespace { +using LikeImpl = MatchImpl; +using FunctionLike = FunctionsStringSearch; + // map(x, y, ...) is a function that allows you to make key-value pair class FunctionMap : public IFunction { @@ -274,6 +280,121 @@ public: } }; +class FunctionMapContainsKeyLike : public IFunction +{ +public: + static constexpr auto name = "mapContainsKeyLike"; + static FunctionPtr create(ContextPtr) { return std::make_shared(); } + String getName() const override { return name; } + bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*info*/) const override { return true; } + + ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count) const override + { + bool is_const = isColumnConst(*arguments[0].column); + const ColumnMap * col_map = is_const ? checkAndGetColumnConstData(arguments[0].column.get()) + : checkAndGetColumn(arguments[0].column.get()); + const DataTypeMap * map_type = checkAndGetDataType(arguments[0].type.get()); + if (!col_map || !map_type) + throw Exception{"First argument for function " + getName() + " must be a map", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT}; + + auto col_res = ColumnVector::create(); + typename ColumnVector::Container & vec_res = col_res->getData(); + + if (input_rows_count == 0) + return col_res; + + vec_res.resize(input_rows_count); + + const auto & column_array = typeid_cast(col_map->getNestedColumn()); + const auto & column_tuple = typeid_cast(column_array.getData()); + + const ColumnString * column_string = checkAndGetColumn(column_tuple.getColumn(0)); + const ColumnFixedString * column_fixed_string = checkAndGetColumn(column_tuple.getColumn(0)); + + FunctionLike func_like; + + for (size_t row = 0; row < input_rows_count; row++) + { + size_t element_start_row = row != 0 ? column_array.getOffsets()[row-1] : 0; + size_t elem_size = column_array.getOffsets()[row]- element_start_row; + + ColumnPtr sub_map_column; + DataTypePtr data_type; + + //The keys of one row map will be processed as a single ColumnString + if (column_string) + { + sub_map_column = column_string->cut(element_start_row, elem_size); + data_type = std::make_shared(); + } + else + { + sub_map_column = column_fixed_string->cut(element_start_row, elem_size); + data_type = std::make_shared(checkAndGetColumn(sub_map_column.get())->getN()); + } + + size_t col_key_size = sub_map_column->size(); + auto column = is_const? ColumnConst::create(std::move(sub_map_column), std::move(col_key_size)) : std::move(sub_map_column); + + ColumnsWithTypeAndName new_arguments = + { + { + column, + data_type, + "" + }, + arguments[1] + }; + + auto res = func_like.executeImpl(new_arguments, result_type, input_rows_count); + + const auto & container = checkAndGetColumn(res.get())->getData(); + + bool exist = 0; + for (auto iter = container.begin(); iter != container.end(); iter++) + { + if (*iter == 1) + { + exist = 1; + break; + } + } + + vec_res[row] = exist; + } + + return col_res; + } + + DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override + { + if (arguments.size() != 2) + throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " + + toString(arguments.size()) + ", should be 2", + ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + + const DataTypeMap * map_type = checkAndGetDataType(arguments[0].type.get()); + const DataTypeString * pattern_type = checkAndGetDataType(arguments[1].type.get()); + + if (!map_type) + throw Exception{"First argument for function " + getName() + " must be a Map", + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT}; + if (!pattern_type) + throw Exception{"Second argument for function " + getName() + " must be String", + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT}; + + if (!isStringOrFixedString(map_type->getKeyType())) + throw Exception{"Key type of map for function " + getName() + " must be `String` or `FixedString`", + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT}; + + return std::make_shared(); + } + + size_t getNumberOfArguments() const override { return 2; } + + bool useDefaultImplementationForConstants() const override { return true; } +}; + } void registerFunctionsMap(FunctionFactory & factory) @@ -282,6 +403,7 @@ void registerFunctionsMap(FunctionFactory & factory) factory.registerFunction(); factory.registerFunction(); factory.registerFunction(); + factory.registerFunction(); } } diff --git a/tests/queries/0_stateless/02030_function_mapContainsKeyLike.reference b/tests/queries/0_stateless/02030_function_mapContainsKeyLike.reference new file mode 100644 index 00000000000..eb8bc83384e --- /dev/null +++ b/tests/queries/0_stateless/02030_function_mapContainsKeyLike.reference @@ -0,0 +1,6 @@ +1 {'1-K1':'1-V1','1-K2':'1-V2'} +1 {'1-K1':'1-V1','1-K2':'1-V2'} +2 {'2-K1':'2-V1','2-K2':'2-V2'} +4 {'4-K1':'4-V1','4-K2':'4-V2'} +5 {'5-K1':'5-V1','5-K2':'5-V2'} +6 {'6-K1':'6-V1','6-K2':'6-V2'} diff --git a/tests/queries/0_stateless/02030_function_mapContainsKeyLike.sql b/tests/queries/0_stateless/02030_function_mapContainsKeyLike.sql new file mode 100644 index 00000000000..7d9722b4c90 --- /dev/null +++ b/tests/queries/0_stateless/02030_function_mapContainsKeyLike.sql @@ -0,0 +1,12 @@ +DROP TABLE IF EXISTS map_containsKeyLike_test; + +CREATE TABLE map_containsKeyLike_test (id UInt32, map Map(String, String)) Engine=MergeTree() ORDER BY id settings index_granularity=2; + +INSERT INTO map_containsKeyLike_test VALUES (1, {'1-K1':'1-V1','1-K2':'1-V2'}),(2,{'2-K1':'2-V1','2-K2':'2-V2'}); +INSERT INTO map_containsKeyLike_test VALUES (3, {'3-K1':'3-V1','3-K2':'3-V2'}),(4, {'4-K1':'4-V1','4-K2':'4-V2'}); +INSERT INTO map_containsKeyLike_test VALUES (5, {'5-K1':'5-V1','5-K2':'5-V2'}),(6, {'6-K1':'6-V1','6-K2':'6-V2'}); + +SELECT id, map FROM map_containsKeyLike_test WHERE mapContainsKeyLike(map, '1-%') = 1; +SELECT id, map FROM map_containsKeyLike_test WHERE mapContainsKeyLike(map, '3-%') = 0 order by id; + +DROP TABLE map_containsKeyLike_test; From fb6716f4acb902b0947989cde8db613e4f7cce77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=87=8C=E6=B6=9B?= Date: Wed, 27 Oct 2021 11:56:55 +0800 Subject: [PATCH 2/3] add doc --- .../functions/tuple-map-functions.md | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/docs/en/sql-reference/functions/tuple-map-functions.md b/docs/en/sql-reference/functions/tuple-map-functions.md index 6ddac9a0530..46ce350377c 100644 --- a/docs/en/sql-reference/functions/tuple-map-functions.md +++ b/docs/en/sql-reference/functions/tuple-map-functions.md @@ -350,6 +350,45 @@ Result: │ ['eleven','11'] │ │ ['twelve','6.0'] │ └──────────────────┘ -``` +``` + +## mapContainsKeyLike {#mapContainsKeyLike} + +**Syntax** + +```sql +mapContainsKeyLike(map, pattern) +``` + +**Parameters** + +- `map` — Map. [Map](../../sql-reference/data-types/map.md). +- `pattern` - String pattern to match. + +**Returned value** + +- `1` if `map` contains `key` like specified pattern, `0` if not. + +**Example** + +Query: + +```sql +CREATE TABLE test (a Map(String,String)) ENGINE = Memory; + +INSERT INTO test VALUES ({'abc':'abc','def':'def'}), ({'hij':'hij','klm':'klm'}); + +SELECT mapContainsKeyLike(a, 'a%') FROM test; +``` + +Result: + +```text +┌─mapContainsKeyLike(a, 'a%')─┐ +│ 1 │ +│ 0 │ +└─────────────────────────────┘ +``` + [Original article](https://clickhouse.com/docs/en/sql-reference/functions/tuple-map-functions/) From 996706101dfd408473ca3554487ad5d59758f914 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=87=8C=E6=B6=9B?= Date: Wed, 27 Oct 2021 15:14:42 +0800 Subject: [PATCH 3/3] optimized code --- src/Functions/MatchImpl.h | 5 ----- src/Functions/like.cpp | 5 +---- src/Functions/like.h | 17 +++++++++++++++++ src/Functions/map.cpp | 19 +++---------------- 4 files changed, 21 insertions(+), 25 deletions(-) create mode 100644 src/Functions/like.h diff --git a/src/Functions/MatchImpl.h b/src/Functions/MatchImpl.h index 4849fe6e12d..7dc0712023f 100644 --- a/src/Functions/MatchImpl.h +++ b/src/Functions/MatchImpl.h @@ -419,9 +419,4 @@ struct MatchImpl } }; -struct NameLike -{ - static constexpr auto name = "like"; -}; - } diff --git a/src/Functions/like.cpp b/src/Functions/like.cpp index 5d2d5de1be2..bd73aa4d471 100644 --- a/src/Functions/like.cpp +++ b/src/Functions/like.cpp @@ -1,14 +1,11 @@ #include "FunctionsStringSearch.h" #include "FunctionFactory.h" -#include "MatchImpl.h" +#include "like.h" namespace DB { -using LikeImpl = MatchImpl; -using FunctionLike = FunctionsStringSearch; - void registerFunctionLike(FunctionFactory & factory) { factory.registerFunction(); diff --git a/src/Functions/like.h b/src/Functions/like.h new file mode 100644 index 00000000000..a00891ec64c --- /dev/null +++ b/src/Functions/like.h @@ -0,0 +1,17 @@ +#pragma once + +#include "MatchImpl.h" +#include "FunctionsStringSearch.h" + +namespace DB +{ + +struct NameLike +{ + static constexpr auto name = "like"; +}; + +using LikeImpl = MatchImpl; +using FunctionLike = FunctionsStringSearch; + +} diff --git a/src/Functions/map.cpp b/src/Functions/map.cpp index f84312ba6dc..edb0c28c980 100644 --- a/src/Functions/map.cpp +++ b/src/Functions/map.cpp @@ -16,7 +16,7 @@ #include #include #include "array/arrayIndex.h" -#include "Functions/MatchImpl.h" +#include "Functions/like.h" #include "Functions/FunctionsStringSearch.h" @@ -31,9 +31,6 @@ namespace ErrorCodes namespace { -using LikeImpl = MatchImpl; -using FunctionLike = FunctionsStringSearch; - // map(x, y, ...) is a function that allows you to make key-value pair class FunctionMap : public IFunction { @@ -347,20 +344,10 @@ public: }; auto res = func_like.executeImpl(new_arguments, result_type, input_rows_count); - const auto & container = checkAndGetColumn(res.get())->getData(); - bool exist = 0; - for (auto iter = container.begin(); iter != container.end(); iter++) - { - if (*iter == 1) - { - exist = 1; - break; - } - } - - vec_res[row] = exist; + const auto it = std::find_if(container.begin(), container.end(), [](int element){ return element == 1; }); // NOLINT + vec_res[row] = it == container.end() ? 0 : 1; } return col_res;