Repository: antgroup/vsag (language: python)

Issue:
Introduce index_feature for all index
The characteristics of each index are often different. Users often want to know the specific characteristics of the index, such as whether it supports adding data from empty index, whether it supports deletion, whether it supports concurrent search and insertion, etc.

In fact, the internal functions of each index should also be implemented according to the characteristic granularity, which is also convenient for testing and verification, and is more friendly to developers.

Therefore, we provide an enum of all characteristics in the external interface, and at the same time provide interface methods for printing the characteristics of the current index for a specific index

```C++
enum class IndexFeature {
     SUPPORT_ADD_WITHOUT_TRAIN = 1,
     SUPPOET_DELETE_BY_ID = 2,
     ...
}
```
```c++
std::vector<Feature> Index::GetFeatures();
bool Index::CheckFeature(IndexFeature feature);
```


the related pull requests:
#199
#210

todo:
- add feature object 
- introduce it to all kinds of index
- use the feature to check and reconstruct some test




Discussion / hints:
sounds good, will it also available in the pyvsag? 

Fix patch:
diff --git a/include/vsag/index.h b/include/vsag/index.h
index 17d7b294..762ccc8a 100644
--- a/include/vsag/index.h
+++ b/include/vsag/index.h
@@ -22,6 +22,7 @@
 #include <stdexcept>
 
 #include "bitset.h"
+#include "features.h"
 #include "vsag/binaryset.h"
 #include "vsag/bitset.h"
 #include "vsag/dataset.h"
@@ -223,6 +224,11 @@ class Index {
         throw std::runtime_error("Index doesn't support get distance by id");
     };
 
+    virtual tl::expected<bool, Error>
+    CheckFeature(IndexFeature feature) const {
+        throw std::runtime_error("Index doesn't support check feature");
+    }
+
 public:
     // [serialize/deserialize with binaryset]
 
diff --git a/include/vsag/index_feature.h b/include/vsag/index_feature.h
index 3c8212d3..c6b9a019 100644
--- a/include/vsag/index_feature.h
+++ b/include/vsag/index_feature.h
@@ -17,8 +17,8 @@
 
 namespace vsag {
 
-enum class IndexFeature {
-    NEED_TRAIN, /**< Indicates that the class needs to be trained */
+enum IndexFeature {
+    NEED_TRAIN = 1, /**< Indicates that the class needs to be trained */
 
     SUPPORT_BUILD,                     /**< Supports building the index */
     SUPPORT_ADD,                       /**< Supports adding new elements */
@@ -47,12 +47,14 @@ enum class IndexFeature {
     SUPPORT_SEARCH_ONE_WITH_MULTI_THREAD, /**< Supports searching one element with multi-threading */
     SUPPORT_BATCH_SEARCH_WITH_MULTI_THREAD, /**< Supports batch searching with multi-threading */
 
-    SUPPORT_ADD_CONCURRENT,              /**< Supports concurrent addition of elements */
-    SUPPORT_SEARCH_CONCURRENT,           /**< Supports concurrent searching */
-    SUPPORT_DELETE_CONCURRENT,           /**< Supports concurrent deletion */
-    SUPPORT_ADD_SEARCH_CONCURRENT,       /**< Supports concurrent addition and searching */
-    SUPPORT_ADD_DELETE_CONCURRENT,       /**< Supports concurrent addition and deletion */
-    SUPPORT_SEARCH_DELETE_CONCURRENT,    /**< Supports concurrent searching and deletion */
-    SUPPORT_ADD_SEARCH_DELETE_CONCURRENT /**< Supports concurrent addition, searching, and deletion */
+    SUPPORT_ADD_CONCURRENT,               /**< Supports concurrent addition of elements */
+    SUPPORT_SEARCH_CONCURRENT,            /**< Supports concurrent searching */
+    SUPPORT_DELETE_CONCURRENT,            /**< Supports concurrent deletion */
+    SUPPORT_ADD_SEARCH_CONCURRENT,        /**< Supports concurrent addition and searching */
+    SUPPORT_ADD_DELETE_CONCURRENT,        /**< Supports concurrent addition and deletion */
+    SUPPORT_SEARCH_DELETE_CONCURRENT,     /**< Supports concurrent searching and deletion */
+    SUPPORT_ADD_SEARCH_DELETE_CONCURRENT, /**< Supports concurrent addition, searching, and deletion */
+
+    INDEX_FEATURE_COUNT /** must be last one */
 };
 }  // namespace vsag
diff --git a/src/index_feature_list.cpp b/src/index_feature_list.cpp
new file mode 100644
index 00000000..7ab3e6ad
--- /dev/null
+++ b/src/index_feature_list.cpp
@@ -0,0 +1,57 @@
+
+// Copyright 2024-present the vsag project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// 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 "index_feature_list.h"
+
+#include <stdexcept>
+
+namespace vsag {
+
+static std::pair<uint32_t, uint32_t>
+get_pos(const uint32_t val) {
+    return {val / 8, val % 8};
+}
+
+IndexFeatureList::IndexFeatureList()
+    : feature_count_(static_cast<uint32_t>(IndexFeature::INDEX_FEATURE_COUNT)) {
+    uint32_t size = (feature_count_ + 7) / 8;
+    data_.resize(size);
+}
+
+bool
+IndexFeatureList::CheckFeature(IndexFeature feature) {
+    if (static_cast<uint32_t>(feature) >= feature_count_) {
+        throw std::runtime_error("wrong feature");
+    } else {
+        auto [x, y] = get_pos(static_cast<uint32_t>(feature));
+        return data_[x] & (1U << y);
+    }
+}
+
+void
+IndexFeatureList::SetFeature(vsag::IndexFeature feature, bool val) {
+    if (static_cast<uint32_t>(feature) >= feature_count_) {
+        throw std::runtime_error("wrong feature");
+    } else {
+        auto [x, y] = get_pos(static_cast<uint32_t>(feature));
+        if (val == true) {
+            data_[x] |= (1U << y);
+        } else {
+            data_[x] &= ~(1U << y);
+        }
+    }
+}
+
+}  // namespace vsag
diff --git a/src/index_feature_list.h b/src/index_feature_list.h
new file mode 100644
index 00000000..4e2747b8
--- /dev/null
+++ b/src/index_feature_list.h
@@ -0,0 +1,39 @@
+
+// Copyright 2024-present the vsag project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// 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.
+
+#pragma once
+
+#include <cstdint>
+#include <vector>
+
+#include "vsag/index_feature.h"
+
+namespace vsag {
+class IndexFeatureList {
+public:
+    explicit IndexFeatureList();
+
+    bool
+    CheckFeature(IndexFeature feature);
+
+    void
+    SetFeature(IndexFeature feature, bool val = true);
+
+private:
+    std::vector<uint8_t> data_{};
+
+    const uint32_t feature_count_{0};
+};
+}  // namespace vsag


Test patch:
diff --git a/src/index_feature_list_test.cpp b/src/index_feature_list_test.cpp
new file mode 100644
index 00000000..164e67ad
--- /dev/null
+++ b/src/index_feature_list_test.cpp
@@ -0,0 +1,52 @@
+
+// Copyright 2024-present the vsag project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// 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 "index_feature_list.h"
+
+#include "catch2/catch_test_macros.hpp"
+
+TEST_CASE("Test set&check", "[ut][index_feature_list]") {
+    using namespace vsag;
+    IndexFeatureList list;
+    auto count = static_cast<uint32_t>(IndexFeature::INDEX_FEATURE_COUNT);
+    for (uint32_t i = 1; i < count; ++i) {
+        REQUIRE(list.CheckFeature(static_cast<IndexFeature>(i)) == false);
+        list.SetFeature(static_cast<IndexFeature>(i));
+        REQUIRE(list.CheckFeature(static_cast<IndexFeature>(i)) == true);
+        list.SetFeature(static_cast<IndexFeature>(i), false);
+        REQUIRE(list.CheckFeature(static_cast<IndexFeature>(i)) == false);
+    }
+
+    std::vector<int> status(count, 0);
+
+    auto func_set = [&]() {
+        int64_t times = random() % 100;
+        while (times > 0) {
+            bool val = random() % 2;
+            int64_t key = random() % (count - 1) + 1;
+            list.SetFeature(static_cast<IndexFeature>(key), val);
+            status[key] = val ? 1 : 0;
+            --times;
+        }
+    };
+
+    for (uint32_t i = 0; i < 10; ++i) {
+        func_set();
+        for (uint32_t j = 1; j < count; ++j) {
+            bool gt = (status[j] != 0);
+            REQUIRE(gt == list.CheckFeature(static_cast<IndexFeature>(j)));
+        }
+    }
+}