diff --git a/apps/api/src/repositories/knowledge_repository.py b/apps/api/src/repositories/knowledge_repository.py index 9cfda483..808403a3 100644 --- a/apps/api/src/repositories/knowledge_repository.py +++ b/apps/api/src/repositories/knowledge_repository.py @@ -190,7 +190,7 @@ class KnowledgeDBRepository: count_query = count_query.where(KnowledgeEntryRecord.status == status) if tags: for tag in tags: - tag_filter = KnowledgeEntryRecord.tags.op('@>')(f'["{tag}"]') + tag_filter = _json_string_array_has_tag(tag) query = query.where(tag_filter) count_query = count_query.where(tag_filter) if q: @@ -347,3 +347,18 @@ class KnowledgeDBRepository: created_at=record.created_at, updated_at=record.updated_at, ) + + +def _json_string_array_has_tag(tag: str): + """建立 JSON/JSONB 皆相容的 tag filter。 + + production 的 knowledge_entries.tags 目前是 JSON 欄位,不支援 json @> text。 + 這裡改用帶引號的字串比對,避免把 tag 片段誤判成完整 tag。 + """ + escaped = ( + tag + .replace("\\", "\\\\") + .replace("%", "\\%") + .replace("_", "\\_") + ) + return KnowledgeEntryRecord.tags.cast(String).ilike(f'%"{escaped}"%', escape="\\") diff --git a/apps/api/tests/test_hermes_kb_growth_worker.py b/apps/api/tests/test_hermes_kb_growth_worker.py index 4652d3ca..d43d0fac 100644 --- a/apps/api/tests/test_hermes_kb_growth_worker.py +++ b/apps/api/tests/test_hermes_kb_growth_worker.py @@ -74,6 +74,16 @@ def test_review_context_keeps_succeeded_at_owner_review_stage(): assert updated["worker_result"]["status"] == "draft_created" +def test_knowledge_tag_filter_is_json_column_compatible(): + """knowledge_entries.tags 是 JSON 欄位時不可使用 json @> text。""" + from src.repositories.knowledge_repository import _json_string_array_has_tag + + compiled = str(_json_string_array_has_tag("dispatch:abc-123")) + + assert "CAST(knowledge_entries.tags AS VARCHAR)" in compiled + assert "@>" not in compiled + + @pytest.mark.asyncio async def test_run_once_advances_pending_dispatch_to_review_draft(): """pending dispatch 應推進到 succeeded + waiting_owner_review read model。"""