feat(view): full=True force-rebuild on refresh_materialized_view

View.refresh(full=True) (sync + async) now works -- it previously raised
NotImplementedError. Thread the flag through the client: RefreshMaterialized-
ViewRequest.full -> the REST body (RemoteRefreshMaterializedViewRequest.full);
pyo3 refresh_materialized_view(full=...); Connection.refresh_materialized_view(
name, full=) sync + async. A full refresh forces a recompute-and-replace and
preserves the view's indexes (reindexed by the distributed indexer).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Wyatt Alt
2026-06-14 13:56:08 -07:00
committed by Jack Ye
parent fd0a3b97d0
commit 8b38500b07
5 changed files with 38 additions and 18 deletions

View File

@@ -562,7 +562,6 @@ class DBConnection(EnforceOverrides):
"""
raise NotImplementedError("serialize is not supported for this connection type")
# -- Derived compute: functions, materialized views, jobs -------------
# Server-backed features (LanceDB Enterprise / Cloud); local
# connections raise NotImplementedError for now.
@@ -711,14 +710,20 @@ class DBConnection(EnforceOverrides):
self,
name: str,
*,
full: bool = False,
src_version: Optional[int] = None,
num_workers: Optional[int] = None,
max_workers: Optional[int] = None,
) -> str:
"""Refresh a materialized view; returns the refresh job id."""
"""Refresh a materialized view; returns the refresh job id.
``full=True`` forces a full rebuild (recompute and replace every row)
instead of the default incremental refresh.
"""
return LOOP.run(
self._conn.refresh_materialized_view(
name,
full=full,
src_version=src_version,
num_workers=num_workers,
max_workers=max_workers,
@@ -767,6 +772,7 @@ class DBConnection(EnforceOverrides):
"""
return LOOP.run(self._conn.cancel_job(job_id))
class LanceDBConnection(DBConnection):
"""
A connection to a LanceDB database.
@@ -2093,13 +2099,19 @@ class AsyncConnection(object):
self,
name: str,
*,
full: bool = False,
src_version: Optional[int] = None,
num_workers: Optional[int] = None,
max_workers: Optional[int] = None,
) -> str:
"""Refresh a materialized view; returns the refresh job id."""
"""Refresh a materialized view; returns the refresh job id.
``full=True`` forces a full rebuild (recompute and replace every row)
instead of the default incremental refresh.
"""
return await self._inner.refresh_materialized_view(
name,
full=full,
src_version=src_version,
num_workers=num_workers,
max_workers=max_workers,

View File

@@ -499,14 +499,13 @@ class View:
self.name = name
def refresh(self, full: bool = False):
if full:
# full/force-rebuild is not honored on any surface yet (the
# refresh event carries no `full` flag) -- do not pretend.
raise NotImplementedError(
"full=True refresh is not supported yet (engine gap: the "
"refresh event has no full-rebuild flag)"
)
return self.conn.refresh_materialized_view(self.name)
"""Refresh the materialized view; returns the refresh job id.
``full=True`` forces a full rebuild (recompute and replace every row)
instead of the default incremental refresh. A full rebuild preserves
the view's indexes -- they are reindexed by the distributed indexer.
"""
return self.conn.refresh_materialized_view(self.name, full=full)
def explain_refresh(self, full: bool = False):
"""Plan a refresh without running it (EXPLAIN REFRESH)."""
@@ -616,12 +615,12 @@ class AsyncView:
self.name = name
async def refresh(self, full: bool = False):
if full:
raise NotImplementedError(
"full=True refresh is not supported yet (engine gap: the "
"refresh event has no full-rebuild flag)"
)
return await self.conn.refresh_materialized_view(self.name)
"""Refresh the materialized view; returns the refresh job id.
``full=True`` forces a full rebuild instead of an incremental refresh
(indexes are preserved and reindexed by the distributed indexer).
"""
return await self.conn.refresh_materialized_view(self.name, full=full)
async def explain_refresh(self, full: bool = False):
return await self.conn.explain_refresh_materialized_view(self.name, full=full)

View File

@@ -437,10 +437,11 @@ impl Connection {
})
}
#[pyo3(signature = (name, src_version=None, num_workers=None, max_workers=None))]
#[pyo3(signature = (name, full=false, src_version=None, num_workers=None, max_workers=None))]
pub fn refresh_materialized_view(
self_: PyRef<'_, Self>,
name: String,
full: bool,
src_version: Option<u64>,
num_workers: Option<u32>,
max_workers: Option<u32>,
@@ -450,6 +451,7 @@ impl Connection {
inner
.refresh_materialized_view(RefreshMaterializedViewRequest {
name,
full,
src_version,
num_workers,
max_workers,