英文:
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("/songsets/{username}/songs", response_description="List all songs for users")
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:
//ensure the position below after $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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论