Skip to content

feat: add support for alter schema drop vector index - #1991

Merged
jfrancoa merged 12 commits into
mainfrom
alter-schema-drop-vector-index
Sep 16, 2026
Merged

jfrancoa merged 12 commits into
mainfrom
alter-schema-drop-vector-index

Conversation

@antas-marcin

@antas-marcin antas-marcin commented Mar 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add delete_vector_index(vector_name) method to collection config, allowing users to drop a named vector's index via DELETE /v1/schema/{className}/vectors/{vectorIndexName}/index
  • Follows the same pattern as the existing delete_property_index method
  • Includes both sync and async type stubs

Closes #1990

@orca-security-eu orca-security-eu Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Orca Security Scan Summary

Status Check Issues by priority
Passed Passed Infrastructure as Code high 0   medium 0   low 0   info 0 View in Orca
Passed Passed SAST high 0   medium 0   low 0   info 0 View in Orca
Passed Passed Secrets high 0   medium 0   low 0   info 0 View in Orca
Passed Passed Vulnerabilities high 0   medium 0   low 0   info 0 View in Orca

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds client support for dropping a named vector’s index via the schema REST endpoint, mirroring the existing “delete property index” capability in the collections config API.

Changes:

  • Added delete_vector_index(vector_name) to the collection config executor, issuing DELETE /v1/schema/{className}/vectors/{vectorIndexName}/index.
  • Added sync and async type stubs for delete_vector_index(...).

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
weaviate/collections/config/executor.py Implements the new delete-vector-index operation via the config executor.
weaviate/collections/config/sync.pyi Exposes the new method in the sync config type stub.
weaviate/collections/config/async_.pyi Exposes the new method in the async config type stub.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread weaviate/collections/config/executor.py Outdated
Comment thread weaviate/collections/config/executor.py Outdated
Comment thread weaviate/collections/config/executor.py
Comment thread weaviate/collections/config/executor.py
@jfrancoa
jfrancoa changed the base branch from main to bump-integration-tests-1.39 July 22, 2026 11:25
@jfrancoa
jfrancoa force-pushed the alter-schema-drop-vector-index branch from d728dd9 to 7e5cd7f Compare July 22, 2026 11:25
@jfrancoa
jfrancoa requested a review from a team as a code owner July 22, 2026 11:25

@orca-security-eu orca-security-eu Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Orca Security Scan Summary

Status Check Issues by priority
Passed Passed Secrets high 0   medium 0   low 0   info 0 View in Orca

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.

Comment thread weaviate/collections/classes/config_methods.py Outdated
Comment on lines 39 to 42
NONE: The index of this vector has been dropped, see ``collection.config.delete_vector_index``.
The vector data is still stored, but it cannot be searched. This value is reported by the
server only, it cannot be used to configure a vector.
"""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving as-is for now: no public Configure.VectorIndex.* factory emits NONE (only the internal create models reference the field), and the server rejects "none" on both create and update, so the exposure is constructing private classes directly. Can add a validator in a follow-up if users actually hit it.

@jfrancoa
jfrancoa force-pushed the alter-schema-drop-vector-index branch from 7e5cd7f to 4e2a580 Compare July 22, 2026 11:53
@jfrancoa
jfrancoa force-pushed the bump-integration-tests-1.39 branch from 736a7cd to be6c8dc Compare July 22, 2026 12:05
@jfrancoa
jfrancoa force-pushed the alter-schema-drop-vector-index branch from 4e2a580 to 2dbfaed Compare July 22, 2026 12:07
@bevzzz

bevzzz commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

thought: Do you think this PR could do with fewer tests? The new endpoint is very simple and it seems to me like one "happy path" test will catch most things.

Other tests verify Weaviate's behavior more than client's logic, and some may add up to 30s of pipeline time.

@orca-security-eu orca-security-eu Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Orca Security Scan Summary

Status Check Issues by priority
Passed Passed Infrastructure as Code high 0   medium 0   low 0   info 0 View in Orca
Passed Passed SAST high 0   medium 0   low 0   info 0 View in Orca
Passed Passed Secrets high 0   medium 0   low 0   info 0 View in Orca
Passed Passed Vulnerabilities high 0   medium 0   low 0   info 0 View in Orca

@jfrancoa

jfrancoa commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

@bevzzz fair point — slimmed in b92dbb7, keeping the coverage but moving it down the pyramid: the integration test is now a single happy-path journey (create → drop → poll → sibling index intact + searchable). The unknown-name case moved to the mock suite (422 → UnexpectedStatusCodeError), the invalid-input case was already covered there and its integration duplicate is deleted, and the list_all parsing assertion was already pinned by the parser unit tests. On the 30s: that's the failure-path bound of the poll — in the green path it returns as soon as the drop converges (~1-2s for a 1-object collection), so it only costs pipeline time when the feature is genuinely broken.

@jfrancoa

jfrancoa commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

@copilot review

@codecov-commenter

codecov-commenter commented Aug 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.76536% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.75%. Comparing base (95b5d76) to head (3ae5c53).
⚠️ Report is 158 commits behind head on main.

Files with missing lines Patch % Lines
integration/test_collection_config.py 84.61% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1991      +/-   ##
==========================================
+ Coverage   86.64%   88.75%   +2.11%     
==========================================
  Files         300      304       +4     
  Lines       23172    23955     +783     
==========================================
+ Hits        20077    21262    +1185     
+ Misses       3095     2693     -402     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jfrancoa
jfrancoa force-pushed the alter-schema-drop-vector-index branch from 9d7b916 to ccad4cc Compare August 7, 2026 10:52
@jfrancoa
jfrancoa force-pushed the alter-schema-drop-vector-index branch from ccad4cc to 16ea348 Compare August 25, 2026 11:17

@dirkkul dirkkul left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a few smaller things, none of them blocking:

  • the delete_vector_index docstring goes stale as soon as the endpoint is promoted or the status code changes: it names ENABLE_EXPERIMENTAL_ALTER_SCHEMA_DROP_VECTOR_INDEX_ENDPOINT=true and says the server answers 500 without it. someone reading it after that sets an env var the server no longer looks at. could we say the endpoint is experimental and may be disabled server-side, without naming the flag or the status code?

  • in test/collection/test_config_methods.py, test_collection_config_simple_from_json_with_dropped_vector_index and test_collection_config_simple_from_json_all_vectors_dropped parse the same schemas as the two non-simple tests above them - __get_vector_config(schema, simple) never reads simple, and __get_vectorizer takes no such parameter. the simple path does build a different dataclass, so could we keep one of the two as the list_all() guard and drop the other?

  • test_delete_vector_index_endpoint_disabled overlaps the 422 case already inside test_delete_vector_index - both assert that a non-OK status comes back as UnexpectedStatusCodeError carrying the status code. the one thing it adds is that the server's error message reaches the exception, so could that move into test_delete_vector_index as another status/message case instead of standing up its own mock server?

reviewed with claude code, I went through every comment below myself

Comment thread test/collection/test_config_update.py
Comment thread weaviate/collections/classes/config.py Outdated
Comment thread weaviate/collections/classes/config_methods.py Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Critical and moderate dropped-index update and recreation issues remain unresolved.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (3)

Previously missed (1) — in code that hasn't changed since the last review.

weaviate/collections/config/executor.py:679

  • The drop cleanup does not retain these vector values permanently: after the marker is applied, Weaviate asynchronously strips the target vector from stored objects before finalizing the schema. Saying that “the vectors themselves are kept” gives callers an incorrect data-retention guarantee; describe them as temporarily retained while cleanup is pending and then removed.

weaviate/collections/classes/config.py:1697

  • When a drop is still in progress, Weaviate returns the dropped entry as vectorIndexType: "none"; this merge preserves that entry while applying a change to another vector. The server's update path skips UpdateVectorIndexConfigs for all vectors whenever any dropped entry is present, so config.update(vector_config=...) can return success while silently not applying the active vector's change. Please handle this intermediate state (or reject/wait for the update) and cover the actual REST update, not only the local merge.
                existing = self.__existing_vector_index_config(schema, vc.name)
                self.__check_quantizers(vc.vectorIndexConfig.quantizer, existing)
                schema["vectorConfig"][vc.name]["vectorIndexConfig"] = (
                    vc.vectorIndexConfig.merge_with_existing(existing)
                )

weaviate/collections/config/executor.py:684

  • The server can keep a dropped named-vector entry in vector_config with VectorIndexConfigNone even after the asynchronous cleanup; the parser and integration helper already support both that shape and an omitted entry. Avoid promising that the entry is always removed once the on-disk index is gone, because callers may still observe the none marker.
            The drop is applied asynchronously. A successful call means that Weaviate accepted the
            request, not that the index is already gone. `collection.config.get()` first reports the
            vector with a `vector_index_config` of `VectorIndexConfigNone` and drops it from
            `vector_config` altogether once the index has been removed from disk.
  • Files reviewed: 15/15 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread weaviate/collections/collections/executor.py Outdated

@dirkkul dirkkul left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reviewed with claude code, I went through every comment below myself

Comment thread weaviate/collections/config/executor.py Outdated
Comment thread weaviate/collections/config/executor.py Outdated
Comment thread weaviate/collections/config/executor.py Outdated
antas-marcin and others added 10 commits September 15, 2026 11:42
Follow-up to the `delete_vector_index` support, addressing review findings.

After a successful drop, Weaviate keeps the vector in the schema as
`vectorIndexType: "none"` with no `vectorIndexConfig`. The client asserted that
every named vector has an index config, so `collection.config.get()` and
`client.collections.list_all()` raised `AssertionError` for every collection in
the cluster once any vector index had been dropped. `_NamedVectorConfig.
vector_index_config` is now optional, `VectorIndexType` gained a server-reported
`NONE` member and `to_dict()` round-trips it.

`collection.config.update()` on a dropped vector raised a bare
`KeyError: 'vectorIndexConfig'` from the schema merge. Both the current and the
deprecated merge paths now go through one helper that raises a
`WeaviateInvalidInputError` explaining that a dropped index cannot be re-created.

The docstring claimed the index could be regenerated and that a missing vector
raises `WeaviateInvalidInputError`. Neither is true: Weaviate rejects
re-creating a dropped index, and an unknown vector name comes back as a 422.
It now also documents that the endpoint is experimental and needs
`ENABLE_EXPERIMENTAL_ALTER_SCHEMA_DROP_VECTOR_INDEX_ENDPOINT=true`, that only
named vectors can be dropped, and that the drop is applied asynchronously. The
error message no longer blames a missing vector for what is usually a disabled
endpoint.

Tests: unit coverage for parsing, exporting and updating a dropped vector, mock
coverage for the request path and the disabled-endpoint response, and
integration coverage gated at 1.39.0. The CI compose file enables the
experimental endpoint; that flag can be dropped once 1.39.0 is GA.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Replace the assert on dropped-vector schema shape with an explicit
  SchemaValidationError so a named vector missing vectorIndexConfig fails
  fast even under python -O; pinned by a new parser unit test
- Slim the integration test to the happy path: the unknown-name error
  contract moved to the mock suite (422 -> UnexpectedStatusCodeError) and
  the redundant list_all/invalid-input assertions are covered by the
  existing unit and mock tests

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A single-vector (non-named) collection whose vector index is dropped with
`collection.config.delete_vector_index` comes back from the server with no
top-level `vectorizer` (and no `vectorConfig`, `vectorIndexType` or
`vectorIndexConfig`). `__get_vectorizer` accessed `schema["vectorizer"]`
unguarded and raised `KeyError: 'vectorizer'`, so both `config.get()` and
`collections.list_all()` crashed on such a collection. Return `None` when the
key is absent, matching how a dropped named vector yields
`vector_index_config is None`. Add parser tests for both entry points.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Verified against the Weaviate server source that these comments were wrong:

- The endpoint is still experimental and off by default in current main; it is
  not enabled by default at 1.39.0 GA. The server rejects the drop unless
  ENABLE_EXPERIMENTAL_ALTER_SCHEMA_DROP_VECTOR_INDEX_ENDPOINT=true
  (usecases/schema/property.go:255). Fix the misleading ci/docker-compose.yml
  comment.

- A legacy single-vector collection cannot reach the "no vectorConfig, no
  vectorizer" shape: the server rejects dropping its index because
  len(class.VectorConfig) == 0 (property.go:288), and setClassDefaults always
  forces a non-empty top-level vectorizer for legacy classes (class.go:750).
  That shape can only come from a named-vector collection whose vectors were all
  dropped. Reword the __get_vectorizer guard comment and rename the parser tests
  accordingly. Behavior and assertions are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- config.py: __existing_vector_index_config raised a raw KeyError when the
  collection had no vectors left (server omits vectorConfig once every named
  vector is dropped). Guard the key so the intended WeaviateInvalidInputError is
  raised instead. Add a regression test.

- config_methods.py: __get_vector_config reported "no vectorIndexConfig" for a
  vectorIndexType the client does not know, even though the config was present
  (an older client against a newer server). Branch on "vectorIndexConfig" in the
  named vector and give the unknown-type case its own message. Add a regression
  test.

- executor.py: delete_vector_index no longer names the env flag or the 500 status
  in its docstring (both go stale when the endpoint is promoted); it now returns
  None instead of a bool that could only ever be True. Regenerate stubs.

- tests: fold the disabled-endpoint case into test_delete_vector_index (its only
  unique check is that the server message reaches the exception) and drop the
  redundant simple-parser test; the all-vectors-dropped simple test remains the
  list_all() guard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The round-4 commit changed delete_vector_index to return None but only updated
the mock test and stubs; the integration assertion still expected True and would
fail on every >=1.39 CI job.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Represent a dropped vector index with _VectorIndexConfigNone instead of None:
  the union in _NamedVectorConfig stays concrete, to_dict() derives the type via
  vector_index_type() like every other index, and the index type of a dropped
  vector is visible without serializing. The dict output still omits
  vectorIndexConfig, matching what the server sends. Exported as
  VectorIndexConfigNone in weaviate.outputs.config.

- Strip dropped-vector entries in collections __create (shared by create,
  create_from_dict and create_from_config): the server rejects
  vectorIndexType "none" on create, so importing an exported config that ever
  had a vector index dropped answered 422. The whole entry is skipped, not just
  the type, because keeping it would re-create an index that was deliberately
  dropped. A Col001 warning names the skipped vectors; an emptied vectorConfig
  block is omitted entirely. Mock test verifies the request body both ways,
  confirmed failing without the strip.

- Document in create_from_dict/create_from_config that the round trip is lossy:
  there is no API to re-create a vector without an index, so the new collection
  does not contain the dropped vectors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Reference delete_vector_index with parentheses in every docstring, comment,
  and warning string so it reads as a method, matching the existing () usage in
  the update-path error message.

- Fix the Col001 warning wording: "their index was dropped" (the subject is
  plural) and "will be created" (the warning fires before the POST, so the
  create can still fail).
Verified against a 1.39.0 server by the reviewer, and against the server source:

- The drop removes the vector's data from every object once the cleanup runs;
  the docstrings claimed the vectors were kept. Corrected in delete_vector_index,
  _VectorIndexConfigNone and VectorIndexType.NONE.

- "cannot be re-created" only holds while the drop is in progress; once it
  completes, add_vector() accepts a new vector with the same name. Corrected in
  the docstring, the config.update() error and the Col001 warning.

- Document the repeated-call semantics: a second delete_vector_index() while the
  drop is in progress succeeds and re-triggers the cleanup; after it completes
  the same call gets a 422 because the vector no longer exists.

- Reject a create whose vectorConfig would end up empty after stripping dropped
  vectors: the server treats a create without named vectors as a legacy
  collection (setClassDefaults applies the default vectorizer and vector index
  whenever len(VectorConfig) == 0, and an empty map behaves like an absent one),
  so the previous behavior silently created a default legacy HNSW collection
  instead of one without vectors. Raise WeaviateInvalidInputError instead.
@jfrancoa
jfrancoa force-pushed the alter-schema-drop-vector-index branch from 2e8450f to acd2390 Compare September 15, 2026 09:49
@jfrancoa
jfrancoa requested review from dirkkul and a lite review from Copilot September 15, 2026 09:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

All vectors being dropped can round-trip as a legacy schema, causing the server to apply a default vector index.

Get a fresh assessment by requesting another Copilot review.

Review details
  • Files reviewed: 15/15 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread weaviate/collections/collections/executor.py
Once a drop's cleanup finishes, an exported config carries no vectorConfig key
and no "none" markers, so the round-7 marker check in __create could not catch
an all-dropped export and create_from_config posted a legacy-style schema (the
server would apply its default vectorizer and vector index). Reject that shape
in _create_from_config: a CollectionConfig with neither named vectors nor a
legacy vectorizer can only be such an export, because the server always fills a
vectorizer on legacy collections. A plain dict without vector fields keeps
working through create_from_dict, since minimal hand-written dicts legitimately
rely on the server defaults.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Collection recreation mishandles cleaned-up all-dropped schemas and valid vectorless configurations.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (2)

weaviate/collections/collections/executor.py:485

  • This guard prevents create_from_config from recreating a valid vectorless collection after its last named vector has been finalized: the exported config has vector_config=None and vectorizer=None, and Weaviate intentionally preserves that state without applying legacy defaults when no legacy vector fields are supplied. create_from_dict(config.to_dict()) already sends the same vectorless shape, so rejecting only the config-object path breaks export/recreate and contradicts the new docstrings. Remove this guard (and its corresponding rejection documentation).
        if config.vector_config is None and config.vectorizer is None:
            raise WeaviateInvalidInputError(
                f"Collection config {config.name!r} has no vector config left; its vectors were "
                "dropped with collection.config.delete_vector_index() and removed by the drop's "
                "cleanup. Creating it would make the server apply its default legacy vector index "
                "instead. Give the config at least one vector before creating the collection."

weaviate/collections/collections/executor.py:173

  • This branch assumes that removing the last vectorConfig entry would make Weaviate apply a legacy default, but Weaviate also supports genuinely vectorless collections: a create body with no named vectors or legacy vector fields is persisted without an index. As a result, a config containing only dropped vectors cannot be recreated even though the safe result is a vectorless collection; do not reject this solely because remaining is empty (or otherwise distinguish dropped provenance without relying on the schema shape).
            raise WeaviateInvalidInputError(
                f"Every vector config in this collection ({dropped}) has vectorIndexType 'none' "
                "(its index was dropped with collection.config.delete_vector_index()). Creating "
                "the collection without them would make the server apply its default legacy "
                "vector index instead. Give at least one vector a real index config before "
  • Files reviewed: 15/15 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread weaviate/collections/collections/executor.py
Narrow the create_from_dict docstring: the all-dropped rejection needs the
"none" markers to still be present in the dictionary. A dictionary exported
after the drops finished carries no vector fields, cannot be told apart from a
minimal legacy config, and is sent unchanged; create_from_config() is the path
that rejects that shape.
@jfrancoa
jfrancoa requested a lite review from Copilot September 15, 2026 15:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The endpoint, schema lifecycle handling, validation, documentation, and automated coverage are consistent and complete.

Review details
  • Files reviewed: 15/15 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@jfrancoa
jfrancoa merged commit decbbd2 into main Sep 16, 2026
127 of 128 checks passed
@jfrancoa
jfrancoa deleted the alter-schema-drop-vector-index branch September 16, 2026 10:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for delete vector index operation

6 participants