From fe22f8cf4337722915a3b1406573091e98c3b637 Mon Sep 17 00:00:00 2001 From: Will Jones Date: Thu, 4 Jun 2026 15:05:28 -0700 Subject: [PATCH] feat: align IndexConfig fields with lance-namespace IndexContent Add `type_url` and `index_version` to `IndexConfig` so the field set mirrors the lance-namespace `IndexContent` contract (lance-format/lance-namespace#348), keeping client and server on the same shape. - `type_url`: from `IndexDescription::type_url` - `index_version`: from the first segment's index version Co-Authored-By: Claude Opus 4.8 (1M context) --- rust/lancedb/src/index.rs | 16 ++++++++++++---- rust/lancedb/src/remote/table.rs | 14 ++++++++++---- rust/lancedb/src/table.rs | 9 ++++++++- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/rust/lancedb/src/index.rs b/rust/lancedb/src/index.rs index 2ed4ba274..4603571c1 100644 --- a/rust/lancedb/src/index.rs +++ b/rust/lancedb/src/index.rs @@ -371,6 +371,10 @@ pub struct IndexConfig { /// This is the UUID of the first segment. `None` if it could not be /// determined (e.g. for remote tables, which do not yet surface this). pub index_uuid: Option, + /// The protobuf type URL, a precise type identifier for the index. + /// + /// `None` if unavailable (e.g. for remote tables). + pub type_url: Option, /// When the index was created, taken as the minimum creation time across /// all segments. /// @@ -387,15 +391,19 @@ pub struct IndexConfig { /// `None` if size information is unavailable, such as for indices created /// before file sizes were tracked, or for remote tables. pub size_bytes: Option, + /// The number of segments that make up the index. + /// + /// `None` if unavailable (e.g. for remote tables). + pub num_segments: Option, + /// The on-disk index format version, taken from the first segment. + /// + /// `None` if unavailable (e.g. for remote tables). + pub index_version: Option, /// Index-type-specific details, serialized as JSON. /// /// The shape of this JSON varies by index type. `None` if the details /// could not be produced (e.g. no plugin available) or for remote tables. pub index_details: Option, - /// The number of segments that make up the index. - /// - /// `None` if unavailable (e.g. for remote tables). - pub num_segments: Option, } #[skip_serializing_none] diff --git a/rust/lancedb/src/remote/table.rs b/rust/lancedb/src/remote/table.rs index fbce83a64..576d686a3 100644 --- a/rust/lancedb/src/remote/table.rs +++ b/rust/lancedb/src/remote/table.rs @@ -2049,13 +2049,15 @@ impl BaseTable for RemoteTable { index_type: stats.index_type, columns, // These are left None until the server response wires - // them through. See https://github.com/lancedb/lancedb/issues/3492 + // them through. See https://github.com/lancedb/lancedb/issues/3494 index_uuid: None, + type_url: None, created_at: None, num_indexed_rows: None, size_bytes: None, - index_details: None, num_segments: None, + index_version: None, + index_details: None, })), Ok(None) => Ok(None), // The index must have been deleted since we listed it. Err(e) => Err(e), @@ -3953,22 +3955,26 @@ mod tests { index_type: IndexType::IvfPq, columns: vec!["vector".into()], index_uuid: None, + type_url: None, created_at: None, num_indexed_rows: None, size_bytes: None, - index_details: None, num_segments: None, + index_version: None, + index_details: None, }, IndexConfig { name: "my_idx".into(), index_type: IndexType::LabelList, columns: vec!["metadata.`my.column`".into()], index_uuid: None, + type_url: None, created_at: None, num_indexed_rows: None, size_bytes: None, - index_details: None, num_segments: None, + index_version: None, + index_details: None, }, ]; assert_eq!(indices, expected); diff --git a/rust/lancedb/src/table.rs b/rust/lancedb/src/table.rs index 893581c39..252572f65 100644 --- a/rust/lancedb/src/table.rs +++ b/rust/lancedb/src/table.rs @@ -2930,17 +2930,20 @@ impl BaseTable for NativeTable { let segments = idx_desc.segments(); let index_uuid = segments.first().map(|seg| seg.uuid.to_string()); let created_at = segments.iter().filter_map(|seg| seg.created_at).min(); + let index_version = segments.first().map(|seg| seg.index_version); Some(IndexConfig { name: idx_desc.name().to_string(), index_type, columns, index_uuid, + type_url: Some(idx_desc.type_url().to_string()), created_at, num_indexed_rows: Some(idx_desc.rows_indexed()), size_bytes: idx_desc.total_size_bytes(), - index_details: idx_desc.details().ok(), num_segments: Some(segments.len() as u32), + index_version, + index_details: idx_desc.details().ok(), }) }) .collect(); @@ -3405,10 +3408,12 @@ mod tests { assert_eq!(index.index_type, crate::index::IndexType::IvfPq); assert_eq!(index.columns, vec!["embeddings".to_string()]); assert!(index.index_uuid.is_some()); + assert!(index.type_url.is_some()); assert_eq!(index.num_segments, Some(1)); assert_eq!(index.num_indexed_rows, Some(512)); assert!(index.created_at.is_some()); assert!(index.size_bytes.is_some()); + assert!(index.index_version.is_some()); assert!(index.index_details.is_some()); assert_eq!(table.count_rows(None).await.unwrap(), 512); assert_eq!(table.name(), "test"); @@ -3762,10 +3767,12 @@ mod tests { // The richer metadata surfaced from describe_indices should be populated. assert!(index.index_uuid.is_some()); + assert!(index.type_url.is_some()); assert_eq!(index.num_segments, Some(1)); assert_eq!(index.num_indexed_rows, Some(1)); assert!(index.created_at.is_some()); assert!(index.size_bytes.is_some()); + assert!(index.index_version.is_some()); assert!(index.index_details.is_some()); let indices = table.as_native().unwrap().load_indices().await.unwrap();