use std::sync::atomic::Ordering;
use anyhow::{Context, Result};
use lancedb::index::scalar::FtsIndexBuilder;
use lancedb::index::Index;
use lancedb::Table;
use super::VectorStore;
impl VectorStore {
pub async fn ensure_fts_index(&self) -> Result<()> {
if self.fts_indexed.load(Ordering::Relaxed) {
return Ok(());
}
let table = match &self.table {
Some(t) => t,
None => return Ok(()),
};
if Self::has_content_fts_index(table).await {
self.fts_indexed.store(true, Ordering::Relaxed);
return Ok(());
}
crate::operational_metrics::record_fts_build_attempt();
self.fts_build_attempts.fetch_add(1, Ordering::Relaxed);
let result = table
.create_index(&["content"], Index::FTS(FtsIndexBuilder::default()))
.replace(false)
.execute()
.await;
match result {
Ok(()) => {}
Err(_) => {
}
}
self.fts_indexed.store(true, Ordering::Relaxed);
Ok(())
}
#[cfg(test)]
pub(crate) fn fts_build_attempts(&self) -> u64 {
self.fts_build_attempts.load(Ordering::Relaxed)
}
pub(super) async fn has_content_fts_index(table: &Table) -> bool {
table
.list_indices()
.await
.map(|indices| {
indices.iter().any(|index| {
index.index_type == lancedb::index::IndexType::FTS
&& index.columns.iter().any(|c| c == "content")
})
})
.unwrap_or(false)
}
pub async fn rebuild_fts_index(&self) -> Result<()> {
let table = match &self.table {
Some(t) => t,
None => return Ok(()),
};
crate::operational_metrics::record_fts_build_attempt();
self.fts_build_attempts.fetch_add(1, Ordering::Relaxed);
table
.create_index(&["content"], Index::FTS(FtsIndexBuilder::default()))
.replace(true)
.execute()
.await
.context("Failed to (re)build FTS index")?;
crate::operational_metrics::record_fts_rebuild();
self.fts_indexed.store(true, Ordering::Relaxed);
Ok(())
}
}