Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
85f89dc
fix
iaojnh May 20, 2026
9e58670
Merge branch 'main' into fix/exclude-arrow
iaojnh May 20, 2026
46bf9aa
Merge branch 'alibaba:main' into main
iaojnh May 21, 2026
d6547aa
Merge branch 'alibaba:main' into main
iaojnh May 25, 2026
c9a4d94
Merge branch 'alibaba:main' into main
iaojnh May 26, 2026
5bff8b6
Merge branch 'alibaba:main' into main
iaojnh May 29, 2026
10f1dee
Merge branch 'alibaba:main' into main
iaojnh Jun 1, 2026
ad6ac3a
Merge branch 'alibaba:main' into main
iaojnh Jun 2, 2026
5c045b0
Merge branch 'alibaba:main' into main
iaojnh Jun 3, 2026
570da53
Merge branch 'alibaba:main' into main
iaojnh Jun 8, 2026
d3fecea
Merge branch 'alibaba:main' into main
iaojnh Jun 9, 2026
4f07741
Merge branch 'alibaba:main' into main
iaojnh Jun 10, 2026
9e900c5
Merge branch 'alibaba:main' into main
iaojnh Jun 11, 2026
4761640
Merge branch 'alibaba:main' into main
iaojnh Jun 15, 2026
a842e22
Merge branch 'alibaba:main' into main
iaojnh Jun 15, 2026
8bb7e53
Merge branch 'alibaba:main' into main
iaojnh Jun 17, 2026
d9af524
Merge branch 'alibaba:main' into main
iaojnh Jun 22, 2026
973d233
Merge branch 'alibaba:main' into main
iaojnh Jun 24, 2026
43cb151
Merge branch 'alibaba:main' into main
iaojnh Jun 25, 2026
4685c96
Merge branch 'alibaba:main' into main
iaojnh Jun 30, 2026
a07dd9d
Merge branch 'alibaba:main' into main
iaojnh Jul 10, 2026
229dc57
Merge branch 'alibaba:main' into main
iaojnh Jul 13, 2026
9ac71b2
Merge branch 'alibaba:main' into main
iaojnh Jul 15, 2026
23a4354
Merge branch 'alibaba:main' into main
iaojnh Jul 17, 2026
a1ad5fc
Merge branch 'alibaba:main' into main
iaojnh Jul 20, 2026
3ead2c7
fix HnswExternalStreamerEntity bug
iaojnh Jul 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/core/algorithm/hnsw/hnsw_streamer_entity.h
Original file line number Diff line number Diff line change
Expand Up @@ -1154,6 +1154,10 @@ class HnswExternalStreamerEntity : public HnswMmapStreamerEntity {
return vec_src_ ? vec_src_->get_vector(id) : nullptr;
}

ailego_force_inline const void *get_vector_ptr(node_id_t id) const {
return vec_src_ ? vec_src_->get_vector(id) : nullptr;
}

int get_vector(const node_id_t *ids, uint32_t count,
const void **vecs) const override {
if (ailego_unlikely(vec_src_ == nullptr)) {
Expand Down
109 changes: 109 additions & 0 deletions tests/core/interface/index_interface_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <algorithm>
#include <cassert>
#include <cmath>
#include <functional>
#include <iostream>
#include <numeric>
#include <random>
#include <unordered_map>
#include <gtest/gtest.h>
#include "tests/test_util.h"
Expand Down Expand Up @@ -2262,6 +2265,112 @@ TEST(IndexInterface, ExternalVectorInnerProduct) {
index->Close();
zvec::test_util::RemoveTestFiles(index_name + "*");
}

TEST(IndexInterface, ExternalVectorFastSearchRecallRegression) {
constexpr uint32_t kDimension = 64;
constexpr uint32_t kNumVectors = 2000;
constexpr uint32_t kTopk = 20;
const std::string index_name{"test_external_fast_search.index"};

std::mt19937 generator(42);
std::uniform_real_distribution<float> distribution(-1.0f, 1.0f);
std::vector<float> all_vectors(kDimension * kNumVectors);
for (uint32_t i = 0; i < kNumVectors; ++i) {
float *vector = all_vectors.data() + i * kDimension;
float squared_norm = 0.0f;
for (uint32_t d = 0; d < kDimension; ++d) {
vector[d] = distribution(generator);
squared_norm += vector[d] * vector[d];
}
const float norm = std::sqrt(squared_norm);
for (uint32_t d = 0; d < kDimension; ++d) {
vector[d] /= norm;
}
}

std::vector<float> query_vector(kDimension);
float query_squared_norm = 0.0f;
for (float &value : query_vector) {
value = distribution(generator);
query_squared_norm += value * value;
}
const float query_norm = std::sqrt(query_squared_norm);
for (float &value : query_vector) {
value /= query_norm;
}

std::vector<std::pair<float, uint32_t>> exact_results;
exact_results.reserve(kNumVectors);
for (uint32_t i = 0; i < kNumVectors; ++i) {
const float *vector = all_vectors.data() + i * kDimension;
const float score = std::inner_product(
query_vector.begin(), query_vector.end(), vector, 0.0f);
exact_results.emplace_back(score, i);
}
std::sort(exact_results.begin(), exact_results.end(),
[](const auto &lhs, const auto &rhs) {
if (lhs.first != rhs.first) {
return lhs.first > rhs.first;
}
return lhs.second < rhs.second;
});

TestVectorSource source(all_vectors.data(), kDimension);
zvec::test_util::RemoveTestFiles(index_name + "*");

auto param = HNSWIndexParamBuilder()
.WithMetricType(MetricType::kInnerProduct)
.WithDataType(DataType::DT_FP32)
.WithDimension(kDimension)
.WithIsSparse(false)
.WithM(16)
.WithEFConstruction(200)
.WithUseExternalVector(true)
.Build();
auto index = IndexFactory::CreateAndInitIndex(*param);
ASSERT_NE(nullptr, index);
ASSERT_EQ(0,
index->Open(index_name,
{StorageOptions::StorageType::kMMAP, true}));

for (uint32_t i = 0; i < kNumVectors; ++i) {
VectorData vector_data{
DenseVector{all_vectors.data() + i * kDimension}};
ASSERT_EQ(0, index->AddWithSource(vector_data, i, source));
}

auto query_param = HNSWQueryParamBuilder()
.with_topk(kTopk)
.with_fetch_vector(true)
.with_ef_search(500)
.build();
VectorData query{DenseVector{query_vector.data()}};
SearchResult result;
ASSERT_EQ(0, index->SearchWithSource(query, query_param, source, &result));
ASSERT_EQ(kTopk, result.doc_list_.size());

uint32_t recall_count = 0;
for (const auto &doc : result.doc_list_) {
const uint32_t key = doc.key();
ASSERT_LT(key, kNumVectors);
const float *vector = all_vectors.data() + key * kDimension;
const float exact_score = std::inner_product(
query_vector.begin(), query_vector.end(), vector, 0.0f);
EXPECT_NEAR(exact_score, doc.score(), 1e-5f) << "key=" << key;

for (uint32_t i = 0; i < kTopk; ++i) {
if (exact_results[i].second == key) {
++recall_count;
break;
}
}
}
EXPECT_GE(recall_count, 18u);

index->Close();
zvec::test_util::RemoveTestFiles(index_name + "*");
}

TEST(IndexInterface, IsDirty) {
constexpr uint32_t kDimension = 16;
const std::string index_name{"test_is_dirty.index"};
Expand Down
Loading