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

SYaoJun 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 63162232 feat(cpp): Add map-based AddPropertyColumn overload for 
EdgesBuilder (#932)
63162232 is described below

commit 63162232e9fab4c4223b50f2507323bffca6d7af
Author: Jason <[email protected]>
AuthorDate: Thu Jul 23 19:31:01 2026 +0800

    feat(cpp): Add map-based AddPropertyColumn overload for EdgesBuilder (#932)
    
    Introduce a new AddPropertyColumn overload that accepts
    unordered_map<pair<IdType, IdType>, any>, allowing users to
    set edge properties by (src, dst) key mapping instead of
    relying on insertion order. Edges not present in the map
    will not have the property set.
    
    Signed-off-by: Jason <[email protected]>
---
 cpp/src/graphar/high-level/edges_builder.h | 40 +++++++++++++++++++
 cpp/test/test_builder.cc                   | 63 +++++++++++++++++++++++++++++-
 2 files changed, 102 insertions(+), 1 deletion(-)

diff --git a/cpp/src/graphar/high-level/edges_builder.h 
b/cpp/src/graphar/high-level/edges_builder.h
index 382d0b96..d92799ed 100644
--- a/cpp/src/graphar/high-level/edges_builder.h
+++ b/cpp/src/graphar/high-level/edges_builder.h
@@ -39,6 +39,21 @@ class Array;
 
 namespace graphar::builder {
 
+/**
+ * @brief Hash functor for std::pair<IdType, IdType>.
+ *
+ * This is needed because the C++ standard does not provide a default
+ * std::hash specialization for std::pair.
+ */
+struct PairIdHash {
+  std::size_t operator()(const std::pair<IdType, IdType>& p) const noexcept {
+    // Combine two 64-bit hashes: a common technique is to XOR the
+    // first hash with a shifted version of the second.
+    std::hash<IdType> h;
+    return h(p.first) ^ (h(p.second) << 1);
+  }
+};
+
 /**
  * @brief Edge is designed for constructing edges builder.
  *
@@ -305,6 +320,31 @@ class EdgesBuilder {
     }
     return Status::OK();
   }
+
+  /**
+   * @brief Add a property to edges in the collection by (src, dst) mapping.
+   *
+   * Edges whose (src_id, dst_id) is not present in the map will not have this
+   * property set (written as null later).
+   *
+   * @param property name of the property
+   * @param values map from (src_id, dst_id) to the property value
+   * @return Status: ok.
+   */
+  [[nodiscard]] Status AddPropertyColumn(
+      const std::string& property,
+      const std::unordered_map<std::pair<IdType, IdType>, std::any, 
PairIdHash>&
+          values) {
+    for (auto& [chunk_index, edges] : edges_) {
+      for (Edge& edge : edges) {
+        auto it = values.find({edge.GetSource(), edge.GetDestination()});
+        if (it != values.end()) {
+          edge.AddProperty(property, it->second);
+        }
+      }
+    }
+    return Status::OK();
+  }
   /**
    * @brief Get the current number of edges in the collection.
    *
diff --git a/cpp/test/test_builder.cc b/cpp/test/test_builder.cc
index 67026e26..b97648f2 100644
--- a/cpp/test/test_builder.cc
+++ b/cpp/test/test_builder.cc
@@ -18,11 +18,13 @@
  */
 
 #include <time.h>
+#include <any>
 #include <fstream>
 #include <iostream>
 #include <map>
 #include <sstream>
 #include <string>
+#include <unordered_map>
 
 #include "arrow/api.h"
 #include "arrow/csv/api.h"
@@ -269,7 +271,7 @@ TEST_CASE_METHOD(GlobalFixture, "test_edges_builder") {
   // check the number of edges in builder
   REQUIRE(builder->GetNum() == lines);
 
-  // add property column
+  // add property column via vector
   std::vector<std::any> string_values(builder->GetNum(),
                                       std::string("test_edge"));
 
@@ -280,6 +282,65 @@ TEST_CASE_METHOD(GlobalFixture, "test_edges_builder") {
   REQUIRE(
       builder->AddPropertyColumn("creationDate", string_values).IsInvalid());
 
+  // add property column via (src, dst) map
+  {
+    // build a new builder for map-based test
+    auto maybe_builder2 = builder::EdgesBuilder::Make(
+        edge_info, "/tmp/", AdjListType::ordered_by_dest, vertices_num);
+    REQUIRE(!maybe_builder2.has_error());
+    auto builder2 = maybe_builder2.value();
+
+    // add a few edges manually
+    REQUIRE(builder2->AddEdge(builder::Edge(0, 1)).ok());
+    REQUIRE(builder2->AddEdge(builder::Edge(0, 2)).ok());
+    REQUIRE(builder2->AddEdge(builder::Edge(1, 3)).ok());
+    REQUIRE(builder2->AddEdge(builder::Edge(2, 4)).ok());
+
+    // build map: (src, dst) -> value
+    std::unordered_map<std::pair<IdType, IdType>, std::any, 
builder::PairIdHash>
+        value_map;
+    value_map[{0, 1}] = std::string("edge_0_1");
+    value_map[{0, 2}] = std::string("edge_0_2");
+    value_map[{1, 3}] = std::string("edge_1_3");
+    // deliberately omit (2, 4) to test null handling
+
+    REQUIRE(builder2->AddPropertyColumn("creationDate", value_map).ok());
+    REQUIRE(builder2->Dump().ok());
+
+    // verify: read back and check
+    auto parquet_file =
+        "/tmp/edge/person_knows_person/ordered_by_dest/creationDate/part0/"
+        "chunk0";
+    std::unique_ptr<parquet::arrow::FileReader> reader;
+    REQUIRE(graphar::util::OpenParquetArrowReader(
+                parquet_file, arrow::default_memory_pool(), &reader)
+                .ok());
+    auto maybe_table = reader->ReadTable();
+    REQUIRE(maybe_table.ok());
+    auto table = maybe_table.ValueOrDie();
+    auto col = table->GetColumnByName("creationDate");
+    REQUIRE(col != nullptr);
+    auto arr = std::static_pointer_cast<arrow::StringArray>(col->chunk(0));
+    REQUIRE(arr->length() == 4);
+
+    // Check that the mapped edges have the correct values
+    bool found_0_1 = false, found_0_2 = false, found_1_3 = false;
+    for (int i = 0; i < arr->length(); i++) {
+      if (arr->IsValid(i)) {
+        std::string val = arr->GetString(i);
+        if (val == "edge_0_1")
+          found_0_1 = true;
+        if (val == "edge_0_2")
+          found_0_2 = true;
+        if (val == "edge_1_3")
+          found_1_3 = true;
+      }
+    }
+    REQUIRE(found_0_1);
+    REQUIRE(found_0_2);
+    REQUIRE(found_1_3);
+  }
+
   // dump to files
   REQUIRE(builder->Dump().ok());
 


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

Reply via email to