From 94e5d2739883aa72af15e530021c181e00a0ca36 Mon Sep 17 00:00:00 2001 From: Bernie Chen Date: Thu, 11 Sep 2025 20:08:35 -0400 Subject: [PATCH 1/9] wip --- src/function/function_collection.cpp | 1 + src/function/struct/CMakeLists.txt | 3 +- src/function/struct/properties_function.cpp | 40 +++++++++++++++++++ .../function/struct/vector_struct_functions.h | 10 +++++ 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 src/function/struct/properties_function.cpp diff --git a/src/function/function_collection.cpp b/src/function/function_collection.cpp index f54cd664496..751e3f7cc62 100644 --- a/src/function/function_collection.cpp +++ b/src/function/function_collection.cpp @@ -178,6 +178,7 @@ FunctionCollection* FunctionCollection::getFunctions() { // Struct functions SCALAR_FUNCTION(StructPackFunctions), SCALAR_FUNCTION(StructExtractFunctions), REWRITE_FUNCTION(KeysFunctions), + SCALAR_FUNCTION(PropertiesFunctions), // Map functions SCALAR_FUNCTION(MapCreationFunctions), SCALAR_FUNCTION(MapExtractFunctions), diff --git a/src/function/struct/CMakeLists.txt b/src/function/struct/CMakeLists.txt index 79c1971dcfb..d898b7a5259 100644 --- a/src/function/struct/CMakeLists.txt +++ b/src/function/struct/CMakeLists.txt @@ -2,7 +2,8 @@ add_library(kuzu_function_struct OBJECT struct_extract_function.cpp struct_pack_function.cpp - keys_function.cpp) + keys_function.cpp + properties_function.cpp) set(ALL_OBJECT_FILES ${ALL_OBJECT_FILES} $ diff --git a/src/function/struct/properties_function.cpp b/src/function/struct/properties_function.cpp new file mode 100644 index 00000000000..b3181422db6 --- /dev/null +++ b/src/function/struct/properties_function.cpp @@ -0,0 +1,40 @@ +#include "binder/expression/expression_util.h" +#include "binder/expression/literal_expression.h" +#include "binder/expression/scalar_function_expression.h" +#include "binder/expression_binder.h" +#include "function/rewrite_function.h" +#include "function/struct/vector_struct_functions.h" + +using namespace kuzu::common; +using namespace kuzu::binder; +using namespace kuzu::catalog; + +namespace kuzu { +namespace function { + +void PropertiesFunctions::compileFunc(FunctionBindData* bindData, + const std::vector>& parameters, + std::shared_ptr& result) { + KU_ASSERT(parameters[0]->dataType.getPhysicalType() == PhysicalTypeID::STRUCT); + result = parameters[0]; +} + +static std::unique_ptr getPropertiesFunction(LogicalTypeID logicalTypeID) { + auto function = std::make_unique(PropertiesFunctions::name, + std::vector{logicalTypeID, LogicalTypeID::STRING}, LogicalTypeID::STRUCT); + function->compileFunc = PropertiesFunctions::compileFunc; + return function; +} + +function_set PropertiesFunctions::getFunctionSet() { + function_set functions; + auto inputTypeIDs = + std::vector{LogicalTypeID::STRUCT, LogicalTypeID::NODE, LogicalTypeID::REL}; + for (auto inputTypeID : inputTypeIDs) { + functions.push_back(getPropertiesFunction(inputTypeID)); + } + return functions; +} + +} // namespace function +} // namespace kuzu diff --git a/src/include/function/struct/vector_struct_functions.h b/src/include/function/struct/vector_struct_functions.h index 9904558ce53..028c774ebc4 100644 --- a/src/include/function/struct/vector_struct_functions.h +++ b/src/include/function/struct/vector_struct_functions.h @@ -55,5 +55,15 @@ struct KeysFunctions { static function_set getFunctionSet(); }; +struct PropertiesFunctions { + static constexpr const char* name = "PROPERTIE"; + + static function_set getFunctionSet(); + + static void compileFunc(FunctionBindData* bindData, + const std::vector>& parameters, + std::shared_ptr& result); +}; + } // namespace function } // namespace kuzu From ae83910b120608596a80fb27d59bf45d02b7b291 Mon Sep 17 00:00:00 2001 From: CI Bot Date: Fri, 12 Sep 2025 00:11:11 +0000 Subject: [PATCH 2/9] ci: auto code format --- src/function/function_collection.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/function/function_collection.cpp b/src/function/function_collection.cpp index 751e3f7cc62..19b90a99662 100644 --- a/src/function/function_collection.cpp +++ b/src/function/function_collection.cpp @@ -177,8 +177,7 @@ FunctionCollection* FunctionCollection::getFunctions() { // Struct functions SCALAR_FUNCTION(StructPackFunctions), SCALAR_FUNCTION(StructExtractFunctions), - REWRITE_FUNCTION(KeysFunctions), - SCALAR_FUNCTION(PropertiesFunctions), + REWRITE_FUNCTION(KeysFunctions), SCALAR_FUNCTION(PropertiesFunctions), // Map functions SCALAR_FUNCTION(MapCreationFunctions), SCALAR_FUNCTION(MapExtractFunctions), From fbfd1e888f6c37bd4f9aac610f776fcdb9363de5 Mon Sep 17 00:00:00 2001 From: Bernie Chen Date: Fri, 12 Sep 2025 13:44:16 -0400 Subject: [PATCH 3/9] implement STRUCT_PROPERTIES(NODE | REL | STRUCT) --- src/function/function_collection.cpp | 3 +- src/function/struct/properties_function.cpp | 47 ++++++++++++++++--- .../function/struct/vector_struct_functions.h | 16 ++++++- 3 files changed, 57 insertions(+), 9 deletions(-) diff --git a/src/function/function_collection.cpp b/src/function/function_collection.cpp index 19b90a99662..672ad25a3b9 100644 --- a/src/function/function_collection.cpp +++ b/src/function/function_collection.cpp @@ -177,7 +177,8 @@ FunctionCollection* FunctionCollection::getFunctions() { // Struct functions SCALAR_FUNCTION(StructPackFunctions), SCALAR_FUNCTION(StructExtractFunctions), - REWRITE_FUNCTION(KeysFunctions), SCALAR_FUNCTION(PropertiesFunctions), + REWRITE_FUNCTION(KeysFunctions), + SCALAR_FUNCTION(StructPropertiesFunctions), // Map functions SCALAR_FUNCTION(MapCreationFunctions), SCALAR_FUNCTION(MapExtractFunctions), diff --git a/src/function/struct/properties_function.cpp b/src/function/struct/properties_function.cpp index b3181422db6..cd1e5ee0436 100644 --- a/src/function/struct/properties_function.cpp +++ b/src/function/struct/properties_function.cpp @@ -2,6 +2,7 @@ #include "binder/expression/literal_expression.h" #include "binder/expression/scalar_function_expression.h" #include "binder/expression_binder.h" +#include "common/exception/binder.h" #include "function/rewrite_function.h" #include "function/struct/vector_struct_functions.h" @@ -12,21 +13,55 @@ using namespace kuzu::catalog; namespace kuzu { namespace function { -void PropertiesFunctions::compileFunc(FunctionBindData* bindData, +std::unique_ptr StructPropertiesFunctions::bindFunc( + const ScalarBindFuncInput& input) { + std::vector fields; + const auto& structType = input.arguments[0]->getDataType(); + auto keys = StructType::getFieldNames(structType); + std::vector fieldIdxs; + for (auto& key : keys) { + if (key == InternalKeyword::ID || key == InternalKeyword::LABEL || + key == InternalKeyword::SRC || key == InternalKeyword::DST) { + continue; + } + auto fieldIdx = StructType::getFieldIdx(structType, key); + auto fieldType = StructType::getField(structType, fieldIdx).getType().copy(); + if (fieldIdx == INVALID_STRUCT_FIELD_IDX) { + throw BinderException(stringFormat("Invalid struct field name: {}.", key)); + } + fieldIdxs.push_back(fieldIdx); + fields.emplace_back(key, std::move(fieldType)); + } + const auto resultType = LogicalType::STRUCT(std::move(fields)); + auto bindData = std::make_unique(resultType.copy(), fieldIdxs); + bindData->paramTypes.push_back(input.arguments[0]->getDataType().copy()); + return bindData; +} + +void StructPropertiesFunctions::compileFunc(FunctionBindData* bindData, const std::vector>& parameters, std::shared_ptr& result) { KU_ASSERT(parameters[0]->dataType.getPhysicalType() == PhysicalTypeID::STRUCT); - result = parameters[0]; + auto& propertiesBindData = bindData->cast(); + std::vector> fieldVectors; + for (auto fieldIdx : propertiesBindData.childIdxs) { + auto fieldVector = StructVector::getFieldVector(parameters[0].get(), fieldIdx); + fieldVectors.push_back(fieldVector); + } + for (auto i = 0u; i < fieldVectors.size(); ++i) { + StructVector::referenceVector(result.get(), i, fieldVectors[i]); + } } static std::unique_ptr getPropertiesFunction(LogicalTypeID logicalTypeID) { - auto function = std::make_unique(PropertiesFunctions::name, - std::vector{logicalTypeID, LogicalTypeID::STRING}, LogicalTypeID::STRUCT); - function->compileFunc = PropertiesFunctions::compileFunc; + auto function = std::make_unique(StructPropertiesFunctions::name, + std::vector{logicalTypeID}, LogicalTypeID::STRUCT); + function->bindFunc = StructPropertiesFunctions::bindFunc; + function->compileFunc = StructPropertiesFunctions::compileFunc; return function; } -function_set PropertiesFunctions::getFunctionSet() { +function_set StructPropertiesFunctions::getFunctionSet() { function_set functions; auto inputTypeIDs = std::vector{LogicalTypeID::STRUCT, LogicalTypeID::NODE, LogicalTypeID::REL}; diff --git a/src/include/function/struct/vector_struct_functions.h b/src/include/function/struct/vector_struct_functions.h index 028c774ebc4..7823c9110ea 100644 --- a/src/include/function/struct/vector_struct_functions.h +++ b/src/include/function/struct/vector_struct_functions.h @@ -55,11 +55,23 @@ struct KeysFunctions { static function_set getFunctionSet(); }; -struct PropertiesFunctions { - static constexpr const char* name = "PROPERTIE"; +struct StructPropertiesBindData : public FunctionBindData { + std::vector childIdxs; + + StructPropertiesBindData(common::LogicalType dataType, std::vector childIdxs) + : FunctionBindData{std::move(dataType)}, childIdxs{std::move(childIdxs)} {} + + std::unique_ptr copy() const override { + return std::make_unique(resultType.copy(), childIdxs); + } +}; + +struct StructPropertiesFunctions { + static constexpr const char* name = "STRUCT_PROPERTIES"; static function_set getFunctionSet(); + static std::unique_ptr bindFunc(const ScalarBindFuncInput& input); static void compileFunc(FunctionBindData* bindData, const std::vector>& parameters, std::shared_ptr& result); From 7a7ec683e741f831e1bf9332fa21362d7a8a7442 Mon Sep 17 00:00:00 2001 From: CI Bot Date: Fri, 12 Sep 2025 17:46:21 +0000 Subject: [PATCH 4/9] ci: auto code format --- src/function/function_collection.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/function/function_collection.cpp b/src/function/function_collection.cpp index 672ad25a3b9..77012823513 100644 --- a/src/function/function_collection.cpp +++ b/src/function/function_collection.cpp @@ -177,8 +177,7 @@ FunctionCollection* FunctionCollection::getFunctions() { // Struct functions SCALAR_FUNCTION(StructPackFunctions), SCALAR_FUNCTION(StructExtractFunctions), - REWRITE_FUNCTION(KeysFunctions), - SCALAR_FUNCTION(StructPropertiesFunctions), + REWRITE_FUNCTION(KeysFunctions), SCALAR_FUNCTION(StructPropertiesFunctions), // Map functions SCALAR_FUNCTION(MapCreationFunctions), SCALAR_FUNCTION(MapExtractFunctions), From cdedc7bbfd82dc18928a1db934b065b1a4719adb Mon Sep 17 00:00:00 2001 From: Bernie Chen Date: Fri, 12 Sep 2025 14:19:18 -0400 Subject: [PATCH 5/9] write STRUCT_PROPERTIES tests in struct.test --- test/test_files/function/struct.test | 65 ++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/test/test_files/function/struct.test b/test/test_files/function/struct.test index 50d72c315f7..f34463e781a 100644 --- a/test/test_files/function/struct.test +++ b/test/test_files/function/struct.test @@ -34,6 +34,13 @@ C [name,length,note,description,content,audience,grade] [name,length,note,description,content,audience,grade] +-LOG PropertiesSingleNodeLabel +-STATEMENT MATCH (p:movies) RETURN struct_properties(p) +---- 3 +{name: Roma, length: 298, note: the movie is very interesting and funny, description: {rating: 1223.000000, stars: 100, views: 10003, release: 2011-02-11 16:44:22, release_ns: 2011-02-11 16:44:22.123456, release_ms: 2011-02-11 16:44:22.123, release_sec: 2011-02-11 16:44:22, release_tz: 2011-02-11 16:44:22.123456+00, film: 2013-02-22, u8: 1, u16: 15, u32: 200, u64: 4, hugedata: -15}, content: pure ascii characters, audience: {}, grade: 254.000000} +{name: Sóló cón tu párejâ, length: 126, note: this is a very very good movie, description: {rating: 5.300000, stars: 2, views: 152, release: 2011-08-20 11:25:30, release_ns: 2011-08-20 11:25:30.123456, release_ms: 2011-08-20 11:25:30.123, release_sec: 2011-08-20 11:25:30, release_tz: 2011-08-20 11:25:30.123456+00, film: 2012-05-11, u8: 220, u16: 20, u32: 1, u64: 180, hugedata: 1844674407370955161811111111}, content: \xAA\xABinteresting\x0B, audience: {audience1=52, audience53=42}, grade: True} +{name: The 😂😃🧘🏻‍♂️🌍🌦️🍞🚗 movie, length: 2544, note: the movie is very very good, description: {rating: 7.000000, stars: 10, views: 982, release: 2018-11-13 13:33:11, release_ns: 2018-11-13 13:33:11.123456, release_ms: 2018-11-13 13:33:11.123, release_sec: 2018-11-13 13:33:11, release_tz: 2018-11-13 13:33:11.123456+00, film: 2014-09-12, u8: 12, u16: 120, u32: 55, u64: 1, hugedata: -1844674407370955161511}, content: \xAB\xCD, audience: {audience1=33}, grade: 8.989000} + -LOG KeysMultiNodeLabel -STATEMENT MATCH (p:person:organisation) RETURN keys(p) ---- 11 @@ -49,6 +56,21 @@ C [ID,fName,gender,isStudent,isWorker,age,eyeSight,birthdate,registerTime,lastJobDuration,workedHours,usedNames,courseScoresPerTerm,grades,height,u,name,orgCode,mark,score,history,licenseValidInterval,rating,state,info] [ID,fName,gender,isStudent,isWorker,age,eyeSight,birthdate,registerTime,lastJobDuration,workedHours,usedNames,courseScoresPerTerm,grades,height,u,name,orgCode,mark,score,history,licenseValidInterval,rating,state,info] +-LOG PropertiesMultiNodeLabel +-STATEMENT MATCH (p:person:organisation) RETURN struct_properties(p) +---- 11 +{ID: 0, fName: Alice, gender: 1, isStudent: True, isWorker: False, age: 35, eyeSight: 5.000000, birthdate: 1900-01-01, registerTime: 2011-08-20 11:25:30, lastJobDuration: 3 years 2 days 13:02:00, workedHours: [10,5], usedNames: [Aida], courseScoresPerTerm: [[10,8],[6,7,8]], grades: [96,54,86,92], height: 1.731000, u: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11, name: , orgCode: , mark: , score: , history: , licenseValidInterval: , rating: , state: , info: } +{ID: 1, fName: , gender: , isStudent: , isWorker: , age: , eyeSight: , birthdate: , registerTime: , lastJobDuration: , workedHours: , usedNames: , courseScoresPerTerm: , grades: , height: , u: , name: ABFsUni, orgCode: 325, mark: 3.700000, score: -2, history: 10 years 5 months 13 hours 24 us, licenseValidInterval: 3 years 5 days, rating: 1.000000, state: {revenue: 138, location: ['toronto','montr,eal'], stock: {price: [96,56], volume: 1000}}, info: 3.120000} +{ID: 10, fName: Hubert Blaine Wolfeschlegelsteinhausenbergerdorff, gender: 2, isStudent: False, isWorker: True, age: 83, eyeSight: 4.900000, birthdate: 1990-11-27, registerTime: 2023-02-21 13:25:30, lastJobDuration: 3 years 2 days 13:02:00, workedHours: [10,11,12,3,4,5,6,7], usedNames: [Ad,De,Hi,Kye,Orlan], courseScoresPerTerm: [[7],[10],[6,7]], grades: [77,64,100,54], height: 1.323000, u: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a18, name: , orgCode: , mark: , score: , history: , licenseValidInterval: , rating: , state: , info: } +{ID: 2, fName: Bob, gender: 2, isStudent: True, isWorker: False, age: 30, eyeSight: 5.100000, birthdate: 1900-01-01, registerTime: 2008-11-03 15:25:30.000526, lastJobDuration: 10 years 5 months 13:00:00.000024, workedHours: [12,8], usedNames: [Bobby], courseScoresPerTerm: [[8,9],[9,10]], grades: [98,42,93,88], height: 0.990000, u: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12, name: , orgCode: , mark: , score: , history: , licenseValidInterval: , rating: , state: , info: } +{ID: 3, fName: Carol, gender: 1, isStudent: False, isWorker: True, age: 45, eyeSight: 5.000000, birthdate: 1940-06-22, registerTime: 1911-08-20 02:32:21, lastJobDuration: 48:24:11, workedHours: [4,5], usedNames: [Carmen,Fred], courseScoresPerTerm: [[8,10]], grades: [91,75,21,95], height: 1.000000, u: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a13, name: , orgCode: , mark: , score: , history: , licenseValidInterval: , rating: , state: , info: } +{ID: 4, fName: , gender: , isStudent: , isWorker: , age: , eyeSight: , birthdate: , registerTime: , lastJobDuration: , workedHours: , usedNames: , courseScoresPerTerm: , grades: , height: , u: , name: CsWork, orgCode: 934, mark: 4.100000, score: -100, history: 2 years 4 days 10 hours, licenseValidInterval: 26 years 52 days 48:00:00, rating: 0.780000, state: {revenue: 152, location: ["vanco,uver north area"], stock: {price: [15,78,671], volume: 432}}, info: abcd} +{ID: 5, fName: Dan, gender: 2, isStudent: False, isWorker: True, age: 20, eyeSight: 4.800000, birthdate: 1950-07-23, registerTime: 2031-11-30 12:25:30, lastJobDuration: 10 years 5 months 13:00:00.000024, workedHours: [1,9], usedNames: [Wolfeschlegelstein,Daniel], courseScoresPerTerm: [[7,4],[8,8],[9]], grades: [76,88,99,89], height: 1.300000, u: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14, name: , orgCode: , mark: , score: , history: , licenseValidInterval: , rating: , state: , info: } +{ID: 6, fName: , gender: , isStudent: , isWorker: , age: , eyeSight: , birthdate: , registerTime: , lastJobDuration: , workedHours: , usedNames: , courseScoresPerTerm: , grades: , height: , u: , name: DEsWork, orgCode: 824, mark: 4.100000, score: 7, history: 2 years 4 hours 22 us 34 minutes, licenseValidInterval: 82:00:00.1, rating: 0.520000, state: {revenue: 558, location: ['very long city name','new york'], stock: {price: [22], volume: 99}}, info: 2023-12-15} +{ID: 7, fName: Elizabeth, gender: 1, isStudent: False, isWorker: True, age: 20, eyeSight: 4.700000, birthdate: 1980-10-26, registerTime: 1976-12-23 11:21:42, lastJobDuration: 48:24:11, workedHours: [2], usedNames: [Ein], courseScoresPerTerm: [[6],[7],[8]], grades: [96,59,65,88], height: 1.463000, u: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a15, name: , orgCode: , mark: , score: , history: , licenseValidInterval: , rating: , state: , info: } +{ID: 8, fName: Farooq, gender: 2, isStudent: True, isWorker: False, age: 25, eyeSight: 4.500000, birthdate: 1980-10-26, registerTime: 1972-07-31 13:22:30.678559, lastJobDuration: 00:18:00.024, workedHours: [3,4,5,6,7], usedNames: [Fesdwe], courseScoresPerTerm: [[8]], grades: [80,78,34,83], height: 1.510000, u: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a16, name: , orgCode: , mark: , score: , history: , licenseValidInterval: , rating: , state: , info: } +{ID: 9, fName: Greg, gender: 2, isStudent: False, isWorker: False, age: 40, eyeSight: 4.900000, birthdate: 1980-10-26, registerTime: 1976-12-23 04:41:42, lastJobDuration: 10 years 5 months 13:00:00.000024, workedHours: [1], usedNames: [Grad], courseScoresPerTerm: [[10]], grades: [43,83,67,43], height: 1.600000, u: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a17, name: , orgCode: , mark: , score: , history: , licenseValidInterval: , rating: , state: , info: } + -LOG KeysSingleRelLabel -STATEMENT MATCH (p:person)-[e:knows]->(:person) RETURN keys(e) ---- 14 @@ -67,6 +89,24 @@ C [date,meetTime,validInterval,comments,summary,notes,someMap] [date,meetTime,validInterval,comments,summary,notes,someMap] +-LOG PropertiesSingleRelLabel +-STATEMENT MATCH (p:person)-[e:knows]->(:person) RETURN struct_properties(e) +---- 14 +{date: 2021-06-30, meetTime: 1986-10-21 21:08:31.521, validInterval: 10 years 5 months 13:00:00.000024, comments: [rnme,m8sihsdnf2990nfiwf], summary: {locations: ['toronto','waterloo'], transfer: {day: 2021-01-02, amount: [100,200]}}, notes: 1, someMap: {a=b}} +{date: 2021-06-30, meetTime: 1946-08-25 19:07:22, validInterval: 20 years 30 days 48:00:00, comments: [njnojppo9u0jkmf,fjiojioh9h9h89hph], summary: {locations: , transfer: }, notes: 2020-10-10, someMap: {c=d, e=f, 1=2}} +{date: 2021-06-30, meetTime: 2012-12-11 20:07:22, validInterval: 10 days, comments: [ioji232,jifhe8w99u43434], summary: {locations: ['shanghai'], transfer: {day: 1990-09-10, amount: [10]}}, notes: nice weather, someMap: } +{date: 2021-06-30, meetTime: 1946-08-25 19:07:22, validInterval: 10 years 5 months 13:00:00.000024, comments: [2huh9y89fsfw23,23nsihufhw723], summary: {locations: ['paris'], transfer: {day: 2000-01-01, amount: [20,5000]}}, notes: 4, someMap: } +{date: 1950-05-14, meetTime: 1946-08-25 19:07:22, validInterval: 00:23:00, comments: [fwehu9h9832wewew,23u9h989sdfsss], summary: {locations: ['paris'], transfer: {day: 2011-05-01, amount: [2000,5340]}}, notes: cool stuff found, someMap: } +{date: 1950-05-14, meetTime: 2012-12-11 20:07:22, validInterval: 20 years 30 days 48:00:00, comments: [fwh9y81232uisuiehuf,ewnuihxy8dyf232], summary: {locations: ['vancouver'], transfer: {day: 2020-01-01, amount: [120,50]}}, notes: matthew perry, someMap: } +{date: 2021-06-30, meetTime: 2002-07-31 11:42:53.12342, validInterval: 40 days 30:00:00, comments: [fnioh8323aeweae34d,osd89e2ejshuih12], summary: {locations: ['london','toronto'], transfer: {day: 2012-11-21, amount: [223,5230]}}, notes: 10, someMap: } +{date: 1950-05-14, meetTime: 2007-02-12 12:11:42.123, validInterval: 00:28:00.03, comments: [fwh983-sdjisdfji,ioh89y32r2huir], summary: {locations: ['paris','beijing'], transfer: {day: 2011-03-11, amount: [2323,50]}}, notes: 1, someMap: } +{date: 2000-01-01, meetTime: 1998-10-02 13:09:22.423, validInterval: 00:00:00.3, comments: [psh989823oaaioe,nuiuah1nosndfisf], summary: {locations: [], transfer: {day: 1980-11-21, amount: [20,5]}}, notes: 2, someMap: } +{date: 2021-06-30, meetTime: 1936-11-02 11:02:01, validInterval: 00:00:00.00048, comments: [fwewe], summary: {locations: ['shanghai','nanjing'], transfer: {day: 1998-11-12, amount: [22,53240]}}, notes: 15, someMap: } +{date: 1950-05-14, meetTime: 1982-11-11 13:12:05.123, validInterval: 00:23:00, comments: [fewh9182912e3,h9y8y89soidfsf,nuhudf78w78efw,hioshe0f9023sdsd], summary: {locations: ['paris'], transfer: {day: 2000-01-01, amount: [20,5000]}}, notes: happy new year, someMap: } +{date: 2000-01-01, meetTime: 1999-04-21 15:12:11.42, validInterval: 48:00:00.052, comments: [23h9sdslnfowhu2932,shuhf98922323sf], summary: {locations: ['paris'], transfer: {day: 2000-01-01, amount: [20,5000]}}, notes: 4, someMap: } +{date: 1905-12-12, meetTime: 2025-01-01 11:22:33.52, validInterval: 00:47:58, comments: [ahu2333333333333,12weeeeeeeeeeeeeeeeee], summary: {locations: ['toronto','thisisalongcityname'], transfer: {day: 1930-11-22, amount: [18,323]}}, notes: 8, someMap: } +{date: 1905-12-12, meetTime: 2020-03-01 12:11:41.6552, validInterval: 00:47:58, comments: [peweeeeeeeeeeeeeeeee,kowje9w0eweeeeeeeee], summary: {locations: ['waterloo'], transfer: {day: 2000-01-01, amount: [1000,5000]}}, notes: 10, someMap: } + -LOG KeysMultiRelLabel -STATEMENT MATCH (p:person)-[e:knows|:meets]->(:person) RETURN keys(e) ---- 21 @@ -92,6 +132,31 @@ C [date,meetTime,validInterval,comments,summary,notes,someMap,location,times,data] [date,meetTime,validInterval,comments,summary,notes,someMap,location,times,data] +-LOG PropertiesMultiRelLabel +-STATEMENT MATCH (p:person)-[e:knows|:meets]->(:person) RETURN struct_properties(e) +---- 21 +{date: 2021-06-30, meetTime: 1986-10-21 21:08:31.521, validInterval: 10 years 5 months 13:00:00.000024, comments: [rnme,m8sihsdnf2990nfiwf], summary: {locations: ['toronto','waterloo'], transfer: {day: 2021-01-02, amount: [100,200]}}, notes: 1, someMap: {a=b}, location: , times: , data: } +{date: 2021-06-30, meetTime: 1946-08-25 19:07:22, validInterval: 20 years 30 days 48:00:00, comments: [njnojppo9u0jkmf,fjiojioh9h9h89hph], summary: {locations: , transfer: }, notes: 2020-10-10, someMap: {c=d, e=f, 1=2}, location: , times: , data: } +{date: 2021-06-30, meetTime: 2012-12-11 20:07:22, validInterval: 10 days, comments: [ioji232,jifhe8w99u43434], summary: {locations: ['shanghai'], transfer: {day: 1990-09-10, amount: [10]}}, notes: nice weather, someMap: , location: , times: , data: } +{date: 2021-06-30, meetTime: 1946-08-25 19:07:22, validInterval: 10 years 5 months 13:00:00.000024, comments: [2huh9y89fsfw23,23nsihufhw723], summary: {locations: ['paris'], transfer: {day: 2000-01-01, amount: [20,5000]}}, notes: 4, someMap: , location: , times: , data: } +{date: 1950-05-14, meetTime: 1946-08-25 19:07:22, validInterval: 00:23:00, comments: [fwehu9h9832wewew,23u9h989sdfsss], summary: {locations: ['paris'], transfer: {day: 2011-05-01, amount: [2000,5340]}}, notes: cool stuff found, someMap: , location: , times: , data: } +{date: 1950-05-14, meetTime: 2012-12-11 20:07:22, validInterval: 20 years 30 days 48:00:00, comments: [fwh9y81232uisuiehuf,ewnuihxy8dyf232], summary: {locations: ['vancouver'], transfer: {day: 2020-01-01, amount: [120,50]}}, notes: matthew perry, someMap: , location: , times: , data: } +{date: 2021-06-30, meetTime: 2002-07-31 11:42:53.12342, validInterval: 40 days 30:00:00, comments: [fnioh8323aeweae34d,osd89e2ejshuih12], summary: {locations: ['london','toronto'], transfer: {day: 2012-11-21, amount: [223,5230]}}, notes: 10, someMap: , location: , times: , data: } +{date: 1950-05-14, meetTime: 2007-02-12 12:11:42.123, validInterval: 00:28:00.03, comments: [fwh983-sdjisdfji,ioh89y32r2huir], summary: {locations: ['paris','beijing'], transfer: {day: 2011-03-11, amount: [2323,50]}}, notes: 1, someMap: , location: , times: , data: } +{date: 2000-01-01, meetTime: 1998-10-02 13:09:22.423, validInterval: 00:00:00.3, comments: [psh989823oaaioe,nuiuah1nosndfisf], summary: {locations: [], transfer: {day: 1980-11-21, amount: [20,5]}}, notes: 2, someMap: , location: , times: , data: } +{date: 2021-06-30, meetTime: 1936-11-02 11:02:01, validInterval: 00:00:00.00048, comments: [fwewe], summary: {locations: ['shanghai','nanjing'], transfer: {day: 1998-11-12, amount: [22,53240]}}, notes: 15, someMap: , location: , times: , data: } +{date: 1950-05-14, meetTime: 1982-11-11 13:12:05.123, validInterval: 00:23:00, comments: [fewh9182912e3,h9y8y89soidfsf,nuhudf78w78efw,hioshe0f9023sdsd], summary: {locations: ['paris'], transfer: {day: 2000-01-01, amount: [20,5000]}}, notes: happy new year, someMap: , location: , times: , data: } +{date: 2000-01-01, meetTime: 1999-04-21 15:12:11.42, validInterval: 48:00:00.052, comments: [23h9sdslnfowhu2932,shuhf98922323sf], summary: {locations: ['paris'], transfer: {day: 2000-01-01, amount: [20,5000]}}, notes: 4, someMap: , location: , times: , data: } +{date: 1905-12-12, meetTime: 2025-01-01 11:22:33.52, validInterval: 00:47:58, comments: [ahu2333333333333,12weeeeeeeeeeeeeeeeee], summary: {locations: ['toronto','thisisalongcityname'], transfer: {day: 1930-11-22, amount: [18,323]}}, notes: 8, someMap: , location: , times: , data: } +{date: 1905-12-12, meetTime: 2020-03-01 12:11:41.6552, validInterval: 00:47:58, comments: [peweeeeeeeeeeeeeeeee,kowje9w0eweeeeeeeee], summary: {locations: ['waterloo'], transfer: {day: 2000-01-01, amount: [1000,5000]}}, notes: 10, someMap: , location: , times: , data: } +{date: , meetTime: , validInterval: , comments: , summary: , notes: , someMap: , location: [7.820000,3.540000], times: 5, data: \xAA\xBB\xCC\xDD} +{date: , meetTime: , validInterval: , comments: , summary: , notes: , someMap: , location: [2.870000,4.230000], times: 2, data: NO hex code} +{date: , meetTime: , validInterval: , comments: , summary: , notes: , someMap: , location: [3.650000,8.440000], times: 3, data: MIXED \xAC with ASCII \x0A} +{date: , meetTime: , validInterval: , comments: , summary: , notes: , someMap: , location: [2.110000,3.100000], times: 7, data: \xA1*} +{date: , meetTime: , validInterval: , comments: , summary: , notes: , someMap: , location: [2.200000,9.000000], times: 9, data: :\xA3} +{date: , meetTime: , validInterval: , comments: , summary: , notes: , someMap: , location: [3.000000,5.200000], times: 11, data: NO hex code} +{date: , meetTime: , validInterval: , comments: , summary: , notes: , someMap: , location: [3.500000,1.100000], times: 13, data: :\xA3} + -LOG StructPackWithOptionalParam -STATEMENT RETURN STRUCT_PACK(x:=2,y:=3); ---- 1 From 9875123010e29be88dc414606b913877310c5ca2 Mon Sep 17 00:00:00 2001 From: Bernie Chen Date: Fri, 12 Sep 2025 14:24:28 -0400 Subject: [PATCH 6/9] Add test for STRUCT_PROPERTIES(STRUCT) --- test/test_files/function/struct.test | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/test_files/function/struct.test b/test/test_files/function/struct.test index f34463e781a..67fc7d178d5 100644 --- a/test/test_files/function/struct.test +++ b/test/test_files/function/struct.test @@ -157,6 +157,11 @@ C {date: , meetTime: , validInterval: , comments: , summary: , notes: , someMap: , location: [3.000000,5.200000], times: 11, data: NO hex code} {date: , meetTime: , validInterval: , comments: , summary: , notes: , someMap: , location: [3.500000,1.100000], times: 13, data: :\xA3} +-LOG PropertiesStruct +-STATEMENT RETURN STRUCT_PROPERTIES({name: "alice", age: 9, nullvar: null}) +---- 1 +{name: alice, age: 9, nullvar: } + -LOG StructPackWithOptionalParam -STATEMENT RETURN STRUCT_PACK(x:=2,y:=3); ---- 1 From d90a1d940a7e323193fd6e7283bf1c0c010e83bd Mon Sep 17 00:00:00 2001 From: Bernie Chen Date: Mon, 15 Sep 2025 10:10:57 -0400 Subject: [PATCH 7/9] consolidate both properties funcs into utility file --- src/function/function_collection.cpp | 6 +- src/function/path/CMakeLists.txt | 1 - src/function/path/properties_function.cpp | 71 ---------- src/function/struct/CMakeLists.txt | 3 +- src/function/utility/CMakeLists.txt | 3 +- src/function/utility/properties.cpp | 127 ++++++++++++++++++ .../function/path/vector_path_functions.h | 17 --- .../function/struct/vector_struct_functions.h | 22 --- .../utility/vector_utility_functions.h | 28 ++++ test/test_files/function/struct.test | 10 +- 10 files changed, 166 insertions(+), 122 deletions(-) delete mode 100644 src/function/path/properties_function.cpp create mode 100644 src/function/utility/properties.cpp diff --git a/src/function/function_collection.cpp b/src/function/function_collection.cpp index 77012823513..3dc76d28f9a 100644 --- a/src/function/function_collection.cpp +++ b/src/function/function_collection.cpp @@ -177,7 +177,7 @@ FunctionCollection* FunctionCollection::getFunctions() { // Struct functions SCALAR_FUNCTION(StructPackFunctions), SCALAR_FUNCTION(StructExtractFunctions), - REWRITE_FUNCTION(KeysFunctions), SCALAR_FUNCTION(StructPropertiesFunctions), + REWRITE_FUNCTION(KeysFunctions), // Map functions SCALAR_FUNCTION(MapCreationFunctions), SCALAR_FUNCTION(MapExtractFunctions), @@ -196,7 +196,7 @@ FunctionCollection* FunctionCollection::getFunctions() { // Path functions SCALAR_FUNCTION(NodesFunction), SCALAR_FUNCTION(RelsFunction), - SCALAR_FUNCTION_ALIAS(RelationshipsFunction), SCALAR_FUNCTION(PropertiesFunction), + SCALAR_FUNCTION_ALIAS(RelationshipsFunction), SCALAR_FUNCTION(IsTrailFunction), SCALAR_FUNCTION(IsACyclicFunction), REWRITE_FUNCTION(LengthFunction), @@ -208,7 +208,7 @@ FunctionCollection* FunctionCollection::getFunctions() { SCALAR_FUNCTION(CoalesceFunction), SCALAR_FUNCTION(IfNullFunction), SCALAR_FUNCTION(ConstantOrNullFunction), SCALAR_FUNCTION(CountIfFunction), SCALAR_FUNCTION(ErrorFunction), REWRITE_FUNCTION(NullIfFunction), - SCALAR_FUNCTION(TypeOfFunction), + SCALAR_FUNCTION(TypeOfFunction), SCALAR_FUNCTION(PropertiesFunctions), // Sequence functions SCALAR_FUNCTION(CurrValFunction), SCALAR_FUNCTION(NextValFunction), diff --git a/src/function/path/CMakeLists.txt b/src/function/path/CMakeLists.txt index b179e2e8c3c..eb7fd44716a 100644 --- a/src/function/path/CMakeLists.txt +++ b/src/function/path/CMakeLists.txt @@ -2,7 +2,6 @@ add_library(kuzu_function_path OBJECT length_function.cpp nodes_function.cpp - properties_function.cpp rels_function.cpp semantic_function.cpp) diff --git a/src/function/path/properties_function.cpp b/src/function/path/properties_function.cpp deleted file mode 100644 index 409f021ac22..00000000000 --- a/src/function/path/properties_function.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "binder/expression/literal_expression.h" -#include "common/exception/binder.h" -#include "common/string_format.h" -#include "common/vector/value_vector.h" -#include "function/path/vector_path_functions.h" -#include "function/scalar_function.h" - -using namespace kuzu::common; -using namespace kuzu::binder; - -namespace kuzu { -namespace function { - -static std::unique_ptr bindFunc(const ScalarBindFuncInput& input) { - if (input.arguments[1]->expressionType != ExpressionType::LITERAL) { - throw BinderException(stringFormat( - "Expected literal input as the second argument for {}().", PropertiesFunction::name)); - } - auto literalExpr = input.arguments[1]->constPtrCast(); - auto key = literalExpr->getValue().getValue(); - const auto& listType = input.arguments[0]->getDataType(); - const auto& childType = ListType::getChildType(listType); - struct_field_idx_t fieldIdx = 0; - if (childType.getLogicalTypeID() == LogicalTypeID::NODE || - childType.getLogicalTypeID() == LogicalTypeID::REL) { - fieldIdx = StructType::getFieldIdx(childType, key); - if (fieldIdx == INVALID_STRUCT_FIELD_IDX) { - throw BinderException(stringFormat("Invalid property name: {}.", key)); - } - } else { - throw BinderException( - stringFormat("Cannot extract properties from {}.", listType.toString())); - } - const auto& field = StructType::getField(childType, fieldIdx); - auto returnType = LogicalType::LIST(field.getType().copy()); - auto bindData = std::make_unique(std::move(returnType), fieldIdx); - bindData->paramTypes.push_back(input.arguments[0]->getDataType().copy()); - bindData->paramTypes.push_back(LogicalType(input.definition->parameterTypeIDs[1])); - return bindData; -} - -static void compileFunc(FunctionBindData* bindData, - const std::vector>& parameters, - std::shared_ptr& result) { - KU_ASSERT(parameters[0]->dataType.getPhysicalType() == PhysicalTypeID::LIST); - auto& propertiesBindData = bindData->cast(); - auto fieldVector = StructVector::getFieldVector(ListVector::getDataVector(parameters[0].get()), - propertiesBindData.childIdx); - ListVector::setDataVector(result.get(), fieldVector); -} - -static void execFunc(const std::vector>& parameters, - const std::vector& parameterSelVectors, common::ValueVector& result, - common::SelectionVector* resultSelVector, void* /*dataPtr*/) { - ListVector::copyListEntryAndBufferMetaData(result, *resultSelVector, *parameters[0], - *parameterSelVectors[0]); -} - -function_set PropertiesFunction::getFunctionSet() { - function_set functions; - auto function = std::make_unique(name, - std::vector{LogicalTypeID::LIST, LogicalTypeID::STRING}, LogicalTypeID::ANY, - execFunc); - function->bindFunc = bindFunc; - function->compileFunc = compileFunc; - functions.push_back(std::move(function)); - return functions; -} - -} // namespace function -} // namespace kuzu diff --git a/src/function/struct/CMakeLists.txt b/src/function/struct/CMakeLists.txt index d898b7a5259..79c1971dcfb 100644 --- a/src/function/struct/CMakeLists.txt +++ b/src/function/struct/CMakeLists.txt @@ -2,8 +2,7 @@ add_library(kuzu_function_struct OBJECT struct_extract_function.cpp struct_pack_function.cpp - keys_function.cpp - properties_function.cpp) + keys_function.cpp) set(ALL_OBJECT_FILES ${ALL_OBJECT_FILES} $ diff --git a/src/function/utility/CMakeLists.txt b/src/function/utility/CMakeLists.txt index cae7823c7cd..2456ca087fb 100644 --- a/src/function/utility/CMakeLists.txt +++ b/src/function/utility/CMakeLists.txt @@ -7,7 +7,8 @@ add_library(kuzu_utility_function count_if.cpp error.cpp nullif.cpp - typeof.cpp) + typeof.cpp + properties.cpp) set(ALL_OBJECT_FILES ${ALL_OBJECT_FILES} $ diff --git a/src/function/utility/properties.cpp b/src/function/utility/properties.cpp new file mode 100644 index 00000000000..8e0819adcd1 --- /dev/null +++ b/src/function/utility/properties.cpp @@ -0,0 +1,127 @@ +#include "binder/expression/expression_util.h" +#include "binder/expression/literal_expression.h" +#include "binder/expression/scalar_function_expression.h" +#include "binder/expression_binder.h" +#include "common/exception/binder.h" +#include "common/string_format.h" +#include "function/scalar_function.h" +#include "function/utility/vector_utility_functions.h" +#include "common/vector/value_vector.h" + +using namespace kuzu::common; +using namespace kuzu::binder; +using namespace kuzu::catalog; + +namespace kuzu { +namespace function { + +static std::unique_ptr bindStructFunc( + const ScalarBindFuncInput& input) { + std::vector fields; + const auto& structType = input.arguments[0]->getDataType(); + auto keys = StructType::getFieldNames(structType); + std::vector fieldIdxs; + for (auto& key : keys) { + if (key == InternalKeyword::ID || key == InternalKeyword::LABEL || + key == InternalKeyword::SRC || key == InternalKeyword::DST) { + continue; + } + auto fieldIdx = StructType::getFieldIdx(structType, key); + auto fieldType = StructType::getField(structType, fieldIdx).getType().copy(); + if (fieldIdx == INVALID_STRUCT_FIELD_IDX) { + throw BinderException(stringFormat("Invalid struct field name: {}.", key)); + } + fieldIdxs.push_back(fieldIdx); + fields.emplace_back(key, std::move(fieldType)); + } + const auto resultType = LogicalType::STRUCT(std::move(fields)); + auto bindData = std::make_unique(resultType.copy(), fieldIdxs); + bindData->paramTypes.push_back(input.arguments[0]->getDataType().copy()); + return bindData; +} + +static void compileStructFunc(FunctionBindData* bindData, + const std::vector>& parameters, + std::shared_ptr& result) { + auto& propertiesBindData = bindData->cast(); + std::vector> fieldVectors; + for (auto fieldIdx : propertiesBindData.childIdxs) { + auto fieldVector = StructVector::getFieldVector(parameters[0].get(), fieldIdx); + fieldVectors.push_back(fieldVector); + } + for (auto i = 0u; i < fieldVectors.size(); ++i) { + StructVector::referenceVector(result.get(), i, fieldVectors[i]); + } +} + +static std::unique_ptr bindPathFunc(const ScalarBindFuncInput& input) { + if (input.arguments[1]->expressionType != ExpressionType::LITERAL) { + throw BinderException(stringFormat( + "Expected literal input as the second argument for {}().", PropertiesFunctions::name)); + } + auto literalExpr = input.arguments[1]->constPtrCast(); + auto key = literalExpr->getValue().getValue(); + const auto& listType = input.arguments[0]->getDataType(); + const auto& childType = ListType::getChildType(listType); + struct_field_idx_t fieldIdx = 0; + if (childType.getLogicalTypeID() == LogicalTypeID::NODE || + childType.getLogicalTypeID() == LogicalTypeID::REL) { + fieldIdx = StructType::getFieldIdx(childType, key); + if (fieldIdx == INVALID_STRUCT_FIELD_IDX) { + throw BinderException(stringFormat("Invalid property name: {}.", key)); + } + } else { + throw BinderException( + stringFormat("Cannot extract properties from {}.", listType.toString())); + } + const auto& field = StructType::getField(childType, fieldIdx); + auto returnType = LogicalType::LIST(field.getType().copy()); + auto bindData = std::make_unique(std::move(returnType), fieldIdx); + bindData->paramTypes.push_back(input.arguments[0]->getDataType().copy()); + bindData->paramTypes.push_back(LogicalType(input.definition->parameterTypeIDs[1])); + return bindData; +} + +static void compilePathFunc(FunctionBindData* bindData, + const std::vector>& parameters, + std::shared_ptr& result) { + KU_ASSERT(parameters[0]->dataType.getPhysicalType() == PhysicalTypeID::LIST); + auto& propertiesBindData = bindData->cast(); + auto fieldVector = StructVector::getFieldVector(ListVector::getDataVector(parameters[0].get()), + propertiesBindData.childIdx); + ListVector::setDataVector(result.get(), fieldVector); +} + +static void execPathFunc(const std::vector>& parameters, + const std::vector& parameterSelVectors, common::ValueVector& result, + common::SelectionVector* resultSelVector, void* /*dataPtr*/) { + ListVector::copyListEntryAndBufferMetaData(result, *resultSelVector, *parameters[0], + *parameterSelVectors[0]); +} + +static std::unique_ptr getStructPropertiesFunction(LogicalTypeID logicalTypeID) { + auto function = std::make_unique(PropertiesFunctions::name, + std::vector{logicalTypeID}, LogicalTypeID::STRUCT); + function->bindFunc = bindStructFunc; + function->compileFunc = compileStructFunc; + return function; +} + +function_set PropertiesFunctions::getFunctionSet() { + function_set functions; + auto inputTypeIDs = // PROPERTIES(STRUCT) + std::vector{LogicalTypeID::STRUCT, LogicalTypeID::NODE, LogicalTypeID::REL}; + for (auto inputTypeID : inputTypeIDs) { + functions.push_back(getStructPropertiesFunction(inputTypeID)); + } + auto function = std::make_unique(name, // PROPERTIES(PATH) + std::vector{LogicalTypeID::LIST, LogicalTypeID::STRING}, LogicalTypeID::ANY, + execPathFunc); + function->bindFunc = bindPathFunc; + function->compileFunc = compilePathFunc; + functions.push_back(std::move(function)); + return functions; +} + +} // namespace function +} // namespace kuzu diff --git a/src/include/function/path/vector_path_functions.h b/src/include/function/path/vector_path_functions.h index a566e36e8bb..f6dfb900c88 100644 --- a/src/include/function/path/vector_path_functions.h +++ b/src/include/function/path/vector_path_functions.h @@ -23,23 +23,6 @@ struct RelationshipsFunction { static constexpr const char* name = "RELATIONSHIPS"; }; -struct PropertiesBindData : public FunctionBindData { - common::idx_t childIdx; - - PropertiesBindData(common::LogicalType dataType, common::idx_t childIdx) - : FunctionBindData{std::move(dataType)}, childIdx{childIdx} {} - - inline std::unique_ptr copy() const override { - return std::make_unique(resultType.copy(), childIdx); - } -}; - -struct PropertiesFunction { - static constexpr const char* name = "PROPERTIES"; - - static function_set getFunctionSet(); -}; - struct IsTrailFunction { static constexpr const char* name = "IS_TRAIL"; diff --git a/src/include/function/struct/vector_struct_functions.h b/src/include/function/struct/vector_struct_functions.h index 7823c9110ea..9904558ce53 100644 --- a/src/include/function/struct/vector_struct_functions.h +++ b/src/include/function/struct/vector_struct_functions.h @@ -55,27 +55,5 @@ struct KeysFunctions { static function_set getFunctionSet(); }; -struct StructPropertiesBindData : public FunctionBindData { - std::vector childIdxs; - - StructPropertiesBindData(common::LogicalType dataType, std::vector childIdxs) - : FunctionBindData{std::move(dataType)}, childIdxs{std::move(childIdxs)} {} - - std::unique_ptr copy() const override { - return std::make_unique(resultType.copy(), childIdxs); - } -}; - -struct StructPropertiesFunctions { - static constexpr const char* name = "STRUCT_PROPERTIES"; - - static function_set getFunctionSet(); - - static std::unique_ptr bindFunc(const ScalarBindFuncInput& input); - static void compileFunc(FunctionBindData* bindData, - const std::vector>& parameters, - std::shared_ptr& result); -}; - } // namespace function } // namespace kuzu diff --git a/src/include/function/utility/vector_utility_functions.h b/src/include/function/utility/vector_utility_functions.h index c70d0c1e111..9704e7bb96a 100644 --- a/src/include/function/utility/vector_utility_functions.h +++ b/src/include/function/utility/vector_utility_functions.h @@ -47,5 +47,33 @@ struct TypeOfFunction { static function_set getFunctionSet(); }; +struct PathPropertiesBindData : public FunctionBindData { + common::idx_t childIdx; + + PathPropertiesBindData(common::LogicalType dataType, common::idx_t childIdx) + : FunctionBindData{std::move(dataType)}, childIdx{childIdx} {} + + inline std::unique_ptr copy() const override { + return std::make_unique(resultType.copy(), childIdx); + } +}; + +struct StructPropertiesBindData : public FunctionBindData { + std::vector childIdxs; + + StructPropertiesBindData(common::LogicalType dataType, std::vector childIdxs) + : FunctionBindData{std::move(dataType)}, childIdxs{std::move(childIdxs)} {} + + std::unique_ptr copy() const override { + return std::make_unique(resultType.copy(), childIdxs); + } +}; + +struct PropertiesFunctions { + static constexpr const char* name = "PROPERTIES"; + + static function_set getFunctionSet(); +}; + } // namespace function } // namespace kuzu diff --git a/test/test_files/function/struct.test b/test/test_files/function/struct.test index 67fc7d178d5..8c7e9c8a89d 100644 --- a/test/test_files/function/struct.test +++ b/test/test_files/function/struct.test @@ -35,7 +35,7 @@ C [name,length,note,description,content,audience,grade] -LOG PropertiesSingleNodeLabel --STATEMENT MATCH (p:movies) RETURN struct_properties(p) +-STATEMENT MATCH (p:movies) RETURN PROPERTIES(p) ---- 3 {name: Roma, length: 298, note: the movie is very interesting and funny, description: {rating: 1223.000000, stars: 100, views: 10003, release: 2011-02-11 16:44:22, release_ns: 2011-02-11 16:44:22.123456, release_ms: 2011-02-11 16:44:22.123, release_sec: 2011-02-11 16:44:22, release_tz: 2011-02-11 16:44:22.123456+00, film: 2013-02-22, u8: 1, u16: 15, u32: 200, u64: 4, hugedata: -15}, content: pure ascii characters, audience: {}, grade: 254.000000} {name: Sóló cón tu párejâ, length: 126, note: this is a very very good movie, description: {rating: 5.300000, stars: 2, views: 152, release: 2011-08-20 11:25:30, release_ns: 2011-08-20 11:25:30.123456, release_ms: 2011-08-20 11:25:30.123, release_sec: 2011-08-20 11:25:30, release_tz: 2011-08-20 11:25:30.123456+00, film: 2012-05-11, u8: 220, u16: 20, u32: 1, u64: 180, hugedata: 1844674407370955161811111111}, content: \xAA\xABinteresting\x0B, audience: {audience1=52, audience53=42}, grade: True} @@ -57,7 +57,7 @@ C [ID,fName,gender,isStudent,isWorker,age,eyeSight,birthdate,registerTime,lastJobDuration,workedHours,usedNames,courseScoresPerTerm,grades,height,u,name,orgCode,mark,score,history,licenseValidInterval,rating,state,info] -LOG PropertiesMultiNodeLabel --STATEMENT MATCH (p:person:organisation) RETURN struct_properties(p) +-STATEMENT MATCH (p:person:organisation) RETURN PROPERTIES(p) ---- 11 {ID: 0, fName: Alice, gender: 1, isStudent: True, isWorker: False, age: 35, eyeSight: 5.000000, birthdate: 1900-01-01, registerTime: 2011-08-20 11:25:30, lastJobDuration: 3 years 2 days 13:02:00, workedHours: [10,5], usedNames: [Aida], courseScoresPerTerm: [[10,8],[6,7,8]], grades: [96,54,86,92], height: 1.731000, u: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11, name: , orgCode: , mark: , score: , history: , licenseValidInterval: , rating: , state: , info: } {ID: 1, fName: , gender: , isStudent: , isWorker: , age: , eyeSight: , birthdate: , registerTime: , lastJobDuration: , workedHours: , usedNames: , courseScoresPerTerm: , grades: , height: , u: , name: ABFsUni, orgCode: 325, mark: 3.700000, score: -2, history: 10 years 5 months 13 hours 24 us, licenseValidInterval: 3 years 5 days, rating: 1.000000, state: {revenue: 138, location: ['toronto','montr,eal'], stock: {price: [96,56], volume: 1000}}, info: 3.120000} @@ -90,7 +90,7 @@ C [date,meetTime,validInterval,comments,summary,notes,someMap] -LOG PropertiesSingleRelLabel --STATEMENT MATCH (p:person)-[e:knows]->(:person) RETURN struct_properties(e) +-STATEMENT MATCH (p:person)-[e:knows]->(:person) RETURN PROPERTIES(e) ---- 14 {date: 2021-06-30, meetTime: 1986-10-21 21:08:31.521, validInterval: 10 years 5 months 13:00:00.000024, comments: [rnme,m8sihsdnf2990nfiwf], summary: {locations: ['toronto','waterloo'], transfer: {day: 2021-01-02, amount: [100,200]}}, notes: 1, someMap: {a=b}} {date: 2021-06-30, meetTime: 1946-08-25 19:07:22, validInterval: 20 years 30 days 48:00:00, comments: [njnojppo9u0jkmf,fjiojioh9h9h89hph], summary: {locations: , transfer: }, notes: 2020-10-10, someMap: {c=d, e=f, 1=2}} @@ -133,7 +133,7 @@ C [date,meetTime,validInterval,comments,summary,notes,someMap,location,times,data] -LOG PropertiesMultiRelLabel --STATEMENT MATCH (p:person)-[e:knows|:meets]->(:person) RETURN struct_properties(e) +-STATEMENT MATCH (p:person)-[e:knows|:meets]->(:person) RETURN PROPERTIES(e) ---- 21 {date: 2021-06-30, meetTime: 1986-10-21 21:08:31.521, validInterval: 10 years 5 months 13:00:00.000024, comments: [rnme,m8sihsdnf2990nfiwf], summary: {locations: ['toronto','waterloo'], transfer: {day: 2021-01-02, amount: [100,200]}}, notes: 1, someMap: {a=b}, location: , times: , data: } {date: 2021-06-30, meetTime: 1946-08-25 19:07:22, validInterval: 20 years 30 days 48:00:00, comments: [njnojppo9u0jkmf,fjiojioh9h9h89hph], summary: {locations: , transfer: }, notes: 2020-10-10, someMap: {c=d, e=f, 1=2}, location: , times: , data: } @@ -158,7 +158,7 @@ C {date: , meetTime: , validInterval: , comments: , summary: , notes: , someMap: , location: [3.500000,1.100000], times: 13, data: :\xA3} -LOG PropertiesStruct --STATEMENT RETURN STRUCT_PROPERTIES({name: "alice", age: 9, nullvar: null}) +-STATEMENT RETURN PROPERTIES({name: "alice", age: 9, nullvar: null}) ---- 1 {name: alice, age: 9, nullvar: } From 9124afe08b5861c342c7cff3462ec945af93b230 Mon Sep 17 00:00:00 2001 From: CI Bot Date: Mon, 15 Sep 2025 14:13:06 +0000 Subject: [PATCH 8/9] ci: auto code format --- src/function/function_collection.cpp | 5 ++--- src/function/utility/properties.cpp | 7 +++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/function/function_collection.cpp b/src/function/function_collection.cpp index 3dc76d28f9a..f3192addf38 100644 --- a/src/function/function_collection.cpp +++ b/src/function/function_collection.cpp @@ -196,9 +196,8 @@ FunctionCollection* FunctionCollection::getFunctions() { // Path functions SCALAR_FUNCTION(NodesFunction), SCALAR_FUNCTION(RelsFunction), - SCALAR_FUNCTION_ALIAS(RelationshipsFunction), - SCALAR_FUNCTION(IsTrailFunction), SCALAR_FUNCTION(IsACyclicFunction), - REWRITE_FUNCTION(LengthFunction), + SCALAR_FUNCTION_ALIAS(RelationshipsFunction), SCALAR_FUNCTION(IsTrailFunction), + SCALAR_FUNCTION(IsACyclicFunction), REWRITE_FUNCTION(LengthFunction), // Hash functions SCALAR_FUNCTION(MD5Function), SCALAR_FUNCTION(SHA256Function), diff --git a/src/function/utility/properties.cpp b/src/function/utility/properties.cpp index 8e0819adcd1..3d603a5ac98 100644 --- a/src/function/utility/properties.cpp +++ b/src/function/utility/properties.cpp @@ -4,9 +4,9 @@ #include "binder/expression_binder.h" #include "common/exception/binder.h" #include "common/string_format.h" +#include "common/vector/value_vector.h" #include "function/scalar_function.h" #include "function/utility/vector_utility_functions.h" -#include "common/vector/value_vector.h" using namespace kuzu::common; using namespace kuzu::binder; @@ -15,8 +15,7 @@ using namespace kuzu::catalog; namespace kuzu { namespace function { -static std::unique_ptr bindStructFunc( - const ScalarBindFuncInput& input) { +static std::unique_ptr bindStructFunc(const ScalarBindFuncInput& input) { std::vector fields; const auto& structType = input.arguments[0]->getDataType(); auto keys = StructType::getFieldNames(structType); @@ -111,7 +110,7 @@ function_set PropertiesFunctions::getFunctionSet() { function_set functions; auto inputTypeIDs = // PROPERTIES(STRUCT) std::vector{LogicalTypeID::STRUCT, LogicalTypeID::NODE, LogicalTypeID::REL}; - for (auto inputTypeID : inputTypeIDs) { + for (auto inputTypeID : inputTypeIDs) { functions.push_back(getStructPropertiesFunction(inputTypeID)); } auto function = std::make_unique(name, // PROPERTIES(PATH) From 4a10776ca634be9f844602c0a78a83aec7ca6902 Mon Sep 17 00:00:00 2001 From: Bernie Chen Date: Mon, 15 Sep 2025 10:45:58 -0400 Subject: [PATCH 9/9] tidy --- src/function/struct/properties_function.cpp | 75 --------------------- src/function/utility/properties.cpp | 2 - 2 files changed, 77 deletions(-) delete mode 100644 src/function/struct/properties_function.cpp diff --git a/src/function/struct/properties_function.cpp b/src/function/struct/properties_function.cpp deleted file mode 100644 index cd1e5ee0436..00000000000 --- a/src/function/struct/properties_function.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "binder/expression/expression_util.h" -#include "binder/expression/literal_expression.h" -#include "binder/expression/scalar_function_expression.h" -#include "binder/expression_binder.h" -#include "common/exception/binder.h" -#include "function/rewrite_function.h" -#include "function/struct/vector_struct_functions.h" - -using namespace kuzu::common; -using namespace kuzu::binder; -using namespace kuzu::catalog; - -namespace kuzu { -namespace function { - -std::unique_ptr StructPropertiesFunctions::bindFunc( - const ScalarBindFuncInput& input) { - std::vector fields; - const auto& structType = input.arguments[0]->getDataType(); - auto keys = StructType::getFieldNames(structType); - std::vector fieldIdxs; - for (auto& key : keys) { - if (key == InternalKeyword::ID || key == InternalKeyword::LABEL || - key == InternalKeyword::SRC || key == InternalKeyword::DST) { - continue; - } - auto fieldIdx = StructType::getFieldIdx(structType, key); - auto fieldType = StructType::getField(structType, fieldIdx).getType().copy(); - if (fieldIdx == INVALID_STRUCT_FIELD_IDX) { - throw BinderException(stringFormat("Invalid struct field name: {}.", key)); - } - fieldIdxs.push_back(fieldIdx); - fields.emplace_back(key, std::move(fieldType)); - } - const auto resultType = LogicalType::STRUCT(std::move(fields)); - auto bindData = std::make_unique(resultType.copy(), fieldIdxs); - bindData->paramTypes.push_back(input.arguments[0]->getDataType().copy()); - return bindData; -} - -void StructPropertiesFunctions::compileFunc(FunctionBindData* bindData, - const std::vector>& parameters, - std::shared_ptr& result) { - KU_ASSERT(parameters[0]->dataType.getPhysicalType() == PhysicalTypeID::STRUCT); - auto& propertiesBindData = bindData->cast(); - std::vector> fieldVectors; - for (auto fieldIdx : propertiesBindData.childIdxs) { - auto fieldVector = StructVector::getFieldVector(parameters[0].get(), fieldIdx); - fieldVectors.push_back(fieldVector); - } - for (auto i = 0u; i < fieldVectors.size(); ++i) { - StructVector::referenceVector(result.get(), i, fieldVectors[i]); - } -} - -static std::unique_ptr getPropertiesFunction(LogicalTypeID logicalTypeID) { - auto function = std::make_unique(StructPropertiesFunctions::name, - std::vector{logicalTypeID}, LogicalTypeID::STRUCT); - function->bindFunc = StructPropertiesFunctions::bindFunc; - function->compileFunc = StructPropertiesFunctions::compileFunc; - return function; -} - -function_set StructPropertiesFunctions::getFunctionSet() { - function_set functions; - auto inputTypeIDs = - std::vector{LogicalTypeID::STRUCT, LogicalTypeID::NODE, LogicalTypeID::REL}; - for (auto inputTypeID : inputTypeIDs) { - functions.push_back(getPropertiesFunction(inputTypeID)); - } - return functions; -} - -} // namespace function -} // namespace kuzu diff --git a/src/function/utility/properties.cpp b/src/function/utility/properties.cpp index 3d603a5ac98..7d06c0efdf3 100644 --- a/src/function/utility/properties.cpp +++ b/src/function/utility/properties.cpp @@ -1,6 +1,4 @@ -#include "binder/expression/expression_util.h" #include "binder/expression/literal_expression.h" -#include "binder/expression/scalar_function_expression.h" #include "binder/expression_binder.h" #include "common/exception/binder.h" #include "common/string_format.h"