Upgrade fts index for phrase queries

This commit is contained in:
Yiorgis Gozadinos 2025-09-18 18:00:46 +03:00
parent cf8b87246b
commit c221a35547
No known key found for this signature in database
4 changed files with 56 additions and 24 deletions

View file

@ -118,8 +118,10 @@ class Store:
self.chunks_table = self.db.open_table("chunks") self.chunks_table = self.db.open_table("chunks")
else: else:
self.chunks_table = self.db.create_table("chunks", schema=self.ChunkRecord) self.chunks_table = self.db.create_table("chunks", schema=self.ChunkRecord)
# Create FTS index on the new table # Create FTS index on the new table with phrase query support
self.chunks_table.create_fts_index("content", replace=True) self.chunks_table.create_fts_index(
"content", replace=True, with_position=True, remove_stop_words=False
)
# Create or get settings table # Create or get settings table
if "settings" in existing_tables: if "settings" in existing_tables:
@ -222,8 +224,10 @@ class Store:
self.ChunkRecord = create_chunk_model(self.embedder._vector_dim) self.ChunkRecord = create_chunk_model(self.embedder._vector_dim)
self.chunks_table = self.db.create_table("chunks", schema=self.ChunkRecord) self.chunks_table = self.db.create_table("chunks", schema=self.ChunkRecord)
# Create FTS index on the new table # Create FTS index on the new table with phrase query support
self.chunks_table.create_fts_index("content", replace=True) self.chunks_table.create_fts_index(
"content", replace=True, with_position=True, remove_stop_words=False
)
def close(self): def close(self):
"""Close the database connection.""" """Close the database connection."""

View file

@ -28,7 +28,9 @@ class ChunkRepository:
def _ensure_fts_index(self) -> None: def _ensure_fts_index(self) -> None:
"""Ensure FTS index exists on the content column.""" """Ensure FTS index exists on the content column."""
try: try:
self.store.chunks_table.create_fts_index("content", replace=True) self.store.chunks_table.create_fts_index(
"content", replace=True, with_position=True, remove_stop_words=False
)
except Exception as e: except Exception as e:
# Log the error but don't fail - FTS might already exist # Log the error but don't fail - FTS might already exist
logger.debug(f"FTS index creation skipped: {e}") logger.debug(f"FTS index creation skipped: {e}")
@ -236,8 +238,10 @@ class ChunkRepository:
self.store.chunks_table = self.store.db.create_table( self.store.chunks_table = self.store.db.create_table(
"chunks", schema=self.store.ChunkRecord "chunks", schema=self.store.ChunkRecord
) )
# Create FTS index on the new table # Create FTS index on the new table with phrase query support
self.store.chunks_table.create_fts_index("content", replace=True) self.store.chunks_table.create_fts_index(
"content", replace=True, with_position=True, remove_stop_words=False
)
async def delete_by_document_id(self, document_id: str) -> bool: async def delete_by_document_id(self, document_id: str) -> bool:
"""Delete all chunks for a document.""" """Delete all chunks for a document."""

View file

@ -1,5 +1,4 @@
from __future__ import annotations import logging
from collections.abc import Callable from collections.abc import Callable
from dataclasses import dataclass from dataclasses import dataclass
@ -7,6 +6,8 @@ from packaging.version import Version, parse
from haiku.rag.store.engine import Store from haiku.rag.store.engine import Store
logger = logging.getLogger(__name__)
@dataclass @dataclass
class Upgrade: class Upgrade:
@ -33,14 +34,27 @@ def run_pending_upgrades(store: Store, from_version: str, to_version: str) -> No
if highest_step_version > v_to: if highest_step_version > v_to:
v_to = highest_step_version v_to = highest_step_version
# Ensure upgrades are applied in ascending version order # Determine applicable steps
for step in sorted(upgrades, key=lambda u: parse(u.version)): sorted_steps = sorted(upgrades, key=lambda u: parse(u.version))
v_step = parse(step.version) applicable = [s for s in sorted_steps if v_from < parse(s.version) <= v_to]
if v_from < v_step <= v_to: if applicable:
step.apply(store) logger.info("%d upgrade step(s) pending", len(applicable))
# Apply in ascending order
for idx, step in enumerate(applicable, start=1):
logger.info(
"Applying upgrade %s: %s (%d/%d)",
step.version,
step.description or "",
idx,
len(applicable),
)
step.apply(store)
logger.info("Completed upgrade %s", step.version)
# Import concrete upgrade modules (module names cannot start with a digit) from .v0_9_3 import upgrade_fts_phrase as upgrade_0_9_3_fts # noqa: E402
from .v0_9_3 import upgrade_order as upgrade_0_9_3_order # noqa: E402 from .v0_9_3 import upgrade_order as upgrade_0_9_3_order # noqa: E402
upgrades.append(upgrade_0_9_3_order) upgrades.append(upgrade_0_9_3_order)
upgrades.append(upgrade_0_9_3_fts)

View file

@ -1,15 +1,10 @@
from __future__ import annotations
import json import json
from typing import TYPE_CHECKING
from lancedb.pydantic import LanceModel, Vector from lancedb.pydantic import LanceModel, Vector
from pydantic import Field from pydantic import Field
from . import Upgrade from haiku.rag.store.engine import Store
from haiku.rag.store.upgrades import Upgrade
if TYPE_CHECKING: # pragma: no cover - for type hints only
from haiku.rag.store.engine import Store
def _infer_vector_dim(store: Store) -> int: def _infer_vector_dim(store: Store) -> int:
@ -32,7 +27,6 @@ def _apply_chunk_order(store: Store) -> None:
vector_dim = _infer_vector_dim(store) vector_dim = _infer_vector_dim(store)
# ============== Chunks: add 'order' column and backfill ==============
class ChunkRecordV2(LanceModel): class ChunkRecordV2(LanceModel):
id: str id: str
document_id: str document_id: str
@ -88,7 +82,6 @@ def _apply_chunk_order(store: Store) -> None:
pass pass
store.chunks_table = store.db.create_table("chunks", schema=ChunkRecordV2) store.chunks_table = store.db.create_table("chunks", schema=ChunkRecordV2)
# Recreate FTS index on content
store.chunks_table.create_fts_index("content", replace=True) store.chunks_table.create_fts_index("content", replace=True)
if new_chunk_records: if new_chunk_records:
@ -100,3 +93,20 @@ upgrade_order = Upgrade(
apply=_apply_chunk_order, apply=_apply_chunk_order,
description="Add 'order' column to chunks and backfill from metadata", description="Add 'order' column to chunks and backfill from metadata",
) )
def _apply_fts_phrase_support(store: Store) -> None:
"""Recreate FTS index with phrase query support and no stop-word removal."""
try:
store.chunks_table.create_fts_index(
"content", replace=True, with_position=True, remove_stop_words=False
)
except Exception:
pass
upgrade_fts_phrase = Upgrade(
version="0.9.3",
apply=_apply_fts_phrase_support,
description="Enable FTS phrase queries (with positions) and keep stop-words",
)