From 86ca214dd8c9d80975ecd4b32ee87b7cf6bec4dd Mon Sep 17 00:00:00 2001 From: jungwoo choi Date: Mon, 13 Oct 2025 16:53:09 +0900 Subject: [PATCH] feat: Add source_keyword-based article queries for dynamic outlet articles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add get_articles_by_source_keyword method to query articles by entities - Search across entities.people, entities.organizations, and entities.groups - Deprecate get_articles_by_ids method in favor of dynamic queries - Support pagination for outlet article listings 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../backend/app/services/article_service.py | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/services/news-api/backend/app/services/article_service.py b/services/news-api/backend/app/services/article_service.py index adc6016..9face54 100644 --- a/services/news-api/backend/app/services/article_service.py +++ b/services/news-api/backend/app/services/article_service.py @@ -145,3 +145,71 @@ class ArticleService: categories.append(doc["_id"]) return categories + + @staticmethod + async def get_articles_by_ids(language: str, article_ids: List[str]) -> List[Article]: + """여러 ID로 기사 조회 (Deprecated - use get_articles_by_source_keyword)""" + collection = get_collection(language) + + if not article_ids: + return [] + + try: + # Convert string IDs to ObjectIds + object_ids = [ObjectId(aid) for aid in article_ids if ObjectId.is_valid(aid)] + + cursor = collection.find({"_id": {"$in": object_ids}}) + + articles = [] + async for doc in cursor: + doc["_id"] = str(doc["_id"]) + articles.append(Article(**doc)) + + return articles + except Exception as e: + print(f"Error fetching articles by IDs: {e}") + return [] + + @staticmethod + async def get_articles_by_source_keyword( + language: str, + source_keyword: str, + page: int = 1, + page_size: int = 20 + ) -> ArticleList: + """source_keyword로 기사 조회 (동적 쿼리) - entities 필드를 사용하여 검색""" + collection = get_collection(language) + + # Query by source_keyword in multiple places: + # 1. Direct source_keyword field (for migrated articles) + # 2. entities.people, entities.organizations, entities.groups (for existing articles) + query = { + "$or": [ + {"source_keyword": source_keyword}, + {"entities.people": source_keyword}, + {"entities.organizations": source_keyword}, + {"entities.groups": source_keyword} + ] + } + + # 전체 개수 + total = await collection.count_documents(query) + + # 페이지네이션 + skip = (page - 1) * page_size + cursor = collection.find(query).sort("created_at", -1).skip(skip).limit(page_size) + + articles = [] + async for doc in cursor: + doc["_id"] = str(doc["_id"]) + articles.append(Article(**doc)) + + total_pages = (total + page_size - 1) // page_size + + return ArticleList( + total=total, + page=page, + page_size=page_size, + total_pages=total_pages, + articles=articles + )