From 62d4a345ee596b4dfc5448590176ce8075a71b66 Mon Sep 17 00:00:00 2001 From: Gatefixer <313497061+lancedb-gatefixer[bot]@users.noreply.github.com> Date: Sat, 8 Aug 2026 12:41:05 +0000 Subject: [PATCH] fix(node): preserve foreign Arrow table rows --- nodejs/__test__/arrow.test.ts | 16 ++++++++++++++++ nodejs/lancedb/arrow.ts | 4 ++-- nodejs/lancedb/sanitize.ts | 15 +++++++++------ 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/nodejs/__test__/arrow.test.ts b/nodejs/__test__/arrow.test.ts index c05849cb9..4e0890082 100644 --- a/nodejs/__test__/arrow.test.ts +++ b/nodejs/__test__/arrow.test.ts @@ -61,6 +61,7 @@ describe.each([arrow15, arrow16, arrow17, arrow18])( Float32, FixedSizeList, Precision, + tableFromArrays, tableFromIPC, DataType, Dictionary, @@ -1054,6 +1055,21 @@ describe.each([arrow15, arrow16, arrow17, arrow18])( }); describe("when using two versions of arrow", function () { + it("preserves rows from a foreign Arrow Table", async function () { + const foreignTable = tableFromArrays({ + id: new Int32Array([1, 2]), + text: ["foo", "bar"], + }); + + // biome-ignore lint/suspicious/noExplicitAny: Arrow Tables from supported versions are structurally compatible + const buf = await fromDataToBuffer(foreignTable as any); + const actual = tableFromIPC(buf); + + expect(actual.numRows).toBe(2); + expect(actual.getChild("id")?.toJSON()).toEqual([1, 2]); + expect(actual.getChild("text")?.toJSON()).toEqual(["foo", "bar"]); + }); + it("can still import data", async function () { const schema = new arrow15.Schema([ new arrow15.Field("id", new arrow15.Int32()), diff --git a/nodejs/lancedb/arrow.ts b/nodejs/lancedb/arrow.ts index 587d30b19..a706f8104 100644 --- a/nodejs/lancedb/arrow.ts +++ b/nodejs/lancedb/arrow.ts @@ -82,8 +82,7 @@ export type FieldLike = }; export type DataLike = - // biome-ignore lint/suspicious/noExplicitAny: - | import("apache-arrow").Data> + | import("apache-arrow").Data | { // biome-ignore lint/suspicious/noExplicitAny: type: any; @@ -92,6 +91,7 @@ export type DataLike = stride: number; nullable: boolean; children: DataLike[]; + dictionary?: { data: readonly DataLike[] }; get nullCount(): number; // biome-ignore lint/suspicious/noExplicitAny: values: Buffers[BufferType.DATA]; diff --git a/nodejs/lancedb/sanitize.ts b/nodejs/lancedb/sanitize.ts index ae0bc0179..9220eeb83 100644 --- a/nodejs/lancedb/sanitize.ts +++ b/nodejs/lancedb/sanitize.ts @@ -72,6 +72,7 @@ import { Uint64, Union, Utf8, + Vector, } from "./arrow"; export function sanitizeMetadata( @@ -568,18 +569,18 @@ function sanitizeRecordBatch(batchLike: RecordBatchLike): RecordBatch { ); } const schema = sanitizeSchema(batchLike.schema); - const data = sanitizeData(batchLike.data); + const data = sanitizeData(batchLike.data) as Data; return new RecordBatch(schema, data); } -function sanitizeData( - dataLike: DataLike, - // biome-ignore lint/suspicious/noExplicitAny: -): import("apache-arrow").Data> { +function sanitizeData(dataLike: DataLike): Data { if (dataLike instanceof Data) { return dataLike; } + const dictionary = dataLike.dictionary + ? new Vector(dataLike.dictionary.data.map(sanitizeData)) + : undefined; return new Data( - dataLike.type, + sanitizeType(dataLike.type), dataLike.offset, dataLike.length, dataLike.nullCount, @@ -589,6 +590,8 @@ function sanitizeData( [BufferType.VALIDITY]: dataLike.nullBitmap, [BufferType.TYPE]: dataLike.typeIds, }, + dataLike.children.map(sanitizeData), + dictionary, ); }