我有一个简单的restapi,它是一个以FastAPI和mongodb为后端创建的书店(我使用了Motor
作为库,而不是Pymongo
)。我有一个GET
端点来获取数据库中的所有书籍,它还支持查询字符串(例如:用户可以搜索具有单个作者或流派类型的书籍等)。
下面是这个端点的对应代码:routers.py
@router.get("/books", response_model=List[models.AllBooksResponse])
async def get_the_list_of_all_books(
authors: Optional[str] = None,
genres: Optional[str] = None,
published_year: Optional[str] = None,
) -> List[Dict[str, Any]]:
if authors is None and genres is None and published_year is None:
all_books = [book for book in await mongo.BACKEND.get_all_books()]
else:
all_books = [
book
for book in await mongo.BACKEND.get_all_books(
authors=authors.strip('"').split(",") if authors is not None else None,
genres=genres.strip('"').split(",") if genres is not None else None,
published_year=datetime.strptime(published_year, "%Y")
if published_year is not None
else None,
)
]
return all_books
对应型号:
class AllBooksResponse(BaseModel):
name: str
author: str
link: Optional[str] = None
def __init__(self, name, author, **data):
super().__init__(
name=name, author=author, link=f"{base_uri()}book/{data['book_id']}"
)
以及获取数据的后端函数:
class MongoBackend:
def __init__(self, uri: str) -> None:
self._client = motor.motor_asyncio.AsyncIOMotorClient(uri)
async def get_all_books(
self,
authors: Optional[List[str]] = None,
genres: Optional[List[str]] = None,
published_year: Optional[datetime] = None,
) -> List[Dict[str, Any]]:
find_condition = {}
if authors is not None:
find_condition["author"] = {"$in": authors}
if genres is not None:
find_condition["genres"] = {"$in": genres}
if published_year is not None:
find_condition["published_year"] = published_year
cursor = self._client[DB][BOOKS_COLLECTION].find(find_condition, {"_id": 0})
return [doc async for doc in cursor]
现在我要实现这个端点的分页。这里我有几个问题:
limit
和skip
)对我有效,因为当我使用其他过滤器参数(例如author和genre)时,我也想让它工作,除非我进行第一次查询以获取数据,然后我想进行分页,否则我不可能知道ObjectId。但是这个问题在我看到的任何地方都存在,使用limit
和skip
都是不鼓励的。
这样的问题没有对错之分。很大程度上取决于您所使用的技术堆栈,以及您所拥有的上下文,同时考虑到您所编写的软件以及您所使用的软件(mongo)的未来发展方向。
回答您的问题:
fastapi
实现这一点似乎很简单:只需将参数limit: int = 10
和skip: int = 0
添加到get
函数中,并在数据库的过滤函数中使用它们Fastapi
将为您检查数据类型,而您可以检查限制是否为负或大于100。skip
函数没有很好的执行。因此,他认为第二种选择更好,只是为了表现。如果你有成百上千万的文档(比如amazon),那么,可能会使用一些不同的东西,尽管当你的网站发展到这样的程度时,我猜你会有足够的钱支付整个专家团队来整理这些东西,并可能开发你自己的数据库。最后,最常见的方法是limit
和skip
。它通常在数据库级别完成,以减少应用程序的工作量和带宽。
Mongo在跳过和限制结果方面效率不高。如果你的数据库有,比如说一百万个文档,那么我想你根本不会注意到。对于这样的工作负载,您甚至可以使用关系数据库。您可以随时对现有的选项进行基准测试,并选择最合适的选项。
我对mongo了解不多,但我知道,一般来说,索引可以帮助限制和跳过记录(本例中是文档),但我不确定mongo是否也是这样。