mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-22 07:31:57 +00:00
Merge pull request #30793 from lingtaolf/feature/function_mapExtractKeyLike
Add new function mapExtractKeyLike
This commit is contained in:
commit
2050d0a33d
@ -392,5 +392,43 @@ Result:
|
||||
└─────────────────────────────┘
|
||||
```
|
||||
|
||||
## mapExtractKeyLike {#mapExtractKeyLike}
|
||||
|
||||
**Syntax**
|
||||
|
||||
```sql
|
||||
mapExtractKeyLike(map, pattern)
|
||||
```
|
||||
|
||||
**Parameters**
|
||||
|
||||
- `map` — Map. [Map](../../sql-reference/data-types/map.md).
|
||||
- `pattern` - String pattern to match.
|
||||
|
||||
**Returned value**
|
||||
|
||||
- A map contained elements the key of which matchs the specified pattern. If there are no elements matched the pattern, it will return an empty map.
|
||||
|
||||
**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 mapExtractKeyLike(a, 'a%') FROM test;
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```text
|
||||
┌─mapExtractKeyLike(a, 'a%')─┐
|
||||
│ {'abc':'abc'} │
|
||||
│ {} │
|
||||
└────────────────────────────┘
|
||||
```
|
||||
|
||||
|
||||
[Original article](https://clickhouse.com/docs/en/sql-reference/functions/tuple-map-functions/) <!--hide-->
|
||||
|
@ -382,6 +382,142 @@ public:
|
||||
bool useDefaultImplementationForConstants() const override { return true; }
|
||||
};
|
||||
|
||||
class FunctionExtractKeyLike : public IFunction
|
||||
{
|
||||
public:
|
||||
static constexpr auto name = "mapExtractKeyLike";
|
||||
static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionExtractKeyLike>(); }
|
||||
|
||||
String getName() const override
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*info*/) const override { return true; }
|
||||
|
||||
size_t getNumberOfArguments() const override { return 2; }
|
||||
|
||||
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<DataTypeMap>(arguments[0].type.get());
|
||||
|
||||
if (!map_type)
|
||||
throw Exception{"First argument for function " + getName() + " must be a map",
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
|
||||
|
||||
|
||||
auto key_type = map_type->getKeyType();
|
||||
|
||||
WhichDataType which(key_type);
|
||||
|
||||
if (!which.isStringOrFixedString())
|
||||
throw Exception{"Function " + getName() + "only support the map with String or FixedString key",
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
|
||||
|
||||
if (!isStringOrFixedString(arguments[1].type))
|
||||
throw Exception{"Second argument passed to function " + getName() + " must be String or FixedString",
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
|
||||
|
||||
return std::make_shared<DataTypeMap>(map_type->getKeyType(), map_type->getValueType());
|
||||
}
|
||||
|
||||
bool useDefaultImplementationForConstants() 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 = typeid_cast<const ColumnMap *>(arguments[0].column.get());
|
||||
|
||||
//It may not be necessary to check this condition, cause it will be checked in getReturnTypeImpl function
|
||||
if (!col_map)
|
||||
return nullptr;
|
||||
|
||||
const DataTypeMap * map_type = checkAndGetDataType<DataTypeMap>(arguments[0].type.get());
|
||||
auto key_type = map_type->getKeyType();
|
||||
auto value_type = map_type->getValueType();
|
||||
|
||||
const auto & nested_column = col_map->getNestedColumn();
|
||||
const auto & keys_column = col_map->getNestedData().getColumn(0);
|
||||
const auto & values_column = col_map->getNestedData().getColumn(1);
|
||||
const ColumnString * keys_string_column = checkAndGetColumn<ColumnString>(keys_column);
|
||||
const ColumnFixedString * keys_fixed_string_column = checkAndGetColumn<ColumnFixedString>(keys_column);
|
||||
|
||||
FunctionLike func_like;
|
||||
|
||||
//create result data
|
||||
MutableColumnPtr keys_data = key_type->createColumn();
|
||||
MutableColumnPtr values_data = value_type->createColumn();
|
||||
MutableColumnPtr offsets = DataTypeNumber<IColumn::Offset>().createColumn();
|
||||
|
||||
IColumn::Offset current_offset = 0;
|
||||
|
||||
for (size_t row = 0; row < input_rows_count; row++)
|
||||
{
|
||||
size_t element_start_row = row != 0 ? nested_column.getOffsets()[row-1] : 0;
|
||||
size_t element_size = nested_column.getOffsets()[row]- element_start_row;
|
||||
|
||||
ColumnsWithTypeAndName new_arguments;
|
||||
ColumnPtr sub_map_column;
|
||||
DataTypePtr data_type;
|
||||
|
||||
if (keys_string_column)
|
||||
{
|
||||
sub_map_column = keys_string_column->cut(element_start_row, element_size);
|
||||
data_type = std::make_shared<DataTypeString>();
|
||||
}
|
||||
else
|
||||
{
|
||||
sub_map_column = keys_fixed_string_column->cut(element_start_row, element_size);
|
||||
data_type =std::make_shared<DataTypeFixedString>(checkAndGetColumn<ColumnFixedString>(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);
|
||||
|
||||
new_arguments = {
|
||||
{
|
||||
column,
|
||||
data_type,
|
||||
""
|
||||
},
|
||||
arguments[1]
|
||||
};
|
||||
|
||||
auto res = func_like.executeImpl(new_arguments, result_type, input_rows_count);
|
||||
const auto & container = checkAndGetColumn<ColumnUInt8>(res.get())->getData();
|
||||
|
||||
for (size_t row_num = 0; row_num < element_size; row_num++)
|
||||
{
|
||||
if (container[row_num] == 1)
|
||||
{
|
||||
auto key_ref = keys_string_column ?
|
||||
keys_string_column->getDataAt(element_start_row + row_num) :
|
||||
keys_fixed_string_column->getDataAt(element_start_row + row_num);
|
||||
auto value_ref = values_column.getDataAt(element_start_row + row_num);
|
||||
|
||||
keys_data->insertData(key_ref.data, key_ref.size);
|
||||
values_data->insertData(value_ref.data, value_ref.size);
|
||||
current_offset += 1;
|
||||
}
|
||||
}
|
||||
|
||||
offsets->insert(current_offset);
|
||||
}
|
||||
|
||||
auto result_nested_column = ColumnArray::create(
|
||||
ColumnTuple::create(Columns{std::move(keys_data), std::move(values_data)}),
|
||||
std::move(offsets));
|
||||
|
||||
return ColumnMap::create(result_nested_column);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void registerFunctionsMap(FunctionFactory & factory)
|
||||
@ -391,6 +527,7 @@ void registerFunctionsMap(FunctionFactory & factory)
|
||||
factory.registerFunction<FunctionMapKeys>();
|
||||
factory.registerFunction<FunctionMapValues>();
|
||||
factory.registerFunction<FunctionMapContainsKeyLike>();
|
||||
factory.registerFunction<FunctionExtractKeyLike>();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,23 @@
|
||||
The data of table:
|
||||
1 {'P1-K1':'1-V1','P2-K2':'1-V2'}
|
||||
2 {'P1-K1':'2-V1','P2-K2':'2-V2'}
|
||||
3 {'P1-K1':'3-V1','P2-K2':'3-V2'}
|
||||
4 {'P1-K1':'4-V1','P2-K2':'4-V2'}
|
||||
5 {'5-K1':'5-V1','5-K2':'5-V2'}
|
||||
6 {'P3-K1':'6-V1','P4-K2':'6-V2'}
|
||||
|
||||
The results of query: SELECT id, mapExtractKeyLike(map, \'P1%\') FROM map_extractKeyLike_test ORDER BY id;
|
||||
1 {'P1-K1':'1-V1'}
|
||||
2 {'P1-K1':'2-V1'}
|
||||
3 {'P1-K1':'3-V1'}
|
||||
4 {'P1-K1':'4-V1'}
|
||||
5 {}
|
||||
6 {}
|
||||
|
||||
The results of query: SELECT id, mapExtractKeyLike(map, \'5-K1\') FROM map_extractKeyLike_test ORDER BY id;
|
||||
1 {}
|
||||
2 {}
|
||||
3 {}
|
||||
4 {}
|
||||
5 {'5-K1':'5-V1'}
|
||||
6 {}
|
@ -0,0 +1,22 @@
|
||||
DROP TABLE IF EXISTS map_extractKeyLike_test;
|
||||
|
||||
CREATE TABLE map_extractKeyLike_test (id UInt32, map Map(String, String)) Engine=MergeTree() ORDER BY id settings index_granularity=2;
|
||||
|
||||
INSERT INTO map_extractKeyLike_test VALUES (1, {'P1-K1':'1-V1','P2-K2':'1-V2'}),(2,{'P1-K1':'2-V1','P2-K2':'2-V2'});
|
||||
INSERT INTO map_extractKeyLike_test VALUES (3, {'P1-K1':'3-V1','P2-K2':'3-V2'}),(4,{'P1-K1':'4-V1','P2-K2':'4-V2'});
|
||||
INSERT INTO map_extractKeyLike_test VALUES (5, {'5-K1':'5-V1','5-K2':'5-V2'}),(6, {'P3-K1':'6-V1','P4-K2':'6-V2'});
|
||||
|
||||
SELECT 'The data of table:';
|
||||
SELECT * FROM map_extractKeyLike_test ORDER BY id;
|
||||
|
||||
SELECT '';
|
||||
|
||||
SELECT 'The results of query: SELECT id, mapExtractKeyLike(map, \'P1%\') FROM map_extractKeyLike_test ORDER BY id;';
|
||||
SELECT id, mapExtractKeyLike(map, 'P1%') FROM map_extractKeyLike_test ORDER BY id;
|
||||
|
||||
SELECT '';
|
||||
|
||||
SELECT 'The results of query: SELECT id, mapExtractKeyLike(map, \'5-K1\') FROM map_extractKeyLike_test ORDER BY id;';
|
||||
SELECT id, mapExtractKeyLike(map, '5-K1') FROM map_extractKeyLike_test ORDER BY id;
|
||||
|
||||
DROP TABLE map_extractKeyLike_test;
|
Loading…
Reference in New Issue
Block a user