This is an automated email from the ASF dual-hosted git repository.

lixueclaire pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-graphar.git


The following commit(s) were added to refs/heads/main by this push:
     new eb1af90d feat(c++): support build multi-properties in high-level API 
(#722)
eb1af90d is described below

commit eb1af90d7c2e1a22d6e8b0e9c447b876c9aaff9b
Author: Xiaokang Yang <[email protected]>
AuthorDate: Tue Aug 5 14:54:02 2025 +0800

    feat(c++): support build multi-properties in high-level API (#722)
---
 cpp/examples/high_level_writer_example.cc      |  13 ++-
 cpp/src/graphar/arrow/chunk_writer.cc          |  11 ++-
 cpp/src/graphar/high-level/vertices_builder.cc | 129 +++++++++++++++---------
 cpp/src/graphar/high-level/vertices_builder.h  |  92 +++++++++++++++++
 cpp/src/graphar/types.h                        |  13 ++-
 cpp/test/test_multi_property.cc                | 130 ++++++++++++++++++++++++-
 testing                                        |   2 +-
 7 files changed, 338 insertions(+), 52 deletions(-)

diff --git a/cpp/examples/high_level_writer_example.cc 
b/cpp/examples/high_level_writer_example.cc
index 0bd040fa..c61bd1f4 100644
--- a/cpp/examples/high_level_writer_example.cc
+++ b/cpp/examples/high_level_writer_example.cc
@@ -29,7 +29,7 @@
 void vertices_builder() {
   // construct vertices builder
   std::string vertex_meta_file =
-      GetTestingResourceRoot() + "/ldbc_sample/parquet/" + "person.vertex.yml";
+      GetTestingResourceRoot() + "/ldbc/parquet/" + "person.vertex.yml";
   auto vertex_meta = graphar::Yaml::LoadFile(vertex_meta_file).value();
   auto vertex_info = graphar::VertexInfo::Load(vertex_meta).value();
   graphar::IdType start_index = 0;
@@ -45,11 +45,16 @@ void vertices_builder() {
   // prepare vertex data
   int vertex_count = 3;
   std::vector<std::string> property_names = {"id", "firstName", "lastName",
-                                             "gender"};
+                                             "gender", "emails"};
   std::vector<int64_t> id = {0, 1, 2};
   std::vector<std::string> firstName = {"John", "Jane", "Alice"};
   std::vector<std::string> lastName = {"Smith", "Doe", "Wonderland"};
   std::vector<std::string> gender = {"male", "famale", "famale"};
+  std::vector<std::vector<std::string>> emails = {
+      {"[email protected]", "[email protected]"},
+      {"[email protected]"},
+      {"[email protected]", "[email protected]",
+       "[email protected]"}};
 
   // add vertices
   for (int i = 0; i < vertex_count; i++) {
@@ -58,6 +63,10 @@ void vertices_builder() {
     v.AddProperty(property_names[1], firstName[i]);
     v.AddProperty(property_names[2], lastName[i]);
     v.AddProperty(property_names[3], gender[i]);
+    for (const auto& email : emails[i]) {
+      v.AddProperty(graphar::Cardinality::LIST, property_names[4],
+                    email);  // Multi-property
+    }
     ASSERT(builder.AddVertex(v).ok());
   }
 
diff --git a/cpp/src/graphar/arrow/chunk_writer.cc 
b/cpp/src/graphar/arrow/chunk_writer.cc
index c8612383..ba949bd2 100644
--- a/cpp/src/graphar/arrow/chunk_writer.cc
+++ b/cpp/src/graphar/arrow/chunk_writer.cc
@@ -23,6 +23,7 @@
 
 #include "arrow/api.h"
 #include "arrow/compute/api.h"
+#include "graphar/fwd.h"
 #include "graphar/writer_util.h"
 #if defined(ARROW_VERSION) && ARROW_VERSION >= 12000000
 #include "arrow/acero/exec_plan.h"
@@ -193,10 +194,16 @@ Status VertexPropertyWriter::validate(
                                " does not exist in the input table.");
       }
       auto field = schema->field(indice);
-      if (DataType::ArrowDataTypeToDataType(field->type()) != property.type) {
+      auto schema_data_type = DataType::DataTypeToArrowDataType(property.type);
+      if (property.cardinality != Cardinality::SINGLE) {
+        schema_data_type = arrow::list(schema_data_type);
+      }
+      if (!DataType::ArrowDataTypeToDataType(field->type())
+               ->Equals(DataType::ArrowDataTypeToDataType(schema_data_type))) {
         return Status::TypeError(
             "The data type of property: ", property.name, " is ",
-            property.type->ToTypeName(), ", but got ",
+            DataType::ArrowDataTypeToDataType(schema_data_type)->ToTypeName(),
+            ", but got ",
             DataType::ArrowDataTypeToDataType(field->type())->ToTypeName(),
             ".");
       }
diff --git a/cpp/src/graphar/high-level/vertices_builder.cc 
b/cpp/src/graphar/high-level/vertices_builder.cc
index fa93fa61..fd0208be 100644
--- a/cpp/src/graphar/high-level/vertices_builder.cc
+++ b/cpp/src/graphar/high-level/vertices_builder.cc
@@ -18,8 +18,14 @@
  */
 
 #include "graphar/high-level/vertices_builder.h"
+#include <any>
+#include <iterator>
+#include <vector>
 #include "graphar/convert_to_arrow_type.h"
+#include "graphar/fwd.h"
 #include "graphar/graph_info.h"
+#include "graphar/label.h"
+#include "graphar/status.h"
 
 namespace graphar::builder {
 
@@ -66,59 +72,62 @@ Status VerticesBuilder::validate(const Vertex& v, IdType 
index,
       bool invalid_type = false;
       switch (type->id()) {
       case Type::BOOL:
-        if (property.second.type() !=
-            typeid(typename TypeToArrowType<Type::BOOL>::CType)) {
-          invalid_type = true;
-        }
+        GAR_RETURN_NOT_OK(
+            v.ValidatePropertyType<typename 
TypeToArrowType<Type::BOOL>::CType>(
+                property.first,
+                vertex_info_->GetPropertyCardinality(property.first).value()));
         break;
       case Type::INT32:
-        if (property.second.type() !=
-            typeid(typename TypeToArrowType<Type::INT32>::CType)) {
-          invalid_type = true;
-        }
+        GAR_RETURN_NOT_OK(v.ValidatePropertyType<
+                          typename TypeToArrowType<Type::INT32>::CType>(
+            property.first,
+            vertex_info_->GetPropertyCardinality(property.first).value()));
         break;
       case Type::INT64:
-        if (property.second.type() !=
-            typeid(typename TypeToArrowType<Type::INT64>::CType)) {
-          invalid_type = true;
-        }
+        GAR_RETURN_NOT_OK(v.ValidatePropertyType<
+                          typename TypeToArrowType<Type::INT64>::CType>(
+            property.first,
+            vertex_info_->GetPropertyCardinality(property.first).value()));
         break;
       case Type::FLOAT:
-        if (property.second.type() !=
-            typeid(typename TypeToArrowType<Type::FLOAT>::CType)) {
-          invalid_type = true;
-        }
+        GAR_RETURN_NOT_OK(v.ValidatePropertyType<
+                          typename TypeToArrowType<Type::FLOAT>::CType>(
+            property.first,
+            vertex_info_->GetPropertyCardinality(property.first).value()));
         break;
       case Type::DOUBLE:
-        if (property.second.type() !=
-            typeid(typename TypeToArrowType<Type::DOUBLE>::CType)) {
-          invalid_type = true;
-        }
+        GAR_RETURN_NOT_OK(v.ValidatePropertyType<
+                          typename TypeToArrowType<Type::DOUBLE>::CType>(
+            property.first,
+            vertex_info_->GetPropertyCardinality(property.first).value()));
         break;
       case Type::STRING:
-        if (property.second.type() !=
-            typeid(typename TypeToArrowType<Type::STRING>::CType)) {
-          invalid_type = true;
-        }
+        GAR_RETURN_NOT_OK(v.ValidatePropertyType<
+                          typename TypeToArrowType<Type::STRING>::CType>(
+            property.first,
+            vertex_info_->GetPropertyCardinality(property.first).value()));
         break;
       case Type::DATE:
         // date is stored as int32_t
-        if (property.second.type() !=
-            typeid(typename TypeToArrowType<Type::DATE>::CType::c_type)) {
-          invalid_type = true;
-        }
+        GAR_RETURN_NOT_OK(v.ValidatePropertyType<
+                          typename TypeToArrowType<Type::DATE>::CType::c_type>(
+            property.first,
+            vertex_info_->GetPropertyCardinality(property.first).value()));
         break;
       case Type::TIMESTAMP:
         // timestamp is stored as int64_t
-        if (property.second.type() !=
-            typeid(typename TypeToArrowType<Type::TIMESTAMP>::CType::c_type)) {
-          invalid_type = true;
-        }
+        GAR_RETURN_NOT_OK(
+            v.ValidatePropertyType<
+                typename TypeToArrowType<Type::TIMESTAMP>::CType::c_type>(
+                property.first,
+                vertex_info_->GetPropertyCardinality(property.first).value()));
         break;
       default:
         return Status::TypeError("Unsupported property type.");
       }
-      if (invalid_type) {
+      if (invalid_type &&
+          Cardinality::SINGLE ==
+              vertex_info_->GetPropertyCardinality(property.first).value()) {
         return Status::TypeError(
             "Invalid data type for property ", property.first + ", defined as 
",
             type->ToTypeName(), ", but got ", property.second.type().name());
@@ -134,16 +143,41 @@ Status VerticesBuilder::tryToAppend(
     std::shared_ptr<arrow::Array>& array) {  // NOLINT
   using CType = typename TypeToArrowType<type>::CType;
   arrow::MemoryPool* pool = arrow::default_memory_pool();
-  typename TypeToArrowType<type>::BuilderType builder(pool);
-  for (auto& v : vertices_) {
-    if (v.Empty() || !v.ContainProperty(property_name)) {
-      RETURN_NOT_ARROW_OK(builder.AppendNull());
-    } else {
-      RETURN_NOT_ARROW_OK(
-          builder.Append(std::any_cast<CType>(v.GetProperty(property_name))));
+  auto builder =
+      std::make_shared<typename TypeToArrowType<type>::BuilderType>(pool);
+  auto cardinality =
+      vertex_info_->GetPropertyCardinality(property_name).value();
+  if (cardinality != Cardinality::SINGLE) {
+    arrow::ListBuilder list_builder(pool, builder);
+    for (auto& v : vertices_) {
+      RETURN_NOT_ARROW_OK(list_builder.Append());
+      if (v.Empty() || !v.ContainProperty(property_name)) {
+        RETURN_NOT_ARROW_OK(builder->AppendNull());
+      } else {
+        if (!v.IsMultiProperty(property_name)) {
+          RETURN_NOT_ARROW_OK(builder->Append(
+              std::any_cast<CType>(v.GetProperty(property_name))));
+        } else {
+          auto property_value_list = std::any_cast<std::vector<std::any>>(
+              v.GetProperty(property_name));
+          for (auto& value : property_value_list) {
+            RETURN_NOT_ARROW_OK(builder->Append(std::any_cast<CType>(value)));
+          }
+        }
+      }
+    }
+    array = list_builder.Finish().ValueOrDie();
+  } else {
+    for (auto& v : vertices_) {
+      if (v.Empty() || !v.ContainProperty(property_name)) {
+        RETURN_NOT_ARROW_OK(builder->AppendNull());
+      } else {
+        RETURN_NOT_ARROW_OK(builder->Append(
+            std::any_cast<CType>(v.GetProperty(property_name))));
+      }
     }
+    array = builder->Finish().ValueOrDie();
   }
-  array = builder.Finish().ValueOrDie();
   return Status::OK();
 }
 
@@ -219,11 +253,18 @@ Result<std::shared_ptr<arrow::Table>> 
VerticesBuilder::convertToTable() {
   for (auto& property_group : property_groups) {
     for (auto& property : property_group->GetProperties()) {
       // add a column to schema
-      schema_vector.push_back(arrow::field(
-          property.name, DataType::DataTypeToArrowDataType(property.type)));
+      if (vertex_info_->GetPropertyCardinality(property.name).value() !=
+          Cardinality::SINGLE) {
+        schema_vector.push_back(arrow::field(
+            property.name,
+            arrow::list(DataType::DataTypeToArrowDataType(property.type))));
+      } else {
+        schema_vector.push_back(arrow::field(
+            property.name, DataType::DataTypeToArrowDataType(property.type)));
+      }
       // add a column to data
       std::shared_ptr<arrow::Array> array;
-      appendToArray(property.type, property.name, array);
+      GAR_RETURN_NOT_OK(appendToArray(property.type, property.name, array));
       arrays.push_back(array);
     }
   }
diff --git a/cpp/src/graphar/high-level/vertices_builder.h 
b/cpp/src/graphar/high-level/vertices_builder.h
index 40c73d57..b044c59a 100644
--- a/cpp/src/graphar/high-level/vertices_builder.h
+++ b/cpp/src/graphar/high-level/vertices_builder.h
@@ -20,16 +20,21 @@
 #pragma once
 
 #include <any>
+#include <cassert>
 #include <cstddef>
 #include <memory>
 #include <string>
 #include <unordered_map>
+#include <unordered_set>
 #include <utility>
 #include <vector>
 
 #include "graphar/arrow/chunk_writer.h"
+#include "graphar/fwd.h"
 #include "graphar/graph_info.h"
 #include "graphar/result.h"
+#include "graphar/status.h"
+#include "graphar/types.h"
 #include "graphar/writer_util.h"
 
 // forward declaration
@@ -88,6 +93,28 @@ class Vertex {
     properties_[name] = val;
   }
 
+  inline void AddProperty(const Cardinality cardinality,
+                          const std::string& name, const std::any& val) {
+    if (cardinality == Cardinality::SINGLE) {
+      cardinalities_[name] = Cardinality::SINGLE;
+      AddProperty(name, val);
+      return;
+    }
+    empty_ = false;
+    if (cardinalities_.find(name) != cardinalities_.end()) {
+      assert(cardinalities_[name] == cardinality);
+      auto property_value_list =
+          std::any_cast<std::vector<std::any>>(properties_[name]);
+      property_value_list.push_back(val);
+      properties_[name] = property_value_list;
+    } else {
+      auto property_value_list = std::vector<std::any>();
+      property_value_list.push_back(val);
+      properties_[name] = property_value_list;
+    }
+    cardinalities_[name] = cardinality;
+  }
+
   /**
    * @brief Get a property of the vertex.
    *
@@ -118,10 +145,75 @@ class Vertex {
     return (properties_.find(property) != properties_.end());
   }
 
+  inline bool IsMultiProperty(const std::string& property) const {
+    return (cardinalities_.find(property) != cardinalities_.end() &&
+            cardinalities_.at(property) != Cardinality::SINGLE);
+  }
+
+  template <typename T>
+  Status ValidatePropertyType(const std::string& property,
+                              const Cardinality cardinality) const {
+    if (cardinality == Cardinality::SINGLE && IsMultiProperty(property)) {
+      return Status::TypeError(
+          "Invalid data cardinality for property ", property,
+          ", defined as SINGLE but got ",
+          cardinalities_.at(property) == Cardinality::LIST ? "LIST" : "SET");
+    }
+    if (IsMultiProperty(property) &&
+        (cardinality == Cardinality::SET ||
+         cardinalities_.at(property) == Cardinality::SET)) {
+      GAR_RETURN_NOT_OK(ValidateMultiPropertySet<T>(property));
+    }
+    if (IsMultiProperty(property)) {
+      auto value_list =
+          std::any_cast<std::vector<std::any>>(properties_.at(property));
+      for (auto value : value_list) {
+        auto& value_type = value.type();
+        if (value_type != typeid(T)) {
+          return Status::TypeError("Invalid data type for property ", property,
+                                   ", defined as ", typeid(T).name(),
+                                   ", but got ", value_type.name());
+        }
+      }
+    } else {
+      auto& value_type = properties_.at(property).type();
+      if (value_type != typeid(T)) {
+        return Status::TypeError("Invalid data type for property ", property,
+                                 ", defined as ", typeid(T).name(),
+                                 ", but got ", value_type.name());
+      }
+    }
+    return Status::OK();
+  }
+
+  template <typename T>
+  Status ValidateMultiProperty(const std::string& property) const {
+    if (IsMultiProperty(property) &&
+        cardinalities_.at(property) == Cardinality::SET) {
+      GAR_RETURN_NOT_OK(ValidateMultiPropertySet<T>(property));
+    }
+    return Status::OK();
+  }
+
+  template <typename T>
+  Status ValidateMultiPropertySet(const std::string& property) const {
+    auto vec = std::any_cast<std::vector<std::any>>(properties_.at(property));
+    std::unordered_set<T> seen;
+    for (const auto& item : vec) {
+      if (!seen.insert(std::any_cast<T>(item)).second) {
+        return Status::KeyError(
+            "Duplicate values exist in set type multi-property key: ", 
property,
+            " value: ", std::any_cast<T>(item));
+      }
+    }
+    return Status::OK();
+  }
+
  private:
   IdType id_;
   bool empty_;
   std::unordered_map<std::string, std::any> properties_;
+  std::unordered_map<std::string, Cardinality> cardinalities_;
 };
 
 /**
diff --git a/cpp/src/graphar/types.h b/cpp/src/graphar/types.h
index ac318ca4..54f322ee 100644
--- a/cpp/src/graphar/types.h
+++ b/cpp/src/graphar/types.h
@@ -100,8 +100,17 @@ class DataType {
   inline DataType& operator=(const DataType& other) = default;
 
   bool Equals(const DataType& other) const {
-    return id_ == other.id_ &&
-           user_defined_type_name_ == other.user_defined_type_name_;
+    if (id_ != other.id_ ||
+        user_defined_type_name_ != other.user_defined_type_name_) {
+      return false;
+    }
+    if (child_ == nullptr && other.child_ == nullptr) {
+      return true;
+    }
+    if (child_ != nullptr && other.child_ != nullptr) {
+      return child_->Equals(other.child_);
+    }
+    return false;
   }
 
   bool Equals(const std::shared_ptr<DataType>& other) const {
diff --git a/cpp/test/test_multi_property.cc b/cpp/test/test_multi_property.cc
index f82fc608..fc07fe62 100644
--- a/cpp/test/test_multi_property.cc
+++ b/cpp/test/test_multi_property.cc
@@ -24,13 +24,18 @@
 #include <ostream>
 #include <string>
 #include "arrow/api.h"
+#include "arrow/filesystem/api.h"
 #include "examples/config.h"
 #include "graphar/arrow/chunk_reader.h"
 #include "graphar/arrow/chunk_writer.h"
+#include "graphar/fwd.h"
 #include "graphar/graph_info.h"
 #include "graphar/types.h"
 #include "parquet/arrow/writer.h"
 
+#include "graphar/api/high_level_writer.h"
+#include "graphar/writer_util.h"
+
 #include "./util.h"
 
 #include <catch2/catch_test_macros.hpp>
@@ -58,7 +63,7 @@ std::shared_ptr<arrow::Table> read_csv_to_table(const 
std::string& filename) {
 }
 
 namespace graphar {
-TEST_CASE_METHOD(GlobalFixture, "read from csv file") {
+TEST_CASE_METHOD(GlobalFixture, "read multi-properties from csv file") {
   // read labels csv file as arrow table
   auto person_table = read_csv_to_table(test_data_dir + 
"/ldbc/person_0_0.csv");
   auto seed = static_cast<unsigned int>(time(NULL));
@@ -158,4 +163,127 @@ TEST_CASE_METHOD(GlobalFixture, "read from csv file") {
   std::cout << emails << std::endl;
   ASSERT(expected_emails == emails);
 }
+TEST_CASE_METHOD(GlobalFixture, "TestMultiProperty high level builder") {
+  int vertex_count = 3;
+  std::vector<std::string> property_names = {"id", "emails"};
+  std::vector<int64_t> id = {0, 1, 2};
+  std::vector<std::vector<std::string>> emails = {
+      {"[email protected]", "[email protected]"},
+      {"[email protected]"},
+      {"[email protected]", "[email protected]",
+       "[email protected]"}};
+  std::string vertex_meta_file =
+      GetTestingResourceRoot() + "/ldbc/parquet/" + "person.vertex.yml";
+  auto vertex_meta = graphar::Yaml::LoadFile(vertex_meta_file).value();
+  auto vertex_info = graphar::VertexInfo::Load(vertex_meta).value();
+  graphar::IdType start_index = 0;
+  SECTION("add sample values to set property") {
+    graphar::builder::VerticesBuilder builder(
+        vertex_info, "/tmp/", start_index, nullptr,
+        graphar::ValidateLevel::strong_validate);
+    // prepare vertex data
+    int vertex_count = 1;
+    std::vector<std::vector<std::string>> emails = {
+        {"[email protected]", "[email protected]"}};
+    // add vertices
+    for (int i = 0; i < vertex_count; i++) {
+      graphar::builder::Vertex v;
+      for (const auto& email : emails[i]) {
+        v.AddProperty(graphar::Cardinality::SET, "emails",
+                      email);  // Multi-property
+      }
+      ASSERT(builder.AddVertex(v).IsKeyError());
+    }
+  }
+  SECTION("test add single values to set property") {
+    graphar::builder::VerticesBuilder builder(
+        vertex_info, "/tmp/", start_index, nullptr,
+        graphar::ValidateLevel::strong_validate);
+    // prepare vertex data
+    int vertex_count = 3;
+    std::vector<std::string> emails = {"[email protected]", "[email protected]",
+                                       "[email protected]"};
+    for (int i = 0; i < vertex_count; i++) {
+      graphar::builder::Vertex v;
+      v.AddProperty("emails", emails[i]);
+      ASSERT(builder.AddVertex(v).ok());
+    }
+  }
+  SECTION("test add multi values to single property") {
+    auto single_email =
+        CreatePropertyGroup({Property("single_email", string(), false)},
+                            FileType::PARQUET, "single_email/");
+    auto test_vertex_info = 
vertex_info->AddPropertyGroup(single_email).value();
+    graphar::builder::VerticesBuilder builder(
+        test_vertex_info, "/tmp/", start_index, nullptr,
+        graphar::ValidateLevel::strong_validate);
+    for (int i = 0; i < vertex_count; i++) {
+      graphar::builder::Vertex v;
+      v.AddProperty(graphar::Cardinality::LIST, "single_email", emails[i]);
+      ASSERT(builder.AddVertex(v).IsTypeError());
+    }
+  }
+  SECTION("test add multi values to set property") {
+    auto set_email = CreatePropertyGroup(
+        {Property("set_email", string(), false, true, Cardinality::SET)},
+        FileType::PARQUET, "set_email/");
+    auto test_vertex_info = vertex_info->AddPropertyGroup(set_email).value();
+    graphar::builder::VerticesBuilder builder(
+        test_vertex_info, "/tmp/", start_index, nullptr,
+        graphar::ValidateLevel::strong_validate);
+    int vertex_count = 1;
+    std::vector<std::vector<std::string>> emails = {
+        {"[email protected]", "[email protected]"}};
+    // add vertices
+    for (int i = 0; i < vertex_count; i++) {
+      graphar::builder::Vertex v;
+      for (const auto& email : emails[i]) {
+        v.AddProperty(graphar::Cardinality::LIST, "set_email",
+                      email);  // Multi-property
+      }
+      ASSERT(builder.AddVertex(v).IsKeyError());
+    }
+  }
+  SECTION("test write to file") {
+    graphar::builder::VerticesBuilder builder(
+        vertex_info, "/tmp/", start_index, nullptr,
+        graphar::ValidateLevel::strong_validate);
+    // prepare vertex data
+    // add vertices
+    for (int i = 0; i < vertex_count; i++) {
+      graphar::builder::Vertex v;
+      v.AddProperty(property_names[0], id[i]);
+      for (const auto& email : emails[i]) {
+        v.AddProperty(graphar::Cardinality::SET, property_names[1], email);
+      }
+      ASSERT(builder.AddVertex(v).ok());
+    }
+    auto st = builder.Dump();
+    std::cout << st.message() << std::endl;
+    ASSERT(st.ok());
+  }
+  SECTION("test read from file") {
+    // read from file
+    auto maybe_reader = graphar::VertexPropertyArrowChunkReader::Make(
+        vertex_info, vertex_info->GetPropertyGroup(property_names[1]), 
"/tmp/");
+    assert(maybe_reader.status().ok());
+    auto reader = maybe_reader.value();
+    assert(reader->seek(0).ok());
+    auto table_result = reader->GetChunk();
+    ASSERT(table_result.status().ok());
+    auto table = table_result.value();
+    auto index = table->schema()->GetFieldIndex(property_names[1]);
+    auto emails_col = table->column(index)->chunk(0);
+    auto result = std::static_pointer_cast<arrow::ListArray>(
+        emails_col->View(arrow::list(arrow::large_utf8())).ValueOrDie());
+    ASSERT(result->length() == 3);
+    for (int i = 0; i < result->length(); i++) {
+      auto email_result = std::static_pointer_cast<arrow::LargeStringArray>(
+          result->value_slice(i));
+      for (int j = 0; j < email_result->length(); j++) {
+        ASSERT(emails[i][j] == email_result->GetString(j));
+      }
+    }
+  }
+}
 }  // namespace graphar
diff --git a/testing b/testing
index 955596c3..12b4b175 160000
--- a/testing
+++ b/testing
@@ -1 +1 @@
-Subproject commit 955596c325ceba7b607e285738e3dd0ce4ff424e
+Subproject commit 12b4b17561ca3e414366b176a8760b7ee825f7d9


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to