From 06285647833a79900a27b36052e6a0c4792e6ebc Mon Sep 17 00:00:00 2001 From: ziyi chen Date: Wed, 10 May 2023 16:19:48 -0400 Subject: [PATCH] Add null to struct fields --- dataset/tinysnb/vMovies.csv | 4 +- src/common/types/types.cpp | 2 + src/common/types/value.cpp | 12 +++-- src/common/vector/value_vector_utils.cpp | 44 ++++++++++++---- src/function/vector_struct_operations.cpp | 51 +++++++++++++++++++ .../common/{null_bytes.h => null_buffer.h} | 0 .../struct/vector_struct_operations.h | 35 +------------ .../in_mem_column_chunk.h | 11 ++-- .../in_mem_node_column.h | 2 + src/processor/result/factorized_table.cpp | 2 +- src/storage/copier/node_copier.cpp | 6 +-- src/storage/copier/rel_copy_executor.cpp | 3 +- .../in_mem_column_chunk.cpp | 24 ++++++--- .../storage_structure/disk_overflow_file.cpp | 2 +- .../tinysnb/projection/multi_label.test | 4 +- .../tinysnb/projection/single_label.test | 14 ++++- 16 files changed, 149 insertions(+), 67 deletions(-) rename src/include/common/{null_bytes.h => null_buffer.h} (100%) diff --git a/dataset/tinysnb/vMovies.csv b/dataset/tinysnb/vMovies.csv index bd1fd0cfde8..8a84bf16430 100644 --- a/dataset/tinysnb/vMovies.csv +++ b/dataset/tinysnb/vMovies.csv @@ -1,3 +1,3 @@ -Sóló cón tu párejâ,126, this is a very very good movie,"{rating: 5.3, views: 152, release: 2011-08-20 11:25:30, film: 2012-05-11}" -The 😂😃🧘🏻‍♂️🌍🌦️🍞🚗 movie,2544, the movie is very very good,"{rating: 7, views: 982, release: 2018-11-13 13:33:11, film: 2014-09-12}" +Sóló cón tu párejâ,126, this is a very very good movie,"{rating: 5.3, views: 152, release: 2011-08-20 11:25:30, film: NULL}" +The 😂😃🧘🏻‍♂️🌍🌦️🍞🚗 movie,2544, the movie is very very good,"{rating: 7, views: NULL, release: 2018-11-13 13:33:11, film: 2014-09-12}" Roma,298,the movie is very interesting and funny,"{rating: 1223, views: 10003, release: 2011-02-11 16:44:22, film: 2013-02-22}" diff --git a/src/common/types/types.cpp b/src/common/types/types.cpp index a3761cb8cc9..a1954368ada 100644 --- a/src/common/types/types.cpp +++ b/src/common/types/types.cpp @@ -4,6 +4,7 @@ #include #include "common/exception.h" +#include "common/null_buffer.h" #include "common/ser_deser.h" #include "common/types/types_include.h" @@ -491,6 +492,7 @@ uint32_t Types::getDataTypeSize(const DataType& dataType) { for (auto& childType : structTypeInfo->getChildrenTypes()) { size += getDataTypeSize(*childType); } + size += NullBuffer::getNumBytesForNullValues(structTypeInfo->getChildrenNames().size()); return size; } case INTERNAL_ID: diff --git a/src/common/types/value.cpp b/src/common/types/value.cpp index 0774d6f0556..44446b8104e 100644 --- a/src/common/types/value.cpp +++ b/src/common/types/value.cpp @@ -1,6 +1,6 @@ #include "common/types/value.h" -#include "common/null_bytes.h" +#include "common/null_buffer.h" #include "common/string_utils.h" namespace kuzu { @@ -378,11 +378,17 @@ std::vector> Value::convertKUStructToVector(const uint8_t std::vector> structVal; auto childrenTypes = structTypeInfo->getChildrenTypes(); auto numFields = childrenTypes.size(); + auto structNullValues = kuStruct; + auto structValues = structNullValues + NullBuffer::getNumBytesForNullValues(numFields); for (auto i = 0; i < numFields; i++) { auto childValue = std::make_unique(Value::createDefaultValue(*childrenTypes[i])); - childValue->copyValueFrom(kuStruct); + if (NullBuffer::isNull(structNullValues, i)) { + childValue->setNull(true); + } else { + childValue->copyValueFrom(structValues); + } structVal.emplace_back(std::move(childValue)); - kuStruct += Types::getDataTypeSize(*childrenTypes[i]); + structValues += Types::getDataTypeSize(*childrenTypes[i]); } return structVal; } diff --git a/src/common/vector/value_vector_utils.cpp b/src/common/vector/value_vector_utils.cpp index 7aed7d9e776..7aadde0ab1a 100644 --- a/src/common/vector/value_vector_utils.cpp +++ b/src/common/vector/value_vector_utils.cpp @@ -1,7 +1,7 @@ #include "common/vector/value_vector_utils.h" #include "common/in_mem_overflow_buffer_utils.h" -#include "common/null_bytes.h" +#include "common/null_buffer.h" using namespace kuzu; using namespace common; @@ -10,9 +10,18 @@ void ValueVectorUtils::copyNonNullDataWithSameTypeIntoPos( ValueVector& resultVector, uint64_t pos, const uint8_t* srcData) { switch (resultVector.dataType.typeID) { case STRUCT: { - for (auto& childVector : StructVector::getChildrenVectors(&resultVector)) { - copyNonNullDataWithSameTypeIntoPos(*childVector, pos, srcData); - srcData += Types::getDataTypeSize(childVector->dataType); + auto structFields = StructVector::getChildrenVectors(&resultVector); + auto structNullBytes = srcData; + auto structValues = + structNullBytes + NullBuffer::getNumBytesForNullValues(structFields.size()); + for (auto i = 0u; i < structFields.size(); i++) { + auto structField = structFields[i]; + if (NullBuffer::isNull(structNullBytes, i)) { + structField->setNull(pos, true /* isNull */); + } else { + copyNonNullDataWithSameTypeIntoPos(*structField, pos, structValues); + } + structValues += Types::getDataTypeSize(structField->dataType); } } break; case VAR_LIST: { @@ -45,9 +54,22 @@ void ValueVectorUtils::copyNonNullDataWithSameTypeOutFromPos(const ValueVector& uint64_t pos, uint8_t* dstData, InMemOverflowBuffer& dstOverflowBuffer) { switch (srcVector.dataType.typeID) { case STRUCT: { - for (auto& childVector : StructVector::getChildrenVectors(&srcVector)) { - copyNonNullDataWithSameTypeOutFromPos(*childVector, pos, dstData, dstOverflowBuffer); - dstData += Types::getDataTypeSize(childVector->dataType); + // The storage structure of STRUCT type in factorizedTable is: + // [NULLBYTES, FIELD1, FIELD2, ...] + auto structFields = StructVector::getChildrenVectors(&srcVector); + NullBuffer::initNullBytes(dstData, structFields.size()); + auto structNullBytes = dstData; + auto structValues = + structNullBytes + NullBuffer::getNumBytesForNullValues(structFields.size()); + for (auto i = 0u; i < structFields.size(); i++) { + auto structField = structFields[i]; + if (structField->isNull(pos)) { + NullBuffer::setNull(structNullBytes, i); + } else { + copyNonNullDataWithSameTypeOutFromPos( + *structField, pos, structValues, dstOverflowBuffer); + } + structValues += Types::getDataTypeSize(structField->dataType); } } break; case VAR_LIST: { @@ -111,8 +133,12 @@ void ValueVectorUtils::copyValue(uint8_t* dstValue, common::ValueVector& dstVect for (auto i = 0u; i < srcFields.size(); i++) { auto srcField = srcFields[i]; auto dstField = dstFields[i]; - copyValue(dstField->getData() + dstField->getNumBytesPerValue() * dstPos, *dstField, - srcField->getData() + srcField->getNumBytesPerValue() * srcPos, *srcField); + if (srcField->isNull(srcPos)) { + dstField->setNull(dstPos, true /* isNull */); + } else { + copyValue(dstField->getData() + dstField->getNumBytesPerValue() * dstPos, *dstField, + srcField->getData() + srcField->getNumBytesPerValue() * srcPos, *srcField); + } } } break; case STRING: { diff --git a/src/function/vector_struct_operations.cpp b/src/function/vector_struct_operations.cpp index 723ffc047a1..1db5e3f8fd0 100644 --- a/src/function/vector_struct_operations.cpp +++ b/src/function/vector_struct_operations.cpp @@ -1,6 +1,7 @@ #include "function/struct/vector_struct_operations.h" #include "binder/expression/literal_expression.h" +#include "binder/expression_binder.h" #include "function/function_definition.h" namespace kuzu { @@ -19,6 +20,10 @@ std::unique_ptr StructPackVectorOperations::bindFunc( const binder::expression_vector& arguments, kuzu::function::FunctionDefinition* definition) { std::vector> fields; for (auto& argument : arguments) { + if (argument->getDataType().typeID == common::ANY) { + binder::ExpressionBinder::resolveAnyDataType( + *argument, common::DataType{common::INT64}); + } fields.emplace_back(std::make_unique( argument->getAlias(), argument->getDataType().copy())); } @@ -26,6 +31,52 @@ std::unique_ptr StructPackVectorOperations::bindFunc( return std::make_unique(resultType); } +void StructPackVectorOperations::execFunc( + const std::vector>& parameters, + common::ValueVector& result) { + for (auto i = 0u; i < parameters.size(); i++) { + auto& parameter = parameters[i]; + if (parameter->state == result.state) { + continue; + } + // If the parameter's state is inconsistent with the result's state, we need to copy the + // parameter's value to the corresponding child vector. + copyParameterValueToStructFieldVector( + parameter.get(), common::StructVector::getChildVector(&result, i).get()); + } +} + +void StructPackVectorOperations::copyParameterValueToStructFieldVector( + const common::ValueVector* parameter, common::ValueVector* structField) { + // If the parameter is unFlat, then its state must be consistent with the result's state. + // Thus, we don't need to copy values to structFieldVector. + assert(parameter->state->isFlat()); + auto srcPos = parameter->state->selVector->selectedPositions[0]; + auto srcValue = parameter->getData() + parameter->getNumBytesPerValue() * srcPos; + bool isSrcValueNull = parameter->isNull(srcPos); + if (structField->state->isFlat()) { + auto pos = structField->state->selVector->selectedPositions[0]; + if (isSrcValueNull) { + structField->setNull(pos, true /* isNull */); + } else { + common::ValueVectorUtils::copyValue( + structField->getData() + structField->getNumBytesPerValue() * pos, *structField, + srcValue, *parameter); + } + } else { + for (auto j = 0u; j < structField->state->selVector->selectedSize; j++) { + auto pos = structField->state->selVector->selectedPositions[j]; + if (isSrcValueNull) { + structField->setNull(pos, true /* isNull */); + } else { + common::ValueVectorUtils::copyValue( + structField->getData() + structField->getNumBytesPerValue() * pos, *structField, + srcValue, *parameter); + } + } + } +} + std::vector> StructExtractVectorOperations::getDefinitions() { std::vector> definitions; diff --git a/src/include/common/null_bytes.h b/src/include/common/null_buffer.h similarity index 100% rename from src/include/common/null_bytes.h rename to src/include/common/null_buffer.h diff --git a/src/include/function/struct/vector_struct_operations.h b/src/include/function/struct/vector_struct_operations.h index 3636fe158a2..53a1d3aeb32 100644 --- a/src/include/function/struct/vector_struct_operations.h +++ b/src/include/function/struct/vector_struct_operations.h @@ -11,40 +11,9 @@ struct StructPackVectorOperations : public VectorOperations { static std::unique_ptr bindFunc( const binder::expression_vector& arguments, FunctionDefinition* definition); static void execFunc(const std::vector>& parameters, - common::ValueVector& result) { - for (auto i = 0u; i < parameters.size(); i++) { - auto& parameter = parameters[i]; - if (parameter->state == result.state) { - continue; - } - // If the parameter's state is inconsistent with the result's state, we need to copy the - // parameter's value to the corresponding child vector. - copyParameterValueToStructFieldVector( - parameter.get(), common::StructVector::getChildVector(&result, i).get()); - } - } + common::ValueVector& result); static void copyParameterValueToStructFieldVector( - const common::ValueVector* parameter, common::ValueVector* structField) { - // If the parameter is unFlat, then its state must be consistent with the result's state. - // Thus, we don't need to copy values to structFieldVector. - assert(parameter->state->isFlat()); - auto srcValue = - parameter->getData() + - parameter->getNumBytesPerValue() * parameter->state->selVector->selectedPositions[0]; - if (structField->state->isFlat()) { - common::ValueVectorUtils::copyValue( - structField->getData() + structField->getNumBytesPerValue() * - structField->state->selVector->selectedPositions[0], - *structField, srcValue, *parameter); - } else { - for (auto j = 0u; j < structField->state->selVector->selectedSize; j++) { - auto pos = structField->state->selVector->selectedPositions[j]; - common::ValueVectorUtils::copyValue( - structField->getData() + structField->getNumBytesPerValue() * pos, *structField, - srcValue, *parameter); - } - } - } + const common::ValueVector* parameter, common::ValueVector* structField); }; struct StructExtractBindData : public FunctionBindData { diff --git a/src/include/storage/in_mem_storage_structure/in_mem_column_chunk.h b/src/include/storage/in_mem_storage_structure/in_mem_column_chunk.h index dc0c0ae20ec..56318ba43ca 100644 --- a/src/include/storage/in_mem_storage_structure/in_mem_column_chunk.h +++ b/src/include/storage/in_mem_storage_structure/in_mem_column_chunk.h @@ -11,6 +11,8 @@ namespace kuzu { namespace storage { +class InMemNodeColumn; + class InMemColumnChunk { public: InMemColumnChunk() = default; @@ -91,7 +93,7 @@ class InMemColumnChunk { virtual void copyStructValueToFields(arrow::Array& array, uint64_t posInArray, const common::CopyDescription& copyDescription, common::offset_t nodeOffset, - uint64_t numValues) { + uint64_t numValues, InMemNodeColumn* column) { assert(false); } @@ -111,7 +113,7 @@ class InMemStructColumnChunk : public InMemColumnChunk { void copyStructValueToFields(arrow::Array& array, uint64_t posInArray, const common::CopyDescription& copyDescription, common::offset_t nodeOffset, - uint64_t numValues) override; + uint64_t numValues, InMemNodeColumn* column) override; std::vector getInMemColumnChunksForFields(); @@ -157,8 +159,9 @@ void InMemColumnChunk::templateCopyValuesToPage void InMemColumnChunk::templateCopyValuesToPage(const PageElementCursor& pageCursor, arrow::Array& array, uint64_t posInArray, - uint64_t numValues, common::CopyDescription& copyDesc, common::offset_t nodeOffset); + common::offset_t, InMemNodeColumn*>(const PageElementCursor& pageCursor, arrow::Array& array, + uint64_t posInArray, uint64_t numValues, common::CopyDescription& copyDesc, + common::offset_t nodeOffset, InMemNodeColumn* column); template<> void InMemColumnChunk::setValueFromString( diff --git a/src/include/storage/in_mem_storage_structure/in_mem_node_column.h b/src/include/storage/in_mem_storage_structure/in_mem_node_column.h index 6841ae84c5a..dbfc8b98576 100644 --- a/src/include/storage/in_mem_storage_structure/in_mem_node_column.h +++ b/src/include/storage/in_mem_storage_structure/in_mem_node_column.h @@ -83,6 +83,8 @@ class NodeInMemStructColumn : public InMemNodeColumn { void flushChunk( InMemColumnChunk* chunk, common::offset_t startOffset, common::offset_t endOffset) override; + inline InMemNodeColumn* getField(uint64_t fieldIdx) { return fields[fieldIdx].get(); } + private: std::vector> fields; }; diff --git a/src/processor/result/factorized_table.cpp b/src/processor/result/factorized_table.cpp index 2fb1646ef08..fc07d6d9888 100644 --- a/src/processor/result/factorized_table.cpp +++ b/src/processor/result/factorized_table.cpp @@ -1,7 +1,7 @@ #include "processor/result/factorized_table.h" #include "common/exception.h" -#include "common/null_bytes.h" +#include "common/null_buffer.h" #include "common/vector/value_vector_utils.h" using namespace kuzu::common; diff --git a/src/storage/copier/node_copier.cpp b/src/storage/copier/node_copier.cpp index d33adfc57fb..223b032b75a 100644 --- a/src/storage/copier/node_copier.cpp +++ b/src/storage/copier/node_copier.cpp @@ -129,9 +129,9 @@ void NodeCopier::copyArrayIntoColumnChunk(InMemColumnChunk* columnChunk, auto numValuesToCopy = std::min(numValuesLeftToCopy, reinterpret_cast(columnChunk) ->getMinNumValuesLeftOnPage(offset)); - columnChunk->templateCopyValuesToPage( - PageElementCursor{}, arrowArray, posInArray, numValuesToCopy, copyDescription, - offset); + columnChunk->templateCopyValuesToPage(PageElementCursor{}, arrowArray, posInArray, numValuesToCopy, + copyDescription, offset, column); numValuesLeftToCopy -= numValuesToCopy; continue; } diff --git a/src/storage/copier/rel_copy_executor.cpp b/src/storage/copier/rel_copy_executor.cpp index fcbbdeda1fe..bbe819005f2 100644 --- a/src/storage/copier/rel_copy_executor.cpp +++ b/src/storage/copier/rel_copy_executor.cpp @@ -775,12 +775,13 @@ void RelCopyExecutor::calculateListHeadersTask(offset_t numNodes, atomic_uint64_ auto numNodesInChunk = std::min((offset_t)ListsMetadataConstants::LISTS_CHUNK_SIZE, numNodes - nodeOffset); csr_offset_t csrOffset = (*listSizes)[chunkNodeOffset].load(std::memory_order_relaxed); - for (auto i = 1u; i <= numNodesInChunk; i++) { + for (auto i = 1u; i < numNodesInChunk; i++) { auto currNodeOffset = chunkNodeOffset + i; auto numElementsInList = (*listSizes)[currNodeOffset].load(std::memory_order_relaxed); listHeadersBuilder->setCSROffset(currNodeOffset, csrOffset); csrOffset += numElementsInList; } + listHeadersBuilder->setCSROffset(chunkNodeOffset + numNodesInChunk, csrOffset); nodeOffset += numNodesInChunk; } logger->trace("End: adjListHeadersBuilder={0:p}", (void*)listHeadersBuilder); diff --git a/src/storage/in_mem_storage_structure/in_mem_column_chunk.cpp b/src/storage/in_mem_storage_structure/in_mem_column_chunk.cpp index ea9d97d8248..61cff8fe052 100644 --- a/src/storage/in_mem_storage_structure/in_mem_column_chunk.cpp +++ b/src/storage/in_mem_storage_structure/in_mem_column_chunk.cpp @@ -3,6 +3,7 @@ #include #include "common/types/types.h" +#include "storage/in_mem_storage_structure/in_mem_node_column.h" namespace kuzu { namespace storage { @@ -78,8 +79,9 @@ void InMemColumnChunk::templateCopyValuesToPage void InMemColumnChunk::templateCopyValuesToPage(const PageElementCursor& pageCursor, arrow::Array& array, uint64_t posInArray, - uint64_t numValues, common::CopyDescription& copyDesc, common::offset_t nodeOffset) { - copyStructValueToFields(array, posInArray, copyDesc, nodeOffset, numValues); + uint64_t numValues, common::CopyDescription& copyDesc, common::offset_t nodeOffset, + InMemNodeColumn* column) { + copyStructValueToFields(array, posInArray, copyDesc, nodeOffset, numValues, column); } template<> @@ -161,8 +163,7 @@ InMemStructColumnChunk::InMemStructColumnChunk( void InMemStructColumnChunk::copyStructValueToFields(arrow::Array& array, uint64_t posInArray, const common::CopyDescription& copyDescription, common::offset_t startOffset, - uint64_t numValues) { - // TODO(Ziyi): support null values in struct. + uint64_t numValues, InMemNodeColumn* column) { for (auto i = 0u; i < numValues; i++) { auto& stringArray = (arrow::StringArray&)array; auto structView = stringArray.GetView(i + posInArray); @@ -181,8 +182,19 @@ void InMemStructColumnChunk::copyStructValueToFields(arrow::Array& array, uint64 std::regex_replace(structField.substr(0, delimPos), whiteSpacePattern, ""); auto structFieldIdx = getStructFieldIdx(structFieldNames, structFieldName); auto structFieldValue = structField.substr(delimPos + 1); - copyValueToStructColumnField(i + startOffset, structFieldIdx, structFieldValue, - *structFieldTypes[structFieldIdx]); + auto offset = i + startOffset; + auto capitalizedStructFiledValue = + std::regex_replace(structFieldValue, whiteSpacePattern, ""); + common::StringUtils::toUpper(capitalizedStructFiledValue); + if (capitalizedStructFiledValue == "NULL") { + continue; + } else { + reinterpret_cast(column) + ->getField(structFieldIdx) + ->setNull(offset, false /* isNull */); + copyValueToStructColumnField( + offset, structFieldIdx, structFieldValue, *structFieldTypes[structFieldIdx]); + } } } } diff --git a/src/storage/storage_structure/disk_overflow_file.cpp b/src/storage/storage_structure/disk_overflow_file.cpp index aab1efd2d9f..b8afa216477 100644 --- a/src/storage/storage_structure/disk_overflow_file.cpp +++ b/src/storage/storage_structure/disk_overflow_file.cpp @@ -1,7 +1,7 @@ #include "storage/storage_structure/disk_overflow_file.h" #include "common/in_mem_overflow_buffer_utils.h" -#include "common/null_bytes.h" +#include "common/null_buffer.h" #include "common/string_utils.h" #include "common/type_utils.h" diff --git a/test/test_files/tinysnb/projection/multi_label.test b/test/test_files/tinysnb/projection/multi_label.test index 190079cd029..68b319ba408 100644 --- a/test/test_files/tinysnb/projection/multi_label.test +++ b/test/test_files/tinysnb/projection/multi_label.test @@ -23,8 +23,8 @@ -NAME MultiLabelReturnStar -QUERY MATCH (a:movies:organisation) RETURN * ---- 6 -(label:movies, 2:0, {ID:, name:Sóló cón tu párejâ, orgCode:, mark:, score:, history:, licenseValidInterval:, rating:, length:126, note: this is a very very good movie, description:{RATING: 5.300000, VIEWS: 152, RELEASE: 2011-08-20 11:25:30, FILM: 2012-05-11}}) -(label:movies, 2:1, {ID:, name:The 😂😃🧘🏻‍♂️🌍🌦️🍞🚗 movie, orgCode:, mark:, score:, history:, licenseValidInterval:, rating:, length:2544, note: the movie is very very good, description:{RATING: 7.000000, VIEWS: 982, RELEASE: 2018-11-13 13:33:11, FILM: 2014-09-12}}) +(label:movies, 2:0, {ID:, name:Sóló cón tu párejâ, orgCode:, mark:, score:, history:, licenseValidInterval:, rating:, length:126, note: this is a very very good movie, description:{RATING: 5.300000, VIEWS: 152, RELEASE: 2011-08-20 11:25:30, FILM: }}) +(label:movies, 2:1, {ID:, name:The 😂😃🧘🏻‍♂️🌍🌦️🍞🚗 movie, orgCode:, mark:, score:, history:, licenseValidInterval:, rating:, length:2544, note: the movie is very very good, description:{RATING: 7.000000, VIEWS: , RELEASE: 2018-11-13 13:33:11, FILM: 2014-09-12}}) (label:movies, 2:2, {ID:, name:Roma, orgCode:, mark:, score:, history:, licenseValidInterval:, rating:, length:298, note:the movie is very interesting and funny, description:{RATING: 1223.000000, VIEWS: 10003, RELEASE: 2011-02-11 16:44:22, FILM: 2013-02-22}}) (label:organisation, 1:0, {ID:1, 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, length:, note:, description:}) (label:organisation, 1:1, {ID:4, 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, length:, note:, description:}) diff --git a/test/test_files/tinysnb/projection/single_label.test b/test/test_files/tinysnb/projection/single_label.test index 05b3f8fd2cf..281b7ca5a9b 100644 --- a/test/test_files/tinysnb/projection/single_label.test +++ b/test/test_files/tinysnb/projection/single_label.test @@ -453,12 +453,22 @@ Dan|Carol ---- 1 1|10|[4,5] +-NAME ReturnStructLiteralWithNull +-QUERY RETURN {info: {name: "AliceBobCarolDan", gender: "female", hobby: null}, height: 1.8, age: null} +---- 1 +{INFO: {NAME: AliceBobCarolDan, GENDER: female, HOBBY: }, HEIGHT: 1.800000, AGE: } + +-NAME ReturnStructListLiteralWithNull +-QUERY RETURN {info: {name: "smith", gender: null, hobby: [null, "footBall"]}, height: 1.8, age: null} +---- 1 +{INFO: {NAME: smith, GENDER: , HOBBY: [,footBall]}, HEIGHT: 1.800000, AGE: } + -NAME ReturnStruct -QUERY MATCH (m:movies) RETURN m.description ---- 3 {RATING: 1223.000000, VIEWS: 10003, RELEASE: 2011-02-11 16:44:22, FILM: 2013-02-22} -{RATING: 5.300000, VIEWS: 152, RELEASE: 2011-08-20 11:25:30, FILM: 2012-05-11} -{RATING: 7.000000, VIEWS: 982, RELEASE: 2018-11-13 13:33:11, FILM: 2014-09-12} +{RATING: 5.300000, VIEWS: 152, RELEASE: 2011-08-20 11:25:30, FILM: } +{RATING: 7.000000, VIEWS: , RELEASE: 2018-11-13 13:33:11, FILM: 2014-09-12} -NAME ReturnStructLiteralWithUnflatFlatChildren -QUERY MATCH (p:person)-[e:knows]->(p1:person) return {name: p.fName, id: p1.ID, date: e.date}