FastAPI查询参数转换为MongoDB聚合

huangapple go评论57阅读模式
英文:

FastAPI query parameter conversion to MongoDB aggregation

问题

我已经构建了一个Flask Web应用程序,用于列出我在吉他上演奏的歌曲。

现在我正在将所有的MongoDB聚合从Flask迁移到FastAPI服务中。其中一个功能允许我根据类别和/或熟练度筛选歌曲,从而生成一个查询字符串,该查询字符串被解释为字典并传递到聚合的$match阶段,前面还有一个重要的'songs.'父键。

问题
我已经在FastAPI中复制了这个功能,使用一系列条件(如下所示)。它可以正常工作,但我想知道是否有更优雅的方法来创建'$match',比如:

  • 计算参数的数量,然后将它们作为字典注入到聚合中
  • 在聚合内部进行循环

我已经阅读了FastAPI关于参数的文档,但它只涵盖了定义和验证参数,没有涉及到这个问题。

FastAPI路由

@api.get("/songsets/{username}/songs", response_description="列出用户的所有歌曲")
async def list_songs(username, category: str | None = None, proficiency: int | None = None):

    if category or proficiency:
        if category and not proficiency:
            songs_pipeline =[
                { "$match" : { "user_name" : username } },
                { "$unwind" : "$songs"},
                { "$match" :
                    {"songs.category" : category }
                },
                { "$project" : { "_id" : 0,
                    "song" : "$songs.song",
                    "artist" : "$songs.artist",
                    }
                }
            ]
        elif proficiency and not category:
            songs_pipeline =[
                { "$match" : { "user_name" : username } },
                { "$unwind" : "$songs"},
                { "$match" :[
                    {"songs.category" : category },
                    {"songs.proficiency" : proficiency }
                    ]
                },
                { "$project" : { "_id" : 0,
                    "song" : "$songs.song",
                    "artist" : "$songs.artist"
                    }
                }
            ]
        elif proficiency and category:
            songs_pipeline =[
                { "$match" : { "user_name" : username } },
                { "$unwind" : "$songs"},
                { "$match" :
                    {"songs.proficiency" : proficiency }
                },
                { "$project" : { "_id" : 0,
                    "song" : "$songs.song",
                    "artist" : "$songs.artist",
                    }
                }
            ]
        
        songs = await db["userSongSet_dev"].aggregate(songs_pipeline).to_list(100)
        return songs
英文:

I have built a Flask web app that lists Songs I play on Guitar.

Now I am in the process of migrating all of my MongoDB aggregations from Flask, into a FastAPI service. One of the features allows me to filter songs (based on category and/or proficiency), thus generating a querystring, which was interpreted as a dictionary and passed into the $match stage of the aggregation, with an all-important 'songs.' parent preceding the keys.

Question
I have replicated the feature in FastAPI using a series of conditions (below). It works ok, but I'm wondering if there's a more elegant way of creating the '$match'? i.e by:

  • Counting the number of params and then injecting them as a dictionary into the aggregation
  • Looping within the aggregation

I have read the FastAPI documentation about params, but it only really covers defining and validating them.

Fast API route

@api.get("/songsets/{username}/songs", response_description="List all songs for users")
async def list_songs(username, category: str | None = None,  proficiency: int | None = None):

    if category or proficiency:
        if category and not proficiency:
            songs_pipeline =[
                { "$match" : { "user_name" : username } },
                { "$unwind" : "$songs"},
                    { "$match" :
                                {"songs.category" : category }
                        },
                { "$project" : { "_id" : 0,
                    "song" : "$songs.song",
                    "artist" : "$songs.artist",
                    }
                    }
                ]
        elif proficiency and not category:
            songs_pipeline =[
                { "$match" : { "user_name" : username } },
                { "$unwind" : "$songs"},
                    { "$match" :[
                            {"songs.category" : category },
                            {"songs.proficiency" : proficiency }
                            ]
                        },
                { "$project" : { "_id" : 0,
                    "song" : "$songs.song",
                    "artist" : "$songs.artist"
                    }
                    }
                ]
        elif proficiency and category:
            songs_pipeline =[
                { "$match" : { "user_name" : username } },
                { "$unwind" : "$songs"},
                    { "$match" :
                                {"songs.proficiency" : proficiency }
                        },
                { "$project" : { "_id" : 0,
                    "song" : "$songs.song",
                    "artist" : "$songs.artist",
                    }
                    }
                ]
        
        songs = await db["userSongSet_dev"].aggregate(songs_pipeline).to_list(100)
        return songs

</details>


# 答案1
**得分**: 0

我认为查询看起来不错

并且查询中有一些重复的关键词这个怎么样

```python
@api.get("/songsets/{username}/songs", response_description="列出用户的所有歌曲")
async def list_songs(username, category: str | None = None, proficiency: int | None = None):

    songs_pipeline = [
        { "$match" : { "user_name" : username } }, 
        { "$unwind" : "$songs"},
        { "$project" : { 
            "_id" : 0,
            "song" : "$songs.song",
            "artist" : "$songs.artist",
            }
        }
    ]
    math_position: int = 2
    
    if category or proficiency:
        if category and not proficiency:
            # 在$unwind之后确保位置正确
            songs_pipeline.insert(math_position, { "$match" : {"songs.category" : 'category' } })
        elif proficiency and not category:
            songs_pipeline.insert(math_position, { "$match" :[
                            {"songs.category" : category },
                            {"songs.proficiency" : proficiency }
                            ]
                        })
            
        elif proficiency and category:
            songs_pipeline.insert(math_position, { "$match" : {"songs.proficiency" : proficiency })
            
        songs = await db["userSongSet_dev"].aggregate(songs_pipeline).to_list(100)
        return songs
英文:

i think the query looks good.

and there is some repetition key of the query,
how about this?

@api.get(&quot;/songsets/{username}/songs&quot;, response_description=&quot;List all songs for users&quot;)
async def list_songs(username, category: str | None = None,  proficiency: int | None = None):
songs_pipeline = [
{ &quot;$match&quot; : { &quot;user_name&quot; : username } }, 
{ &quot;$unwind&quot; : &quot;$songs&quot;},
{ &quot;$project&quot; : { 
&quot;_id&quot; : 0,
&quot;song&quot; : &quot;$songs.song&quot;,
&quot;artist&quot; : &quot;$songs.artist&quot;,
}
}
]
math_position: int = 2
if category or proficiency:
if category and not proficiency:
//ensure the position below after $unwind
songs_pipeline.insert(math_position, { &quot;$match&quot; : {&quot;songs.category&quot; : &#39;category&#39; } })
elif proficiency and not category:
songs_pipeline.insert(math_position, { &quot;$match&quot; :[
{&quot;songs.category&quot; : category },
{&quot;songs.proficiency&quot; : proficiency }
]
})
elif proficiency and category:
songs_pipeline.insert(math_position, { &quot;$match&quot; : {&quot;songs.proficiency&quot; : proficiency } })
songs = await db[&quot;userSongSet_dev&quot;].aggregate(songs_pipeline).to_list(100)
return songs

huangapple
  • 本文由 发表于 2023年3月3日 18:54:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/75626144.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定