diff --git a/modules/docs/lib.py b/modules/docs/lib.py index c3590d8d..e8174919 100644 --- a/modules/docs/lib.py +++ b/modules/docs/lib.py @@ -364,7 +364,7 @@ def _search_docs(search_str=None, author=None, subject=None, language=None, mime query = session.query(FileGroup).join(Doc, Doc.file_group_id == FileGroup.id) if search_str: - query = query.filter(FileGroup.textsearch.match(search_str)) + query = query.filter(FileGroup.textsearch.op('@@')(func.websearch_to_tsquery(search_str))) if author: query = query.filter(FileGroup.b_text.ilike(f'%{author}%')) diff --git a/modules/docs/test/test_api.py b/modules/docs/test/test_api.py index 01998dde..30fd4cb1 100644 --- a/modules/docs/test/test_api.py +++ b/modules/docs/test/test_api.py @@ -30,6 +30,17 @@ async def test_docs_search(async_client, test_session, test_directory, example_e assert len(response.json['file_groups']) >= 1 +@pytest.mark.asyncio +async def test_docs_search_multi_word(async_client, test_session, test_directory, example_epub, refresh_files): + """Docs search handles multi-word queries without tsquery syntax errors.""" + await refresh_files() + + # Multi-word search should not raise a syntax error. + content = dict(search_str='primitive fire starting friction methods') + request, response = await async_client.post('/api/docs/search', content=json.dumps(content)) + assert response.status_code == HTTPStatus.OK + + @pytest.mark.asyncio async def test_get_doc(async_client, test_session, test_directory, example_epub, refresh_files): """Single doc endpoint returns the doc."""