diff --git a/src/basic_memory/repository/sqlite_search_repository.py b/src/basic_memory/repository/sqlite_search_repository.py index eade25393..394bda989 100644 --- a/src/basic_memory/repository/sqlite_search_repository.py +++ b/src/basic_memory/repository/sqlite_search_repository.py @@ -779,8 +779,11 @@ async def _build_fts_query_parts( # Use _prepare_search_term to handle both Boolean and non-Boolean queries processed_text = self._prepare_search_term(search_text.strip()) params["text"] = processed_text + # content_stems is capped for Postgres index-row compatibility, while + # SQLite stores the complete note body in its FTS5 content_snippet column. match_conditions.append( - "(search_index.title MATCH :text OR search_index.content_stems MATCH :text)" + "(search_index.title MATCH :text OR search_index.content_stems MATCH :text " + "OR search_index.content_snippet MATCH :text)" ) # Handle title match search diff --git a/tests/repository/test_search_repository.py b/tests/repository/test_search_repository.py index d47a40ab5..52c2cae3c 100644 --- a/tests/repository/test_search_repository.py +++ b/tests/repository/test_search_repository.py @@ -253,6 +253,35 @@ async def test_index_item(search_repository, search_entity): assert results[0].project_id == search_repository.project_id +@pytest.mark.asyncio +async def test_sqlite_text_search_matches_full_content_snippet(search_repository, search_entity): + """SQLite finds terms beyond the Postgres-sized content_stems prefix (#1065).""" + if is_postgres_backend(search_repository): + pytest.skip("The full content_snippet FTS column is SQLite-specific") + + marker = "latecontentmarker" + search_row = SearchIndexRow( + id=search_entity.id, + type=SearchItemType.ENTITY.value, + title=search_entity.title, + content_stems="prefix content only", + content_snippet=f"{'x' * 7000} {marker}", + permalink=search_entity.permalink, + file_path=search_entity.file_path, + entity_id=search_entity.id, + metadata={"note_type": search_entity.note_type}, + created_at=search_entity.created_at, + updated_at=search_entity.updated_at, + project_id=search_repository.project_id, + ) + await search_repository.index_item(search_row) + + results = await search_repository.search(search_text=marker) + + assert [result.id for result in results] == [search_entity.id] + assert await search_repository.count(search_text=marker) == 1 + + @pytest.mark.asyncio async def test_index_item_upsert_on_duplicate_permalink(search_repository, search_entity): """Test that indexing the same permalink twice uses upsert instead of failing.