Repository: qdrant/qdrant
File: lib/segment/src/index/hnsw_index/hnsw.rs (Rust)

Code before:
            return None;
        }

        let old_index = AtomicRef::filter_map(old_index.borrow(), |index| match index {
            VectorIndexEnum::Hnsw(old_index) => Some(old_index),
            _ => None,
        })?;

        let old_quantized_vectors_ref = old_index.quantized_vectors.borrow();
        let old_quantization_config = old_quantized_vectors_ref
            .as_ref()
            .map(|qv| &qv.config().quantization_config);
        let new_quantization_config = quantized_vectors
            .as_ref()
            .map(|qv| &qv.config().quantization_config);

        let no_main_graph = config.m == 0;
        let configuration_mismatch = config.m != old_index.config.m
            || config.m0 != old_index.config.m0
            || config.ef_construct != old_index.config.ef_construct
            || new_quantization_config != old_quantization_config;
        // If old graph has vectors, reusing it will cause a lot of random reads,
        // making it slower than building from scratch.
        let old_graph_is_with_vectors = old_index.graph.has_vectors();
        if no_main_graph || configuration_mismatch || old_graph_is_with_vectors {
            return None;
        }
        drop(old_quantized_vectors_ref);

        let new_deleted = vector_storage.deleted_vector_bitslice();
        let old_id_tracker = old_index.id_tracker.borrow();

        let healing_enabled = hnsw_global_config.healing_threshold > 0.0;

        if old_id_tracker.deleted_point_count() != 0 {
            // Old index has deleted points.
            if !healing_enabled {
                return None;
            }
        }

        // Rough check whether the point is included in the old graph.
        // If it's included, it almost certainly has at least one outgoing link at level 0.
        let old_graph_has_point = |id: PointOffsetType| !old_index.graph.links.links_empty(id, 0);

        // Build old_to_new mapping.
        let mut valid_points = 0;
        let mut missing_points = 0;
        let mut old_to_new = vec![None; old_id_tracker.total_point_count()];

        // Loop 1: Loop through all external ids, which present in either new or old index.

Review comment (performance):
Nit: renaming `has_vectors()` to `has_inline_vectors()` is a little bit more intuitive to me.

I was confused because I'd argue all graphs have _vectors_.

Alternatively I'm fine with just this change:


```suggestion
        // If old graph has inline vectors, reusing it will cause a lot of random reads,
        // making it slower than building from scratch.
        let old_graph_is_with_vectors = old_index.graph.has_vectors();
        if no_main_graph || configuration_mismatch || old_graph_is_with_vectors {
```

Code after:
        if !feature_flags.incremental_hnsw_building {
            return None;
        }

        let old_index = AtomicRef::filter_map(old_index.borrow(), |index| match index {
            VectorIndexEnum::Hnsw(old_index) => Some(old_index),
            _ => None,
        })?;

        let old_quantized_vectors_ref = old_index.quantized_vectors.borrow();
        let old_quantization_config = old_quantized_vectors_ref
            .as_ref()
            .map(|qv| &qv.config().quantization_config);
        let new_quantization_config = quantized_vectors
            .as_ref()
            .map(|qv| &qv.config().quantization_config);

        let no_main_graph = config.m == 0;
        let configuration_mismatch = config.m != old_index.config.m
            || config.m0 != old_index.config.m0
            || config.ef_construct != old_index.config.ef_construct
            || new_quantization_config != old_quantization_config;
        // If old graph has vectors, reusing it will cause a lot of random reads,
        // making it slower than building from scratch.
        let old_graph_is_with_vectors = old_index.graph.has_inline_vectors();
        if no_main_graph || configuration_mismatch || old_graph_is_with_vectors {
            return None;
        }
        drop(old_quantized_vectors_ref);

        let new_deleted = vector_storage.deleted_vector_bitslice();
        let old_id_tracker = old_index.id_tracker.borrow();

        let healing_enabled = hnsw_global_config.healing_threshold > 0.0;

        if old_id_tracker.deleted_point_count() != 0 {
            // Old index has deleted points.
            if !healing_enabled {
                return None;
            }
        }

        // Rough check whether the point is included in the old graph.
        // If it's included, it almost certainly has at least one outgoing link at level 0.
        let old_graph_has_point = |id: PointOffsetType| !old_index.graph.links.links_empty(id, 0);

        // Build old_to_new mapping.
        let mut valid_points = 0;
        let mut missing_points = 0;
        let mut old_to_new = vec![None; old_id_tracker.total_point_count()];