Skip to content

Commit 30c5a8b

Browse files
committed
Added from_string support for SparseVector
1 parent 14ff4c9 commit 30c5a8b

File tree

3 files changed

+39
-6
lines changed

3 files changed

+39
-6
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.2.4 (unreleased)
2+
3+
- Added `from_string` support for `SparseVector`
4+
15
## 0.2.3 (2025-07-13)
26

37
- Fixed `duplicate symbol` errors

include/pgvector/pqxx.hpp

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,37 @@ template <> struct nullness<pgvector::SparseVector> : no_null<pgvector::SparseVe
118118
template <> struct string_traits<pgvector::SparseVector> {
119119
static constexpr bool converts_to_string{true};
120120

121-
// TODO add from_string
122-
static constexpr bool converts_from_string{false};
121+
static constexpr bool converts_from_string{true};
122+
123+
static pgvector::SparseVector from_string(std::string_view text) {
124+
if (text.size() < 4 || text.front() != '{') {
125+
throw conversion_error("Malformed sparsevec literal");
126+
}
127+
128+
size_t n = text.find("}/", 1);
129+
if (n == std::string_view::npos) {
130+
throw conversion_error("Malformed sparsevec literal");
131+
}
132+
133+
std::vector<int> indices;
134+
std::vector<float> values;
135+
std::istringstream ss(std::string(text.substr(1, n)));
136+
while (ss.good()) {
137+
std::string substr;
138+
std::getline(ss, substr, ',');
139+
140+
size_t ne = substr.find(":");
141+
if (ne == std::string::npos) {
142+
throw conversion_error("Malformed sparsevec literal");
143+
}
144+
145+
indices.push_back(std::stoi(substr.substr(0, ne)) - 1);
146+
values.push_back(std::stof(substr.substr(ne + 1)));
147+
}
148+
149+
int dimensions = std::stoi(std::string(text.substr(n + 2)));
150+
return pgvector::SparseVector(dimensions, indices, values);
151+
}
123152

124153
static zview to_buf(char* begin, char* end, const pgvector::SparseVector& value) {
125154
char *const next = into_buf(begin, end, value);

test/pqxx_test.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,9 @@ void test_sparsevec(pqxx::connection &conn) {
7878

7979
pqxx::result res = tx.exec("SELECT sparse_embedding FROM items ORDER BY sparse_embedding <-> $1", {embedding2});
8080
assert(res.size() == 3);
81-
assert(res[0][0].as<std::string>() == "{1:4,2:5,3:6}/3");
82-
assert(res[1][0].as<std::string>() == "{1:1,2:2,3:3}/3");
83-
assert(!res[2][0].as<std::optional<std::string>>().has_value());
81+
assert(res[0][0].as<pgvector::SparseVector>() == embedding2);
82+
assert(res[1][0].as<pgvector::SparseVector>() == embedding);
83+
assert(!res[2][0].as<std::optional<pgvector::SparseVector>>().has_value());
8484
}
8585

8686
void test_sparsevec_nnz(pqxx::connection &conn) {
@@ -224,7 +224,7 @@ void test_sparsevec_to_string() {
224224
}
225225

226226
void test_sparsevec_from_string() {
227-
// TODO add
227+
assert(pqxx::from_string<pgvector::SparseVector>("{1:1,3:2,5:3}/6") == pgvector::SparseVector({1, 0, 2, 0, 3, 0}));
228228
}
229229

230230
void test_pqxx() {

0 commit comments

Comments
 (0)