aboutsummaryrefslogtreecommitdiffstats
path: root/searchlib
diff options
context:
space:
mode:
Diffstat (limited to 'searchlib')
-rw-r--r--searchlib/src/tests/features/prod_features.cpp20
-rw-r--r--searchlib/src/tests/tensor/hnsw_saver/hnsw_save_load_test.cpp100
-rw-r--r--searchlib/src/vespa/searchlib/features/attributefeature.cpp6
-rw-r--r--searchlib/src/vespa/searchlib/features/utils.h8
-rw-r--r--searchlib/src/vespa/searchlib/fef/indexproperties.cpp8
-rw-r--r--searchlib/src/vespa/searchlib/fef/indexproperties.h1
-rw-r--r--searchlib/src/vespa/searchlib/fef/query_value.cpp18
-rw-r--r--searchlib/src/vespa/searchlib/fef/query_value.h2
-rw-r--r--searchlib/src/vespa/searchlib/tensor/hnsw_graph.h1
-rw-r--r--searchlib/src/vespa/searchlib/tensor/hnsw_index_loader.hpp5
-rw-r--r--searchlib/src/vespa/searchlib/tensor/hnsw_index_saver.cpp20
-rw-r--r--searchlib/src/vespa/searchlib/tensor/hnsw_index_saver.h3
-rw-r--r--searchlib/src/vespa/searchlib/tensor/hnsw_index_saver_metadata_node.h68
13 files changed, 205 insertions, 55 deletions
diff --git a/searchlib/src/tests/features/prod_features.cpp b/searchlib/src/tests/features/prod_features.cpp
index 80650050b25..73b66f36172 100644
--- a/searchlib/src/tests/features/prod_features.cpp
+++ b/searchlib/src/tests/features/prod_features.cpp
@@ -251,13 +251,13 @@ Test::testAttribute()
addScore("attribute(sebool)", 0).
addScore("attribute(sfloat)", 60.5f).
addScore("attribute(sdouble)", 67.5f).
- addScore("attribute(sstr)", (feature_t)vespalib::hash_code("foo")).
+ addScore("attribute(sstr)", vespalib::hash2d("foo")).
addScore("attribute(sint).count", 1).
addScore("attribute(sfloat).count", 1).
addScore("attribute(sstr).count", 1).
addScore("attribute(udefint)", search::attribute::getUndefined<feature_t>()).
addScore("attribute(udeffloat)", search::attribute::getUndefined<feature_t>()).
- addScore("attribute(udefstr)", (feature_t)vespalib::hash_code(""));
+ addScore("attribute(udefstr)", vespalib::hash2d(""));
FtFeatureTest ft(_factory, exp.getKeys());
ft.getIndexEnv().getBuilder()
@@ -284,8 +284,8 @@ Test::testAttribute()
addScore("attribute(aint,2)", 0).
addScore("attribute(afloat,0)", 70.5f).
addScore("attribute(afloat,1)", 80.5f).
- addScore("attribute(astr,0)", (feature_t)vespalib::hash_code("bar")).
- addScore("attribute(astr,1)", (feature_t)vespalib::hash_code("baz")).
+ addScore("attribute(astr,0)", vespalib::hash2d("bar")).
+ addScore("attribute(astr,1)", vespalib::hash2d("baz")).
addScore("attribute(aint).count", 2).
addScore("attribute(aint,0).count", 0).
addScore("attribute(afloat).count", 2).
@@ -333,10 +333,10 @@ Test::testAttribute()
addScore("attribute(wsstr,foo).value", 0).
addScore("attribute(wsstr,foo).weight", 0).
addScore("attribute(wsstr,foo).contains", 0).
- addScore("attribute(wsstr,qux).value", (feature_t)vespalib::hash_code("qux")).
+ addScore("attribute(wsstr,qux).value", vespalib::hash2d("qux")).
addScore("attribute(wsstr,qux).weight", 11).
addScore("attribute(wsstr,qux).contains", 1).
- addScore("attribute(wsstr,quux).value", (feature_t)vespalib::hash_code("quux")).
+ addScore("attribute(wsstr,quux).value", vespalib::hash2d("quux")).
addScore("attribute(wsstr,quux).weight", 12).
addScore("attribute(wsstr,quux).contains", 1).
addScore("attribute(wsint).count", 2).
@@ -1762,10 +1762,10 @@ Test::testQuery()
addScore("query(def3)", 0.0).
addScore("query(val1)", 1.1).
addScore("query(val2)", 2.2).
- addScore("query(hash1)", vespalib::hash_code("foo")).
- addScore("query(hash2)", vespalib::hash_code("2")).
- addScore("query(hash3)", vespalib::hash_code("foo")).
- addScore("query(hash4)", vespalib::hash_code("'foo"));
+ addScore("query(hash1)", vespalib::hash2d("foo")).
+ addScore("query(hash2)", vespalib::hash2d("2")).
+ addScore("query(hash3)", vespalib::hash2d("foo")).
+ addScore("query(hash4)", vespalib::hash2d("'foo"));
FtFeatureTest ft(_factory, exp.getKeys());
ft.getIndexEnv().getProperties()
.add("query(def1)", "1.0")
diff --git a/searchlib/src/tests/tensor/hnsw_saver/hnsw_save_load_test.cpp b/searchlib/src/tests/tensor/hnsw_saver/hnsw_save_load_test.cpp
index d7666700e77..e2a96ec059c 100644
--- a/searchlib/src/tests/tensor/hnsw_saver/hnsw_save_load_test.cpp
+++ b/searchlib/src/tests/tensor/hnsw_saver/hnsw_save_load_test.cpp
@@ -51,14 +51,59 @@ public:
using V = std::vector<uint32_t>;
template <HnswIndexType type>
+uint32_t fake_docid(uint32_t nodeid);
+
+template <>
+uint32_t fake_docid<HnswIndexType::SINGLE>(uint32_t nodeid)
+{
+ return nodeid;
+}
+
+template <>
+uint32_t fake_docid<HnswIndexType::MULTI>(uint32_t nodeid)
+{
+ return nodeid + 100;
+}
+
+template <HnswIndexType type>
+uint32_t fake_subspace(uint32_t nodeid);
+
+template <>
+uint32_t fake_subspace<HnswIndexType::SINGLE>(uint32_t)
+{
+ return 0;
+}
+
+template <>
+uint32_t fake_subspace<HnswIndexType::MULTI>(uint32_t nodeid)
+{
+ return nodeid + 10;
+}
+
+template <typename NodeType>
+uint32_t fake_get_docid(const NodeType& node, uint32_t nodeid);
+
+template <>
+uint32_t fake_get_docid<HnswSimpleNode>(const HnswSimpleNode &, uint32_t nodeid)
+{
+ return fake_docid<HnswIndexType::SINGLE>(nodeid);
+}
+
+template <>
+uint32_t fake_get_docid<HnswNode>(const HnswNode& node, uint32_t)
+{
+ return node.acquire_docid();
+}
+
+template <HnswIndexType type>
void populate(HnswGraph<type> &graph) {
// no 0
- graph.make_node(1, 1, 0, 1);
- auto er = graph.make_node(2, 2, 0, 2);
+ graph.make_node(1, fake_docid<type>(1), fake_subspace<type>(1), 1);
+ auto er = graph.make_node(2, 102, 12, 2);
// no 3
- graph.make_node(4, 4, 0, 2);
- graph.make_node(5, 5, 0, 0);
- graph.make_node(6, 6, 0, 1);
+ graph.make_node(4, fake_docid<type>(4), fake_subspace<type>(4), 2);
+ graph.make_node(5, fake_docid<type>(5), fake_subspace<type>(5), 0);
+ graph.make_node(6, fake_docid<type>(6), fake_subspace<type>(6), 1);
graph.set_link_array(1, 0, V{2, 4, 6});
graph.set_link_array(2, 0, V{1, 4, 6});
@@ -73,7 +118,7 @@ template <HnswIndexType type>
void modify(HnswGraph<type> &graph) {
graph.remove_node(2);
graph.remove_node(6);
- graph.make_node(7, 7, 0, 2);
+ graph.make_node(7, fake_docid<type>(7), fake_subspace<type>(7), 2);
graph.set_link_array(1, 0, V{7, 4});
graph.set_link_array(4, 0, V{7, 2});
@@ -85,10 +130,11 @@ void modify(HnswGraph<type> &graph) {
}
+template <typename GraphType>
class CopyGraphTest : public ::testing::Test {
public:
- HnswGraph<HnswIndexType::SINGLE> original;
- HnswGraph<HnswIndexType::SINGLE> copy;
+ GraphType original;
+ GraphType copy;
void expect_empty_d(uint32_t nodeid) const {
EXPECT_FALSE(copy.acquire_node_ref(nodeid).valid());
@@ -121,10 +167,16 @@ public:
return vector_writer.output;
}
void load_copy(std::vector<char> data) {
- HnswIndexLoader<VectorBufferReader, HnswIndexType::SINGLE> loader(copy, std::make_unique<VectorBufferReader>(data));
+ HnswIndexLoader<VectorBufferReader, GraphType::index_type> loader(copy, std::make_unique<VectorBufferReader>(data));
while (loader.load_next()) {}
}
+ void expect_docid_and_subspace(uint32_t nodeid) const {
+ auto& node = copy.node_refs.get_elem_ref(nodeid);
+ EXPECT_EQ(fake_docid<GraphType::index_type>(nodeid), fake_get_docid(node, nodeid));
+ EXPECT_EQ(fake_subspace<GraphType::index_type>(nodeid), node.acquire_subspace());
+ }
+
void expect_copy_as_populated() const {
EXPECT_EQ(copy.size(), 7);
auto entry = copy.get_entry_node();
@@ -142,27 +194,35 @@ public:
expect_level_1(2, {4});
expect_level_1(4, {2});
+ expect_docid_and_subspace(1);
+ expect_docid_and_subspace(2);
+ expect_docid_and_subspace(4);
+ expect_docid_and_subspace(6);
}
};
-TEST_F(CopyGraphTest, reconstructs_graph)
+using GraphTestTypes = ::testing::Types<HnswGraph<HnswIndexType::SINGLE>, HnswGraph<HnswIndexType::MULTI>>;
+
+TYPED_TEST_SUITE(CopyGraphTest, GraphTestTypes);
+
+TYPED_TEST(CopyGraphTest, reconstructs_graph)
{
- populate(original);
- auto data = save_original();
- load_copy(data);
- expect_copy_as_populated();
+ populate(this->original);
+ auto data = this->save_original();
+ this->load_copy(data);
+ this->expect_copy_as_populated();
}
-TEST_F(CopyGraphTest, later_changes_ignored)
+TYPED_TEST(CopyGraphTest, later_changes_ignored)
{
- populate(original);
- HnswIndexSaver saver(original);
- modify(original);
+ populate(this->original);
+ HnswIndexSaver saver(this->original);
+ modify(this->original);
VectorBufferWriter vector_writer;
saver.save(vector_writer);
auto data = vector_writer.output;
- load_copy(data);
- expect_copy_as_populated();
+ this->load_copy(data);
+ this->expect_copy_as_populated();
}
GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/searchlib/src/vespa/searchlib/features/attributefeature.cpp b/searchlib/src/vespa/searchlib/features/attributefeature.cpp
index 8afb5ec1f21..dae1ccc994b 100644
--- a/searchlib/src/vespa/searchlib/features/attributefeature.cpp
+++ b/searchlib/src/vespa/searchlib/features/attributefeature.cpp
@@ -113,7 +113,7 @@ public:
*
* @param attribute The attribute vector to use.
*/
- SingleAttributeExecutor(const T & attribute) : _attribute(attribute) { }
+ explicit SingleAttributeExecutor(const T & attribute) : _attribute(attribute) { }
void handle_bind_outputs(vespalib::ArrayRef<fef::NumberOrObject> outputs_in) override {
fef::FeatureExecutor::handle_bind_outputs(outputs_in);
auto o = outputs().get_bound();
@@ -128,7 +128,7 @@ class BoolAttributeExecutor final : public fef::FeatureExecutor {
private:
const SingleBoolAttribute & _attribute;
public:
- BoolAttributeExecutor(const SingleBoolAttribute & attribute)
+ explicit BoolAttributeExecutor(const SingleBoolAttribute & attribute)
: _attribute(attribute)
{}
void execute(uint32_t docId) override {
@@ -162,7 +162,7 @@ private:
const attribute::IAttributeVector & _attribute;
public:
- CountOnlyAttributeExecutor(const attribute::IAttributeVector & attribute) : _attribute(attribute) { }
+ explicit CountOnlyAttributeExecutor(const attribute::IAttributeVector & attribute) : _attribute(attribute) { }
void execute(uint32_t docId) override;
void handle_bind_outputs(vespalib::ArrayRef<fef::NumberOrObject> outputs_in) override {
fef::FeatureExecutor::handle_bind_outputs(outputs_in);
diff --git a/searchlib/src/vespa/searchlib/features/utils.h b/searchlib/src/vespa/searchlib/features/utils.h
index eeecd55c6da..9a8ddacb99f 100644
--- a/searchlib/src/vespa/searchlib/features/utils.h
+++ b/searchlib/src/vespa/searchlib/features/utils.h
@@ -57,7 +57,7 @@ inline feature_t getAsFeature(T value)
*/
template <>
inline feature_t getAsFeature<ConstCharPtr>(ConstCharPtr value) {
- return static_cast<feature_t>(vespalib::hash_code(value, strlen(value)));
+ return vespalib::hash2d(value, strlen(value));
}
/**
@@ -68,7 +68,7 @@ inline feature_t getAsFeature<ConstCharPtr>(ConstCharPtr value) {
*/
template <>
inline feature_t getAsFeature<vespalib::stringref>(vespalib::stringref value) {
- return static_cast<feature_t>(vespalib::hash_code(value));
+ return vespalib::hash2d(value);
}
@@ -184,7 +184,7 @@ lookupTable(const search::fef::IIndexEnvironment & env, const vespalib::string &
inline const search::fef::ITermFieldData *
getTermFieldData(const search::fef::IQueryEnvironment &env, uint32_t termId, uint32_t fieldId) {
const search::fef::ITermData *td = env.getTerm(termId);
- return (td == 0) ? 0 : td->lookupField(fieldId);
+ return (td == nullptr) ? nullptr : td->lookupField(fieldId);
}
/**
@@ -198,7 +198,7 @@ getTermFieldData(const search::fef::IQueryEnvironment &env, uint32_t termId, uin
inline search::fef::TermFieldHandle
getTermFieldHandle(const search::fef::IQueryEnvironment &env, uint32_t termId, uint32_t fieldId) {
const search::fef::ITermFieldData *tfd = getTermFieldData(env, termId, fieldId);
- return (tfd == 0) ? search::fef::IllegalHandle : tfd->getHandle();
+ return (tfd == nullptr) ? search::fef::IllegalHandle : tfd->getHandle();
}
/**
diff --git a/searchlib/src/vespa/searchlib/fef/indexproperties.cpp b/searchlib/src/vespa/searchlib/fef/indexproperties.cpp
index 70433800469..26a7be005b7 100644
--- a/searchlib/src/vespa/searchlib/fef/indexproperties.cpp
+++ b/searchlib/src/vespa/searchlib/fef/indexproperties.cpp
@@ -587,7 +587,13 @@ const uint32_t ArraySize::DEFAULT_VALUE(10000);
uint32_t
ArraySize::lookup(const Properties &props)
{
- return lookupUint32(props, NAME, DEFAULT_VALUE);
+ return lookup(props, DEFAULT_VALUE);
+}
+
+uint32_t
+ArraySize::lookup(const Properties &props, uint32_t defaultValue)
+{
+ return lookupUint32(props, NAME, defaultValue);
}
const vespalib::string EstimatePoint::NAME("vespa.hitcollector.estimatepoint");
diff --git a/searchlib/src/vespa/searchlib/fef/indexproperties.h b/searchlib/src/vespa/searchlib/fef/indexproperties.h
index 3ebfe781400..5ff4ea26bd8 100644
--- a/searchlib/src/vespa/searchlib/fef/indexproperties.h
+++ b/searchlib/src/vespa/searchlib/fef/indexproperties.h
@@ -476,6 +476,7 @@ namespace hitcollector {
static const vespalib::string NAME;
static const uint32_t DEFAULT_VALUE;
static uint32_t lookup(const Properties &props);
+ static uint32_t lookup(const Properties &props, uint32_t defaultValue);
};
/**
diff --git a/searchlib/src/vespa/searchlib/fef/query_value.cpp b/searchlib/src/vespa/searchlib/fef/query_value.cpp
index 8b881bc722f..ea17743bfc7 100644
--- a/searchlib/src/vespa/searchlib/fef/query_value.cpp
+++ b/searchlib/src/vespa/searchlib/fef/query_value.cpp
@@ -4,6 +4,7 @@
#include "iindexenvironment.h"
#include "indexproperties.h"
#include "iqueryenvironment.h"
+#include <vespa/searchlib/features/utils.h>
#include <vespa/document/datatype/tensor_data_type.h>
#include <vespa/eval/eval/fast_value.h>
#include <vespa/eval/eval/interpreted_function.h>
@@ -11,7 +12,6 @@
#include <vespa/eval/eval/value_codec.h>
#include <vespa/vespalib/locale/c.h>
#include <vespa/vespalib/util/issue.h>
-#include <vespa/vespalib/util/string_hash.h>
#include <cerrno>
using document::TensorDataType;
@@ -59,10 +59,10 @@ as_feature(const vespalib::string& str)
errno = 0;
double val = vespalib::locale::c::strtod(str.c_str(), &end);
if (errno != 0 || *end != '\0') { // not happy
- if (str.size() > 0 && str[0] == '\'') {
- val = vespalib::hash_code(str.substr(1));
+ if ( ! str.empty() && str[0] == '\'') {
+ val = features::util::getAsFeature(vespalib::stringref(str.substr(1)));
} else {
- val = vespalib::hash_code(str);
+ val = features::util::getAsFeature(vespalib::stringref(str));
}
}
return val;
@@ -84,7 +84,7 @@ as_tensor(const vespalib::string& expr, const ValueType& wanted_type)
auto fun = Function::parse(expr);
if (!fun->has_error() && (fun->num_params() == 0)) {
NodeTypes types = NodeTypes(*fun, {});
- ValueType res_type = types.get_type(fun->root());
+ const ValueType & res_type = types.get_type(fun->root());
if (res_type == wanted_type) {
SimpleObjectParams params({});
InterpretedFunction ifun(factory, *fun, types);
@@ -152,12 +152,12 @@ QueryValue::QueryValue()
{
}
-QueryValue::QueryValue(const vespalib::string& key, const vespalib::eval::ValueType& type)
+QueryValue::QueryValue(const vespalib::string& key, vespalib::eval::ValueType type)
: _key(key),
_name("query(" + key + ")"),
_old_key("$" + key),
_stored_value_key("query.value." + key),
- _type(type)
+ _type(std::move(type))
{
}
@@ -171,7 +171,7 @@ QueryValue::from_config(const vespalib::string& key, const IIndexEnvironment& en
if (type.is_error()) {
throw InvalidValueTypeException(key, type_str);
}
- return {key, type};
+ return {key, std::move(type)};
}
std::unique_ptr<Value>
@@ -187,7 +187,7 @@ QueryValue::make_default_value(const IIndexEnvironment& env) const
} else {
if (p.found()) {
auto tensor = as_tensor(p.get(), _type);
- if (tensor.get() == nullptr) {
+ if ( ! tensor) {
throw InvalidTensorValueException(_type, p.get().c_str());
}
return tensor;
diff --git a/searchlib/src/vespa/searchlib/fef/query_value.h b/searchlib/src/vespa/searchlib/fef/query_value.h
index fb014883268..17042d662c3 100644
--- a/searchlib/src/vespa/searchlib/fef/query_value.h
+++ b/searchlib/src/vespa/searchlib/fef/query_value.h
@@ -59,7 +59,7 @@ private:
public:
QueryValue();
- QueryValue(const vespalib::string& key, const vespalib::eval::ValueType& type);
+ QueryValue(const vespalib::string& key, vespalib::eval::ValueType type);
~QueryValue();
/**
diff --git a/searchlib/src/vespa/searchlib/tensor/hnsw_graph.h b/searchlib/src/vespa/searchlib/tensor/hnsw_graph.h
index 5b448ea27b7..4706f9cf7e8 100644
--- a/searchlib/src/vespa/searchlib/tensor/hnsw_graph.h
+++ b/searchlib/src/vespa/searchlib/tensor/hnsw_graph.h
@@ -27,6 +27,7 @@ struct HnswGraph {
// This uses 12 bits for buffer id -> 4096 buffers.
using LinkArrayEntryRefType = vespalib::datastore::EntryRefT<20>;
+ static constexpr HnswIndexType index_type = type;
using NodeType = typename HnswIndexTraits<type>::NodeType;
// Provides mapping from document id -> node reference.
diff --git a/searchlib/src/vespa/searchlib/tensor/hnsw_index_loader.hpp b/searchlib/src/vespa/searchlib/tensor/hnsw_index_loader.hpp
index 1bf02edbefa..04e1fcc1792 100644
--- a/searchlib/src/vespa/searchlib/tensor/hnsw_index_loader.hpp
+++ b/searchlib/src/vespa/searchlib/tensor/hnsw_index_loader.hpp
@@ -40,10 +40,13 @@ bool
HnswIndexLoader<ReaderType, type>::load_next()
{
assert(!_complete);
+ static constexpr bool identity_mapping = (type == HnswIndexType::SINGLE);
if (_nodeid < _num_nodes) {
uint32_t num_levels = next_int();
if (num_levels > 0) {
- _graph.make_node(_nodeid, _nodeid, 0, num_levels);
+ uint32_t docid = identity_mapping ? _nodeid : next_int();
+ uint32_t subspace = identity_mapping ? 0 : next_int();
+ _graph.make_node(_nodeid, docid, subspace, num_levels);
for (uint32_t level = 0; level < num_levels; ++level) {
uint32_t num_links = next_int();
_link_array.clear();
diff --git a/searchlib/src/vespa/searchlib/tensor/hnsw_index_saver.cpp b/searchlib/src/vespa/searchlib/tensor/hnsw_index_saver.cpp
index ef6621d910b..370e2ddf92a 100644
--- a/searchlib/src/vespa/searchlib/tensor/hnsw_index_saver.cpp
+++ b/searchlib/src/vespa/searchlib/tensor/hnsw_index_saver.cpp
@@ -54,8 +54,9 @@ HnswIndexSaver<type>::HnswIndexSaver(const HnswGraph<type> &graph)
_meta_data.refs.reserve(link_array_count);
_meta_data.nodes.reserve(num_nodes+1);
for (size_t i = 0; i < num_nodes; ++i) {
- _meta_data.nodes.push_back(_meta_data.refs.size());
- auto node_ref = graph.get_node_ref(i);
+ auto& node = graph.node_refs.get_elem_ref(i);
+ _meta_data.nodes.emplace_back(_meta_data.refs.size(), node);
+ auto node_ref = node.ref().load_relaxed();
if (node_ref.valid()) {
auto levels = graph.nodes.get(node_ref);
for (const auto& links_ref : levels) {
@@ -63,7 +64,7 @@ HnswIndexSaver<type>::HnswIndexSaver(const HnswGraph<type> &graph)
}
}
}
- _meta_data.nodes.push_back(_meta_data.refs.size());
+ _meta_data.nodes.emplace_back(_meta_data.refs.size());
}
template <HnswIndexType type>
@@ -75,10 +76,19 @@ HnswIndexSaver<type>::save(BufferWriter& writer) const
uint32_t num_nodes = _meta_data.nodes.size() - 1;
writer.write(&num_nodes, sizeof(uint32_t));
for (uint32_t i(0); i < num_nodes; i++) {
- uint32_t offset = _meta_data.nodes[i];
- uint32_t next_offset = _meta_data.nodes[i+1];
+ auto& node = _meta_data.nodes[i];
+ uint32_t offset = node.get_refs_offset();
+ uint32_t next_offset = _meta_data.nodes[i+1].get_refs_offset();
uint32_t num_levels = next_offset - offset;
writer.write(&num_levels, sizeof(uint32_t));
+ if (num_levels > 0) {
+ if constexpr (!HnswIndexSaverMetaDataNode<type>::identity_mapping) {
+ uint32_t docid = node.get_docid();
+ uint32_t subspace = node.get_subspace();
+ writer.write(&docid, sizeof(uint32_t));
+ writer.write(&subspace, sizeof(uint32_t));
+ }
+ }
for (; offset < next_offset; offset++) {
auto links_ref = _meta_data.refs[offset];
if (links_ref.valid()) {
diff --git a/searchlib/src/vespa/searchlib/tensor/hnsw_index_saver.h b/searchlib/src/vespa/searchlib/tensor/hnsw_index_saver.h
index fa1ae6a5ed6..2884ee9b494 100644
--- a/searchlib/src/vespa/searchlib/tensor/hnsw_index_saver.h
+++ b/searchlib/src/vespa/searchlib/tensor/hnsw_index_saver.h
@@ -4,6 +4,7 @@
#include "nearest_neighbor_index_saver.h"
#include "hnsw_graph.h"
+#include "hnsw_index_saver_metadata_node.h"
#include <vespa/vespalib/datastore/entryref.h>
#include <vespa/vespalib/stllike/allocator.h>
#include <vector>
@@ -29,7 +30,7 @@ private:
uint32_t entry_nodeid;
int32_t entry_level;
std::vector<EntryRef, vespalib::allocator_large<EntryRef>> refs;
- std::vector<uint32_t, vespalib::allocator_large<uint32_t>> nodes;
+ std::vector<HnswIndexSaverMetaDataNode<type>, vespalib::allocator_large<HnswIndexSaverMetaDataNode<type>>> nodes;
MetaData();
~MetaData();
};
diff --git a/searchlib/src/vespa/searchlib/tensor/hnsw_index_saver_metadata_node.h b/searchlib/src/vespa/searchlib/tensor/hnsw_index_saver_metadata_node.h
new file mode 100644
index 00000000000..bf7fcbc06ed
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/tensor/hnsw_index_saver_metadata_node.h
@@ -0,0 +1,68 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "hnsw_index_type.h"
+#include "hnsw_node.h"
+#include "hnsw_simple_node.h"
+
+namespace search::tensor {
+
+/*
+ * Meta data for node during save of hnsw graph.
+ */
+template <HnswIndexType type>
+class HnswIndexSaverMetaDataNode;
+
+/*
+ * Meta data for node during save of hnsw graph with one node per document and
+ * identity mapping between nodeid and docid.
+ */
+template <>
+class HnswIndexSaverMetaDataNode<HnswIndexType::SINGLE>
+{
+ uint32_t _refs_offset;
+
+public:
+ HnswIndexSaverMetaDataNode(uint32_t refs_offset) noexcept
+ : _refs_offset(refs_offset)
+ {
+ }
+ HnswIndexSaverMetaDataNode(uint32_t refs_offset, const HnswSimpleNode&) noexcept
+ : _refs_offset(refs_offset)
+ {
+ }
+ uint32_t get_refs_offset() const noexcept { return _refs_offset; }
+ static constexpr bool identity_mapping = true;
+};
+
+/*
+ * Meta data for node during save of hnsw graph with multiple nodes per document and
+ * managed mapping between nodeid and docid.
+ */
+template <>
+class HnswIndexSaverMetaDataNode<HnswIndexType::MULTI>
+{
+ uint32_t _refs_offset;
+ uint32_t _docid;
+ uint32_t _subspace;
+public:
+ HnswIndexSaverMetaDataNode(uint32_t refs_offset) noexcept
+ : _refs_offset(refs_offset),
+ _docid(0),
+ _subspace(0)
+ {
+ }
+ HnswIndexSaverMetaDataNode(uint32_t refs_offset, const HnswNode& node) noexcept
+ : _refs_offset(refs_offset),
+ _docid(node.acquire_docid()),
+ _subspace(node.acquire_subspace())
+ {
+ }
+ uint32_t get_refs_offset() const noexcept { return _refs_offset; }
+ uint32_t get_docid() const noexcept { return _docid; }
+ uint32_t get_subspace() const noexcept { return _subspace; }
+ static constexpr bool identity_mapping = false;
+};
+
+}