mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
revert search service changes
This commit is contained in:
@@ -5,11 +5,12 @@ from sqlalchemy import DDL
|
||||
# Define FTS5 virtual table creation
|
||||
CREATE_SEARCH_INDEX = DDL("""
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS search_index USING fts5(
|
||||
content, -- Searchable text content
|
||||
permalink UNINDEXED, -- Link to entity/document (must be unique)
|
||||
title, -- Title for exact/fuzzy matching
|
||||
content, -- Additional searchable content
|
||||
permalink UNINDEXED, -- Link to entity/document
|
||||
file_path UNINDEXED, -- Filesystem path
|
||||
type UNINDEXED, -- 'entity' or 'document'
|
||||
metadata UNINDEXED, -- JSON with timestamps, types, etc.
|
||||
type UNINDEXED, -- entity type
|
||||
metadata UNINDEXED, -- Additional metadata
|
||||
tokenize='porter unicode61' -- Enable stemming + unicode
|
||||
);
|
||||
""")
|
||||
|
||||
@@ -8,8 +8,8 @@ from sqlalchemy import text
|
||||
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
|
||||
|
||||
from basic_memory import db
|
||||
from basic_memory.schemas.search import SearchQuery, SearchResult, SearchItemType
|
||||
from basic_memory.models.search import CREATE_SEARCH_INDEX
|
||||
from basic_memory.schemas.search import SearchQuery, SearchResult, SearchItemType
|
||||
|
||||
|
||||
class SearchRepository:
|
||||
@@ -27,46 +27,13 @@ class SearchRepository:
|
||||
async def search(
|
||||
self, query: SearchQuery, context: Optional[List[str]] = None
|
||||
) -> List[SearchResult]:
|
||||
"""Search across all indexed content using FTS5.
|
||||
|
||||
Uses a three-tier matching strategy:
|
||||
1. title:term matches (highest priority)
|
||||
2. Exact term matches (medium priority)
|
||||
3. Prefix matches (lowest priority)
|
||||
"""
|
||||
"""Search across all indexed content."""
|
||||
conditions = []
|
||||
params = {}
|
||||
|
||||
# Handle text search with fuzzy matching
|
||||
# Handle text search
|
||||
if query.text:
|
||||
# Prepare search terms
|
||||
search_terms = query.text.lower().split()
|
||||
params["search_terms"] = search_terms
|
||||
|
||||
# Build match conditions in priority order
|
||||
matches = []
|
||||
|
||||
# 1. Title field matches (highest weight)
|
||||
title_matches = [f'title:"{term}"' for term in search_terms]
|
||||
if len(search_terms) > 1:
|
||||
# Multi-word title match
|
||||
title_phrase = " ".join(f'title:"{term}"' for term in search_terms)
|
||||
title_matches.append(f'NEAR({title_phrase}, {len(search_terms)})')
|
||||
matches.extend(title_matches)
|
||||
|
||||
# 2. Exact word matches
|
||||
matches.extend([f'"{term}"' for term in search_terms])
|
||||
if len(search_terms) > 1:
|
||||
# Multi-word proximity match
|
||||
phrase = " ".join(f'"{term}"' for term in search_terms)
|
||||
matches.append(f'NEAR({phrase}, {len(search_terms) * 2})')
|
||||
|
||||
# 3. Prefix matches
|
||||
matches.extend([f'{term}*' for term in search_terms])
|
||||
|
||||
# Complete match expression
|
||||
match_expr = " OR ".join(matches)
|
||||
conditions.append(f"content MATCH '{match_expr}'")
|
||||
conditions.append(f"content MATCH '{query.text}'")
|
||||
|
||||
# Handle type filter
|
||||
if query.types:
|
||||
@@ -87,26 +54,16 @@ class SearchRepository:
|
||||
# Build WHERE clause
|
||||
where_clause = " AND ".join(conditions) if conditions else "1=1"
|
||||
|
||||
# Build SQL query
|
||||
sql = f"""
|
||||
WITH search_results AS (
|
||||
SELECT
|
||||
permalink,
|
||||
file_path,
|
||||
type,
|
||||
metadata,
|
||||
rank
|
||||
FROM search_index
|
||||
WHERE {where_clause}
|
||||
)
|
||||
SELECT
|
||||
permalink,
|
||||
file_path,
|
||||
type,
|
||||
metadata,
|
||||
rank as score
|
||||
FROM search_results
|
||||
ORDER BY rank ASC
|
||||
bm25(search_index) as score
|
||||
FROM search_index
|
||||
WHERE {where_clause}
|
||||
ORDER BY score DESC
|
||||
"""
|
||||
|
||||
logger.debug(f"Search query: {sql}")
|
||||
@@ -129,6 +86,7 @@ class SearchRepository:
|
||||
|
||||
async def index_item(
|
||||
self,
|
||||
title: str,
|
||||
content: str,
|
||||
permalink: str,
|
||||
file_path: str,
|
||||
@@ -170,4 +128,4 @@ class SearchRepository:
|
||||
text("DELETE FROM search_index WHERE permalink = :permalink"),
|
||||
{"permalink": permalink},
|
||||
)
|
||||
await session.commit()
|
||||
await session.commit()
|
||||
|
||||
@@ -50,12 +50,11 @@ class SearchService:
|
||||
async def index_entity(
|
||||
self, entity: Entity, background_tasks: Optional[BackgroundTasks] = None
|
||||
) -> None:
|
||||
"""Index an entity's core identity for fuzzy matching.
|
||||
"""Index an entity's content for search.
|
||||
|
||||
Core content:
|
||||
- Original title (lowercase)
|
||||
- Title with field marker (for weighted matching)
|
||||
- Path components as discrete terms
|
||||
Indexes:
|
||||
- Title in dedicated field for better match control
|
||||
- Path components in content field for findability
|
||||
"""
|
||||
# Build searchable content
|
||||
content_parts = []
|
||||
@@ -64,6 +63,7 @@ class SearchService:
|
||||
title = entity.title.lower()
|
||||
content_parts.extend([
|
||||
title, # Base title
|
||||
entity.summary or "",
|
||||
f"title:{title}", # Field marker for targeted matching
|
||||
*title.split(), # Individual words
|
||||
])
|
||||
@@ -88,6 +88,7 @@ class SearchService:
|
||||
if background_tasks:
|
||||
background_tasks.add_task(
|
||||
self._do_index,
|
||||
title=title,
|
||||
content=content,
|
||||
permalink=entity.permalink,
|
||||
file_path=entity.file_path,
|
||||
@@ -96,6 +97,7 @@ class SearchService:
|
||||
)
|
||||
else:
|
||||
await self._do_index(
|
||||
title=title,
|
||||
content=content,
|
||||
permalink=entity.permalink,
|
||||
file_path=entity.file_path,
|
||||
@@ -104,10 +106,17 @@ class SearchService:
|
||||
)
|
||||
|
||||
async def _do_index(
|
||||
self, content: str, permalink: str, file_path: str, type: SearchItemType, metadata: dict
|
||||
self,
|
||||
title: str,
|
||||
content: str,
|
||||
permalink: str,
|
||||
file_path: str,
|
||||
type: SearchItemType,
|
||||
metadata: dict,
|
||||
) -> None:
|
||||
"""Actually perform the indexing."""
|
||||
await self.repository.index_item(
|
||||
title=title,
|
||||
content=content,
|
||||
permalink=permalink,
|
||||
file_path=file_path,
|
||||
|
||||
Reference in New Issue
Block a user