diff --git a/.vscode/settings.json b/.vscode/settings.json index c351126..7ccc5e4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,14 @@ { - "python.formatting.provider": "black", + "python.formatting.provider": "none", "[python]": { - "editor.defaultFormatter": null, + "editor.defaultFormatter": "ms-python.black-formatter", "editor.formatOnSave": true - } -} + }, + "cSpell.words": [ + "fastapi", + "ilike", + "outerjoin", + "sqlalchemy", + "toybox" + ] +} \ No newline at end of file diff --git a/cruds/works/__init__.py b/cruds/works/__init__.py index 932c186..406e41b 100644 --- a/cruds/works/__init__.py +++ b/cruds/works/__init__.py @@ -2,7 +2,9 @@ import markdown from fastapi import HTTPException -from sqlalchemy import desc, func, or_ +from sqlalchemy import desc, asc, func, or_, case +from sqlalchemy.orm import aliased +from sqlalchemy.sql.functions import coalesce, count from sqlalchemy.orm.session import Session from cruds.assets import delete_asset_by_id @@ -91,26 +93,106 @@ def get_works_by_limit( tag_names: str, tag_ids: str, user: Optional[User], - search_word:str, + search_word: str, + order_by: str = "created_at", + order: str = "asc", ) -> ResWorks: if tag_names != None and tag_ids != None: raise HTTPException( - status_code=422, detail="tag name and ID cannot be specified at the same time." + status_code=422, + detail="tag name and ID cannot be specified at the same time.", + ) + + comment_count_subquery = ( + db.query( + models.Comment.work_id, count(models.Comment.work_id).label("comment_count") + ) + .group_by(models.Comment.work_id) + .subquery("comment_count_subquery") + ) + favorite_count_subquery = ( + db.query( + models.Favorite.work_id, + count(models.Favorite.work_id).label("favorite_count"), + ) + .group_by(models.Favorite.work_id) + .subquery("favorite_count_subquery") + ) + is_favorite_subquery = None + if user is not None: + is_favorite_subquery = ( + db.query( + models.Favorite.work_id, + models.Favorite.user_id.label("is_favorite_flag"), + ) + .filter( + models.Work.id == models.Favorite.work_id, + models.Favorite.user_id == user.id, + ) + .subquery("is_favorite_subquery") ) works_orm = ( - db.query(models.Work) - .join(models.User,models.Work.user_id==models.User.id) - .join(models.Tagging,models.Work.id==models.Tagging.work_id) - .join(models.Tag,models.Tagging.tag_id==models.Tag.id) - .group_by(models.Work.id) - .order_by(desc(models.Work.created_at)) + db.query( + models.Work, + coalesce(favorite_count_subquery.c.favorite_count, 0).label( + "favorite_count" + ), + case( + [(is_favorite_subquery.c.is_favorite_flag == None, False)], else_=True + ).label("is_favorite") + if is_favorite_subquery is not None + else False, + coalesce(comment_count_subquery.c.comment_count, 0).label("comment_count"), + ) + .join(models.User, models.Work.user_id == models.User.id) + .join(models.Tagging, models.Work.id == models.Tagging.work_id) + .join(models.Tag, models.Tagging.tag_id == models.Tag.id) + # .group_by(models.Work.id) .filter(models.Work.visibility != models.Visibility.draft) ) - if search_word: - works_orm = works_orm.filter(or_(models.User.name.ilike(f"%{search_word}%"),models.Tag.name.ilike(f"%{search_word}%"),models.Work.title.ilike(f"%{search_word}%"))) + works_orm = works_orm.outerjoin( + favorite_count_subquery, models.Work.id == favorite_count_subquery.c.work_id + ) + works_orm = works_orm.outerjoin( + comment_count_subquery, models.Work.id == comment_count_subquery.c.work_id + ) + + if is_favorite_subquery is not None: + works_orm = works_orm.outerjoin( + is_favorite_subquery, models.Work.id == is_favorite_subquery.c.work_id + ) + + # order_byの指定 + if order_by == "created_at": + column_of_order_by = models.Work.created_at + elif order_by == "updated_at": + column_of_order_by = models.Work.updated_at + elif order_by == "favorite": + column_of_order_by = favorite_count_subquery.c.favorite_count + elif order_by == "comment": + column_of_order_by = comment_count_subquery.c.comment_count + else: + column_of_order_by = models.Work.created_at + + # orderの指定 + if order == "desc": + works_orm = works_orm.order_by(desc(column_of_order_by)) + else: + works_orm = works_orm.order_by(asc(column_of_order_by)) + + # search_by_free_word + if search_word: + works_orm = works_orm.filter( + or_( + models.User.name.ilike(f"%{search_word}%"), + models.Tag.name.ilike(f"%{search_word}%"), + models.Work.title.ilike(f"%{search_word}%"), + ) + ) + # search_by_tag_ids if tag_ids: tag_id_list = tag_ids.split(",") works_orm = works_orm.filter(models.Tagging.tag_id.in_(tag_id_list)).filter( @@ -119,6 +201,8 @@ def get_works_by_limit( works_orm = works_orm.group_by(models.Work.id).having( func.count(models.Work.id) == len(tag_id_list) ) + + # search_by_tag_names if tag_names: tag_name_list = tag_names.split(",") works_orm = works_orm.filter(models.Tag.name.in_(tag_name_list)).filter( @@ -128,13 +212,16 @@ def get_works_by_limit( func.count(models.Work.id) == len(tag_name_list) ) + # check user if user is None: works_orm = works_orm.filter(models.Work.visibility == models.Visibility.public) elif visibility is not None: works_orm = works_orm.filter(models.Work.visibility == visibility) + # get works total count works_total_count = works_orm.count() + # pagination if oldest_work_id: oldest_work = ( db.query(models.Work).filter(models.Work.id == oldest_work_id).first() @@ -149,27 +236,33 @@ def get_works_by_limit( if newest_work is None: raise HTTPException(status_code=400, detail="this newest_id is invalid") works_orm = works_orm.filter(models.Work.created_at < newest_work.created_at) + + # works_orm = works_orm.group_by(models.Work.id) + print(works_orm) works_orm = works_orm.limit(limit).all() - works = [] - for work_orm in works_orm: - work = Work.from_orm(work_orm) - if user is not None: - work.is_favorite = ( - db.query(models.Favorite) - .filter( - models.Favorite.work_id == work.id, - models.Favorite.user_id == user.id, - ) - .first() - is not None - ) - else: - work.is_favorite = False - work.favorite_count = ( - db.query(models.Favorite).filter(models.Favorite.work_id == work.id).count() + works = list( + map( + lambda work_orm: Work( + id=work_orm[0].id, + title=work_orm[0].title, + description=work_orm[0].description, + description_html=work_orm[0].description_html, + user=work_orm[0].user, + assets=work_orm[0].assets, + urls=work_orm[0].urls, + visibility=work_orm[0].visibility, + tags=work_orm[0].tags, + thumbnail=work_orm[0].thumbnail, + created_at=work_orm[0].created_at, + updated_at=work_orm[0].updated_at, + comments=work_orm[0].comments, + favorite_count=work_orm[1], + is_favorite=work_orm[2], + ), + works_orm, ) - works.append(work) + ) resWorks = ResWorks(works=works, works_total_count=works_total_count) return resWorks diff --git a/routers/works/__init__.py b/routers/works/__init__.py index f6f053c..15ef0d5 100644 --- a/routers/works/__init__.py +++ b/routers/works/__init__.py @@ -58,10 +58,12 @@ async def get_works( tag_ids:str = None, db: Session = Depends(get_db), user: User = Depends(GetCurrentUser(auto_error=False)), - search_word:str= None + search_word:str= None, + order_by: str = None, + order: str = None, ): works = get_works_by_limit( - db, limit, visibility, oldest_work_id, newest_work_id,tag_names, tag_ids, user,search_word + db, limit, visibility, oldest_work_id, newest_work_id,tag_names, tag_ids, user,search_word, order_by, order ) return works