mirror of
https://github.com/lancedb/lancedb.git
synced 2026-10-13 07:12:27 +00:00
feat: cascade option on materialized view refresh
A view over a view could only be brought fully up to date by refreshing each view in the lineage by hand, in order. `cascade` on the refresh builder, Python `refresh`/`refresh_async` and Node `refresh` does that in one call: every upstream view is refreshed furthest first, each reading the version the one before it left, then the named view. `full` applies to the named view only. Locally the lineage is walked through each view's source, and each hop is refreshed against the incarnation read in that walk. Remote tables send `cascade` in the refresh request, only when set, so older servers that reject unknown fields are unaffected by plain refreshes.
This commit is contained in:
@@ -112,6 +112,23 @@ describe("materialized views", () => {
|
||||
expect((await view.refresh()).mode).toBe("no_op");
|
||||
});
|
||||
|
||||
it("cascades a refresh through upstream views", async () => {
|
||||
await db.createMaterializedView("adults", "people", {
|
||||
where: "age >= 18",
|
||||
withNoData: true,
|
||||
});
|
||||
const seniors = await db.createMaterializedView("seniors", "adults", {
|
||||
where: "age >= 65",
|
||||
withNoData: true,
|
||||
});
|
||||
expect(Number((await seniors.refresh()).rowsWritten)).toBe(0);
|
||||
|
||||
expect(Number((await seniors.refresh({ cascade: true })).rowsWritten)).toBe(
|
||||
1,
|
||||
);
|
||||
expect(await (await db.openTable("adults")).countRows()).toBe(2);
|
||||
});
|
||||
|
||||
it("lists views and rejects non-views", async () => {
|
||||
await db.createMaterializedView("adults", "people", {
|
||||
where: "age >= 18",
|
||||
|
||||
@@ -190,7 +190,9 @@ export class MaterializedView {
|
||||
* The refresh is incremental when the source's changes can be reconciled
|
||||
* into the view -- rows added, changed or removed since the last one --
|
||||
* and otherwise rebuilds. `full` forces a rebuild; `sourceVersion`
|
||||
* refreshes to that source version instead of the latest.
|
||||
* refreshes to that source version instead of the latest. `cascade` first
|
||||
* refreshes every upstream view, furthest first, each reading the version
|
||||
* the one before it left; `full` applies to this view only.
|
||||
*
|
||||
* Concurrent refreshes of one view do not duplicate its rows. Two that
|
||||
* plan the same source rows conflict on commit, and the loser throws
|
||||
@@ -199,11 +201,13 @@ export class MaterializedView {
|
||||
async refresh(options?: {
|
||||
full?: boolean;
|
||||
sourceVersion?: number;
|
||||
cascade?: boolean;
|
||||
}): Promise<RefreshMaterializedViewResult> {
|
||||
validateNonNegativeInteger(options?.sourceVersion, "sourceVersion");
|
||||
return await this.inner.refreshMaterializedView(
|
||||
options?.full,
|
||||
options?.sourceVersion,
|
||||
options?.cascade,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -670,6 +670,7 @@ export abstract class Table {
|
||||
abstract refreshMaterializedView(
|
||||
full?: boolean,
|
||||
sourceVersion?: number,
|
||||
cascade?: boolean,
|
||||
): Promise<RefreshMaterializedViewResult>;
|
||||
|
||||
/** @ignore */
|
||||
@@ -1397,8 +1398,13 @@ export class LocalTable extends Table {
|
||||
async refreshMaterializedView(
|
||||
full?: boolean,
|
||||
sourceVersion?: number,
|
||||
cascade?: boolean,
|
||||
): Promise<RefreshMaterializedViewResult> {
|
||||
return await this.inner.refreshMaterializedView(full, sourceVersion);
|
||||
return await this.inner.refreshMaterializedView(
|
||||
full,
|
||||
sourceVersion,
|
||||
cascade,
|
||||
);
|
||||
}
|
||||
|
||||
async materializedViewDefinition(): Promise<string> {
|
||||
|
||||
+5
-1
@@ -458,11 +458,15 @@ impl Table {
|
||||
&self,
|
||||
full: Option<bool>,
|
||||
source_version: Option<i64>,
|
||||
cascade: Option<bool>,
|
||||
) -> napi::Result<RefreshMaterializedViewResult> {
|
||||
let view = lancedb::MaterializedView::from_table(self.inner_ref()?.clone())
|
||||
.await
|
||||
.default_error()?;
|
||||
let mut builder = view.refresh().full(full.unwrap_or(false));
|
||||
let mut builder = view
|
||||
.refresh()
|
||||
.full(full.unwrap_or(false))
|
||||
.cascade(cascade.unwrap_or(false));
|
||||
if let Some(version) = source_version {
|
||||
let version = u64::try_from(version).map_err(|_| {
|
||||
napi::Error::from_reason("sourceVersion must be a non-negative integer")
|
||||
|
||||
Reference in New Issue
Block a user