Skip to content

fix(v2): make ExactNearestNeighbor instantiable (unblocks exact search)#316

Open
Andrew Chen (chuenchen309) wants to merge 1 commit into
langchain-ai:mainfrom
chuenchen309:fix/exact-nearest-neighbor-instantiable
Open

fix(v2): make ExactNearestNeighbor instantiable (unblocks exact search)#316
Andrew Chen (chuenchen309) wants to merge 1 commit into
langchain-ai:mainfrom
chuenchen309:fix/exact-nearest-neighbor-instantiable

Conversation

@chuenchen309

Copy link
Copy Markdown

Problem

ExactNearestNeighbor is the intended way to switch a PGVectorStore to
brute-force / exact search — apply_vector_index treats it specially and drops
the ANN index for it. But the class cannot be constructed:

@dataclass
class ExactNearestNeighbor(BaseIndex):
    index_type: str = "exactnearestneighbor"

BaseIndex declares index_options as an @abstractmethod, and
ExactNearestNeighbor never implements it, so it stays abstract:

>>> from langchain_postgres.v2.indexes import ExactNearestNeighbor
>>> ExactNearestNeighbor()
TypeError: Can't instantiate abstract class ExactNearestNeighbor without an
implementation for abstract method 'index_options'

Because no instance can ever be created, the exact-search branch in
aapply_vector_index is dead code:

async def aapply_vector_index(self, index: BaseIndex, ...):
    if isinstance(index, ExactNearestNeighbor):
        await self.adrop_vector_index()
        return
    ...
    params = "WITH " + index.index_options()

HNSWIndex / IVFFlatIndex work because they implement index_options.

Fix

Implement index_options on ExactNearestNeighbor returning an empty string.
This is safe: aapply_vector_index short-circuits (drops the index and returns)
before index_options() is ever used to build DDL, so the returned value is
never consumed — it exists only to make the class concrete/instantiable.

Test

Added test_exact_nearest_neighbor_index to
tests/unit_tests/v2/test_indexes.py (pure-Python, no live Postgres): it
constructs ExactNearestNeighbor() and checks index_type/index_options().
Fails before the change (TypeError), passes after.

$ python -m pytest tests/unit_tests/v2/test_indexes.py -q
6 passed

ruff check passes on the changed files.


Disclosure: I used AI assistance (Claude) to help find this
uninstantiable-class / dead-branch issue and write the test. I reviewed the
change, ran the tests, and take responsibility for its correctness.

`ExactNearestNeighbor` inherits `BaseIndex`'s `@abstractmethod index_options`
but never implements it, so it remains abstract: `ExactNearestNeighbor()` raises
`TypeError: Can't instantiate abstract class ... without an implementation for
abstract method 'index_options'`.

This makes the documented brute-force / exact-search path unreachable. Its only
consumer, `aapply_vector_index`, does
`if isinstance(index, ExactNearestNeighbor): await self.adrop_vector_index(); return`,
but no caller can ever construct such an instance to reach that branch. Sibling
indexes (HNSWIndex/IVFFlatIndex) work because they implement `index_options`.

Implement `index_options` returning an empty string. It is safe because
`aapply_vector_index` short-circuits (drops the index and returns) before
`index_options()` would be used to build DDL, so the value is never consumed;
it exists only to make the class concrete.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

1 participant