mirror of
https://github.com/lancedb/lancedb.git
synced 2026-10-12 14:52:26 +00:00
fix(search): validate effort examples and hybrid queries
This commit is contained in:
@@ -646,6 +646,38 @@ rerank(reranker): VectorQuery
|
||||
|
||||
***
|
||||
|
||||
### searchEffort()
|
||||
|
||||
```ts
|
||||
searchEffort(searchEffort): VectorQuery
|
||||
```
|
||||
|
||||
Set the initial IVF search effort to a finite value in [0, 1].
|
||||
|
||||
The default, null, uses Auto (internally 0.5). Zero starts at the caller
|
||||
minimum; one searches all partitions within the caller maximum. Later
|
||||
probing may continue when too few candidates are found.
|
||||
Any explicit effort, including 0.5, overrides nprobes in either call order.
|
||||
Explicit minimum and maximum bounds still apply. This is not a recall
|
||||
guarantee and does not change refinement or HNSW ef. The MemWAL LSM
|
||||
scanner does not yet support this option.
|
||||
|
||||
#### Parameters
|
||||
|
||||
* **searchEffort**: `null` \| `number`
|
||||
|
||||
#### Returns
|
||||
|
||||
[`VectorQuery`](VectorQuery.md)
|
||||
|
||||
#### Example
|
||||
|
||||
```ts
|
||||
table.vectorSearch([0.1, 0.2]).searchEffort(0.75).limit(10);
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
### select()
|
||||
|
||||
```ts
|
||||
|
||||
@@ -1734,6 +1734,9 @@ class LanceVectorQueryBuilder(LanceQueryBuilder):
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import lancedb
|
||||
>>> db = lancedb.connect("memory://")
|
||||
>>> table = db.create_table("effort_vectors", [{"vector": [0.1, 0.2]}])
|
||||
>>> query = table.search([0.1, 0.2]).search_effort(0.75)
|
||||
"""
|
||||
self._search_effort = search_effort
|
||||
@@ -2562,11 +2565,17 @@ class LanceHybridQueryBuilder(LanceQueryBuilder):
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> query = table.search([0.1, 0.2]).search_effort(0.75)
|
||||
>>> import lancedb
|
||||
>>> db = lancedb.connect("memory://")
|
||||
>>> table = db.create_table(
|
||||
... "effort_hybrid", [{"vector": [0.1, 0.2], "text": "dog"}]
|
||||
... )
|
||||
>>> query = (
|
||||
... table.search(query_type="hybrid").vector([0.1, 0.2])
|
||||
... .text("dog").search_effort(0.75)
|
||||
... )
|
||||
"""
|
||||
self._search_effort = search_effort
|
||||
if self._vector_query is not None:
|
||||
self._vector_query.search_effort(search_effort)
|
||||
return self
|
||||
|
||||
def minimum_nprobes(self, minimum_nprobes: int) -> LanceHybridQueryBuilder:
|
||||
@@ -3790,7 +3799,15 @@ class AsyncVectorQueryBase:
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> query = table.vector_search([0.1, 0.2]).search_effort(0.75)
|
||||
>>> import asyncio
|
||||
>>> import lancedb
|
||||
>>> async def build_query():
|
||||
... db = await lancedb.connect_async("memory://")
|
||||
... table = await db.create_table(
|
||||
... "effort_async", [{"vector": [0.1, 0.2]}]
|
||||
... )
|
||||
... return table.vector_search([0.1, 0.2]).search_effort(0.75)
|
||||
>>> query = asyncio.run(build_query())
|
||||
"""
|
||||
self._inner.search_effort(search_effort)
|
||||
return self
|
||||
|
||||
@@ -2284,6 +2284,7 @@ def test_search_effort_probe_budget(tmp_path, monkeypatch, metric, effort, probe
|
||||
data = pa.table(
|
||||
{
|
||||
"id": np.arange(512),
|
||||
"text": ["dog"] * 512,
|
||||
"vector": pa.FixedSizeListArray.from_arrays(pa.array(vectors.ravel()), 16),
|
||||
}
|
||||
)
|
||||
@@ -2298,7 +2299,8 @@ def test_search_effort_probe_budget(tmp_path, monkeypatch, metric, effort, probe
|
||||
return table.search(vector).distance_type(metric).search_effort(value).limit(8)
|
||||
|
||||
def partitions(query):
|
||||
values = re.findall(r"partitions_searched=(\d+)", query.analyze_plan())
|
||||
vector_plan = query.analyze_plan().split("FTS Search Plan:", 1)[0]
|
||||
values = re.findall(r"partitions_searched=(\d+)", vector_plan)
|
||||
assert values
|
||||
return sum(map(int, values))
|
||||
|
||||
@@ -2335,6 +2337,19 @@ def test_search_effort_probe_budget(tmp_path, monkeypatch, metric, effort, probe
|
||||
assert partitions(filtered) <= 2
|
||||
assert all(row["id"] % 8 == 0 for row in filtered.to_list())
|
||||
|
||||
if metric == "l2" and effort == 0.5:
|
||||
table.create_index("text", config=FTS())
|
||||
hybrid = (
|
||||
table.search(query_type="hybrid")
|
||||
.vector(vector)
|
||||
.text("dog")
|
||||
.nprobes(1)
|
||||
.search_effort(0.5)
|
||||
.limit(8)
|
||||
)
|
||||
assert partitions(hybrid) == 4
|
||||
assert partitions(hybrid.search_effort(None)) == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"effort", [-0.1, 1.1, float("nan"), float("inf"), -float("inf")]
|
||||
|
||||
Reference in New Issue
Block a user