fix(search): 将搜索历史持久化到应用数据库
This commit is contained in:
@@ -120,6 +120,13 @@ MIGRATIONS: list[str] = [
|
||||
PRIMARY KEY(job_id, revision, options_hash)
|
||||
);
|
||||
""",
|
||||
# v5: application-owned search history, shared by web and desktop clients.
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS search_history (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
query TEXT NOT NULL UNIQUE
|
||||
);
|
||||
""",
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -303,9 +303,24 @@ async def rename_note(note_id: str, request: NoteRenameRequest) -> Note:
|
||||
# Retrieval and chat
|
||||
@router.post("/search", response_model=SearchResponse, tags=["Search"])
|
||||
async def search_notes(request: SearchRequest) -> SearchResponse:
|
||||
from app.services import search_history
|
||||
search_history.record(request.query)
|
||||
return await engine.search(request)
|
||||
|
||||
|
||||
@router.get("/search/history", tags=["Search"])
|
||||
async def get_search_history() -> dict[str, list[str]]:
|
||||
from app.services import search_history
|
||||
return {"queries": search_history.list_queries()}
|
||||
|
||||
|
||||
@router.delete("/search/history", tags=["Search"])
|
||||
async def clear_search_history() -> dict[str, list[str]]:
|
||||
from app.services import search_history
|
||||
search_history.clear()
|
||||
return {"queries": []}
|
||||
|
||||
|
||||
@router.post(
|
||||
"/chat",
|
||||
response_class=StreamingResponse,
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
from contextlib import closing
|
||||
|
||||
from app.database.db import connect, transaction
|
||||
|
||||
|
||||
def list_queries():
|
||||
with closing(connect()) as conn:
|
||||
return [row['query'] for row in conn.execute('SELECT query FROM search_history ORDER BY id DESC LIMIT 10')]
|
||||
|
||||
|
||||
def record(query: str):
|
||||
query = query.strip()
|
||||
if not query:
|
||||
return
|
||||
with closing(connect()) as conn, transaction(conn):
|
||||
conn.execute('DELETE FROM search_history WHERE query=?', (query,))
|
||||
conn.execute('INSERT INTO search_history(query) VALUES (?)', (query,))
|
||||
conn.execute('DELETE FROM search_history WHERE id NOT IN (SELECT id FROM search_history ORDER BY id DESC LIMIT 10)')
|
||||
|
||||
|
||||
def clear():
|
||||
with closing(connect()) as conn, transaction(conn):
|
||||
conn.execute('DELETE FROM search_history')
|
||||
@@ -0,0 +1,22 @@
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.main import app
|
||||
from app.services import search_history
|
||||
|
||||
|
||||
def test_history_survives_new_clients_and_clear():
|
||||
with TestClient(app) as client:
|
||||
for query in ['first', 'second', ' first ']:
|
||||
assert client.post('/api/search', json={'query': query, 'mode': 'fts'}).status_code == 200
|
||||
assert client.get('/api/search/history').json() == {'queries': ['first', 'second']}
|
||||
with TestClient(app) as client:
|
||||
assert client.get('/api/search/history').json() == {'queries': ['first', 'second']}
|
||||
assert client.delete('/api/search/history').json() == {'queries': []}
|
||||
assert search_history.list_queries() == []
|
||||
|
||||
|
||||
def test_history_is_bounded_and_blank_queries_are_ignored():
|
||||
for number in range(12):
|
||||
search_history.record(str(number))
|
||||
search_history.record(' ')
|
||||
assert search_history.list_queries() == [str(number) for number in range(11, 1, -1)]
|
||||
Reference in New Issue
Block a user