@@ -727,6 +727,7 @@ HGraph::build_by_odescent(const DatasetPtr& data) {
727727
728728std::vector<int64_t >
729729HGraph::Add (const DatasetPtr& data, AddMode mode) {
730+ std::shared_lock force_remove_rlock (this ->force_remove_mutex_ );
730731 std::vector<int64_t > failed_ids;
731732 auto base_dim = data->GetDim ();
732733 if (data_type_ != DataTypes::DATA_TYPE_SPARSE) {
@@ -867,6 +868,7 @@ HGraph::KnnSearch(const DatasetPtr& query,
867868 (1 <= params.ef_search ) and (params.ef_search <= ef_search_threshold),
868869 fmt::format (" ef_search({}) must in range[1, {}]" , params.ef_search , ef_search_threshold));
869870
871+ std::shared_lock force_remove_rlock (this ->force_remove_mutex_ );
870872 std::shared_lock shared_lock (this ->global_mutex_ );
871873 // check k
872874 CHECK_ARGUMENT (k > 0 , fmt::format (" k({}) must be greater than 0" , k));
@@ -1125,6 +1127,7 @@ HGraph::RangeSearch(const DatasetPtr& query,
11251127 CHECK_ARGUMENT (limited_size != 0 ,
11261128 fmt::format (" limited_size({}) must not be equal to 0" , limited_size));
11271129
1130+ std::shared_lock force_remove_rlock (this ->force_remove_mutex_ );
11281131 std::shared_lock shared_lock (this ->global_mutex_ );
11291132
11301133 InnerSearchParam search_param;
@@ -1912,46 +1915,168 @@ HGraph::Remove(const std::vector<int64_t>& ids, RemoveMode mode) {
19121915 delete_count_ += delete_count;
19131916 return delete_count;
19141917 }
1915- for (const auto & id : ids) {
1916- InnerIdType inner_id;
1917- {
1918- std::shared_lock lock (this ->label_lookup_mutex_ );
1919- inner_id = this ->label_table_ ->GetIdByLabel (id);
1920- }
1921- if (inner_id == this ->entry_point_id_ ) {
1922- bool find_new_ep = false ;
1923- while (not route_graphs_.empty ()) {
1924- auto & upper_graph = route_graphs_.back ();
1925- Vector<InnerIdType> neighbors (allocator_);
1926- upper_graph->GetNeighbors (this ->entry_point_id_ , neighbors);
1927- for (const auto & nb_id : neighbors) {
1928- if (inner_id == nb_id) {
1929- continue ;
1930- }
1931- this ->entry_point_id_ = nb_id;
1932- find_new_ep = true ;
1933- break ;
1934- }
1935- if (find_new_ep) {
1936- break ;
1937- }
1938- route_graphs_.pop_back ();
1918+
1919+ if (mode == RemoveMode::FORCE_REMOVE) {
1920+ std::unique_lock<std::shared_mutex> wlock (this ->force_remove_mutex_ );
1921+ for (const auto & id : ids) {
1922+ delete_count += this ->force_remove_one (id);
1923+ }
1924+ if (delete_count != 0 ) {
1925+ this ->shrink_to_fit ();
1926+ }
1927+ return delete_count;
1928+ }
1929+
1930+ throw VsagException (ErrorType::INVALID_ARGUMENT, " RemoveMode not supported" );
1931+ }
1932+
1933+ void
1934+ HGraph::find_new_entry_point () {
1935+ bool find_new_ep = false ;
1936+ auto inner_id = this ->entry_point_id_ ;
1937+ while (not route_graphs_.empty ()) {
1938+ auto & upper_graph = route_graphs_.back ();
1939+ Vector<InnerIdType> neighbors (allocator_);
1940+ upper_graph->GetNeighbors (this ->entry_point_id_ , neighbors);
1941+ for (const auto & nb_id : neighbors) {
1942+ if (inner_id == nb_id) {
1943+ continue ;
19391944 }
1945+ this ->entry_point_id_ = nb_id;
1946+ find_new_ep = true ;
1947+ break ;
19401948 }
1941- {
1942- {
1943- std::scoped_lock<std::shared_mutex> wlock (this ->global_mutex_ );
1944- for (int level = static_cast <int >(route_graphs_.size ()) - 1 ; level >= 0 ; --level) {
1945- this ->route_graphs_ [level]->DeleteNeighborsById (inner_id);
1946- }
1947- this ->bottom_graph_ ->DeleteNeighborsById (inner_id);
1949+ if (find_new_ep) {
1950+ break ;
1951+ }
1952+ route_graphs_.pop_back ();
1953+ }
1954+ }
1955+
1956+ void
1957+ HGraph::graph_force_remove_one (const InnerIdType& inner_id,
1958+ const FlattenInterfacePtr& flatten,
1959+ const GraphInterfacePtr& graph) {
1960+ Vector<InnerIdType> forward_neighbors (allocator_);
1961+ graph->GetNeighbors (inner_id, forward_neighbors);
1962+ Vector<InnerIdType> reverse_neighbors (allocator_);
1963+ graph->GetIncomingNeighbors (inner_id, reverse_neighbors);
1964+ if (forward_neighbors.empty () && reverse_neighbors.empty ()) {
1965+ return ;
1966+ }
1967+
1968+ UnorderedSet<InnerIdType> affected_nodes (allocator_);
1969+ auto current_count = this ->total_count_ .load ();
1970+ for (const auto & n : forward_neighbors) {
1971+ if (n < current_count) {
1972+ affected_nodes.insert (n);
1973+ }
1974+ }
1975+ for (const auto & n : reverse_neighbors) {
1976+ if (n < current_count) {
1977+ affected_nodes.insert (n);
1978+ }
1979+ }
1980+
1981+ auto max_degree = graph->MaximumDegree ();
1982+
1983+ for (const auto & neighbor : affected_nodes) {
1984+ LockGuard lock (neighbors_mutex_, neighbor);
1985+
1986+ Vector<InnerIdType> neighbors_of_neighbor (allocator_);
1987+ graph->GetNeighbors (neighbor, neighbors_of_neighbor);
1988+
1989+ UnorderedSet<InnerIdType> candidate_set (allocator_);
1990+ for (const auto & nb : neighbors_of_neighbor) {
1991+ if (nb != inner_id) {
1992+ candidate_set.insert (nb);
19481993 }
1949- std::scoped_lock label_lock (this ->label_lookup_mutex_ );
1950- this ->label_table_ ->MarkRemove (id);
1951- delete_count++;
19521994 }
1995+ for (const auto & nb : forward_neighbors) {
1996+ if (nb != inner_id && nb != neighbor) {
1997+ candidate_set.insert (nb);
1998+ }
1999+ }
2000+
2001+ Vector<InnerIdType> candidate_list (allocator_);
2002+ auto current_count = this ->total_count_ .load ();
2003+ for (const auto & candidate : candidate_set) {
2004+ if (candidate < current_count) {
2005+ candidate_list.emplace_back (candidate);
2006+ }
2007+ }
2008+
2009+ select_edges_by_heuristic (
2010+ candidate_list, neighbor, max_degree, flatten, allocator_, alpha_);
2011+
2012+ graph->InsertNeighborsById (neighbor, candidate_list);
2013+ }
2014+
2015+ Vector<InnerIdType> empty_neighbor (allocator_);
2016+ graph->InsertNeighborsById (inner_id, empty_neighbor);
2017+ }
2018+
2019+ void
2020+ HGraph::move_id (InnerIdType from, InnerIdType to) {
2021+ basic_flatten_codes_->Move (from, to);
2022+ if (high_precise_codes_) {
2023+ high_precise_codes_->Move (from, to);
2024+ }
2025+
2026+ if (extra_infos_) {
2027+ extra_infos_->Move (from, to);
2028+ }
2029+
2030+ bottom_graph_->Move (from, to);
2031+ for (const auto & route_graph : route_graphs_) {
2032+ route_graph->Move (from, to);
2033+ }
2034+
2035+ label_table_->Move (from, to);
2036+
2037+ if (entry_point_id_ == from) {
2038+ entry_point_id_ = to;
2039+ }
2040+ }
2041+
2042+ uint32_t
2043+ HGraph::force_remove_one (int64_t label) {
2044+ InnerIdType inner_id;
2045+ {
2046+ std::shared_lock lock (this ->label_lookup_mutex_ );
2047+ inner_id = this ->label_table_ ->GetIdByLabel (label);
2048+ }
2049+ if (inner_id == this ->entry_point_id_ ) {
2050+ this ->find_new_entry_point ();
2051+ }
2052+
2053+ graph_force_remove_one (inner_id, basic_flatten_codes_, bottom_graph_);
2054+
2055+ for (const auto & route_graph : route_graphs_) {
2056+ graph_force_remove_one (inner_id, basic_flatten_codes_, route_graph);
2057+ }
2058+ InnerIdType swap_id = this ->total_count_ .load () - 1 ;
2059+
2060+ if (swap_id != inner_id) {
2061+ this ->move_id (swap_id, inner_id);
2062+ }
2063+ this ->total_count_ --;
2064+ return 1 ;
2065+ }
2066+
2067+ void
2068+ HGraph::shrink_to_fit () {
2069+ auto total_count = this ->total_count_ .load ();
2070+
2071+ basic_flatten_codes_->ShrinkToFit (total_count);
2072+ if (high_precise_codes_) {
2073+ high_precise_codes_->ShrinkToFit (total_count);
2074+ }
2075+ bottom_graph_->ShrinkToFit (total_count);
2076+ for (const auto & route_graph : route_graphs_) {
2077+ route_graph->ShrinkToFit (total_count);
19532078 }
1954- return delete_count ;
2079+ label_table_-> ShrinkToFit (total_count) ;
19552080}
19562081
19572082void
@@ -2173,6 +2298,7 @@ HGraph::SearchWithRequest(const SearchRequest& request) const {
21732298 (1 <= params.ef_search ) and (params.ef_search <= ef_search_threshold),
21742299 fmt::format (" ef_search({}) must in range[1, {}]" , params.ef_search , ef_search_threshold));
21752300
2301+ std::shared_lock force_remove_rlock (this ->force_remove_mutex_ );
21762302 std::shared_lock shared_lock (this ->global_mutex_ );
21772303 // check k
21782304 CHECK_ARGUMENT (k > 0 , fmt::format (" k({}) must be greater than 0" , k));
0 commit comments