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]