当前位置: 首页 > 工具软件 > Mongodb Motor > 使用案例 >

如何用mongo db(Motor)实现fastapi分页

乔俊才
2023-12-01

mongodb pagination pymongo restapi fastapi

我有一个简单的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]

现在我要实现这个端点的分页。这里我有几个问题:

  1. 在数据库级或应用程序级进行分页是好的吗?
  2. 我们有一些现成的库可以帮助我在fastapi中做到这一点吗?我查看了文档中的https://pypi.org/project/fastapi-pagination/,但这似乎更针对SQL数据库
  3. 我还查看了这个链接:https://www.codementor.io/@arpitbhayani/fast-and-efficient-pagination-in-mongodb-9095flbqr,它讨论了在mongodb中执行此操作的不同方法,但我认为只有第一个选项(使用limitskip)对我有效,因为当我使用其他过滤器参数(例如author和genre)时,我也想让它工作,除非我进行第一次查询以获取数据,然后我想进行分页,否则我不可能知道ObjectId。

但是这个问题在我看到的任何地方都存在,使用limitskip都是不鼓励的。

这样的问题没有对错之分。很大程度上取决于您所使用的技术堆栈,以及您所拥有的上下文,同时考虑到您所编写的软件以及您所使用的软件(mongo)的未来发展方向。

回答您的问题:

  1. 这取决于您必须管理的负载和您使用的dev堆栈。通常它是在数据库级别完成的,因为检索前110并删除前100是非常愚蠢和消耗资源的(数据库将为您完成)。
  2. 在我看来,如何通过fastapi实现这一点似乎很简单:只需将参数limit: int = 10skip: int = 0添加到get函数中,并在数据库的过滤函数中使用它们Fastapi将为您检查数据类型,而您可以检查限制是否为负或大于100。
  3. 它说没有银弹,因为mongo的skip函数没有很好的执行。因此,他认为第二种选择更好,只是为了表现。如果你有成百上千万的文档(比如amazon),那么,可能会使用一些不同的东西,尽管当你的网站发展到这样的程度时,我猜你会有足够的钱支付整个专家团队来整理这些东西,并可能开发你自己的数据库。

最后,最常见的方法是limitskip。它通常在数据库级别完成,以减少应用程序的工作量和带宽。

Mongo在跳过和限制结果方面效率不高。如果你的数据库有,比如说一百万个文档,那么我想你根本不会注意到。对于这样的工作负载,您甚至可以使用关系数据库。您可以随时对现有的选项进行基准测试,并选择最合适的选项。

我对mongo了解不多,但我知道,一般来说,索引可以帮助限制和跳过记录(本例中是文档),但我不确定mongo是否也是这样。

 

 类似资料: