#sea-orm #query #dto #pagination #sorting #query-parser #metadata

pebble_query

SeaOrm 的一个辅助库,用于解析执行查询,并返回包含查询元数据的标准 DTO

1 个不稳定版本

0.1.0 2023年10月14日

1061数据库接口

MIT 许可证

56KB
803

小石子查询

什么是 Pebble 查询

SeaOrm 的一个简单辅助库,用于解析(应用过滤器、分页和排序)、执行,并返回包含元数据的标准 DTO。

它做什么

  • 在 SeaOrm 的 Select<T> 上应用标准 DTO。
  • 自动按列字段和运算符进行过滤,
  • 按列字段排序,
  • 分页查询。
  • 返回包含分页信息的标准 DTO。

典型用例

  • 处理来自 API 客户端的复杂标准查询请求,例如来自 Web 前端的 JSON 请求,其中包含多个过滤器、排序和分页。

换句话说,Pebble Query 提供了一些可组合的通用函数,允许将相同的标准结构体喂给 SeaOrm,并期望结果被封装在标准结构体中,以便消费。它可以大大简化常见的查询,并且可以用于任何 SeaOrm 实体,而无需编写太多样板代码。

示例

对于一个示例数据库任务

  • 列出三位作者的所有书籍,并按包含 "sea" 的书名和1976年后出版的书籍进行过滤。按书名排序,每页返回10本书,从第2页开始。返回所有数据,并带有分页信息。

你可以这样做

1. 不使用 Pebble Query(伪代码)

一个典型的 SeaOrm DSL 查询看起来像这样,不易重用

let db = conn();
let book_ids: Vec<i32> = vec![1, 2, 3];

// construct query
let select: Select<Book> = book::Entity::find()
   .inner_join(author::Entity)
   .filter(
    Expr::col((document::Entity, document::Column::Id)).is_in(book_ids).into_condition() // filter by book ids
   )
   .filter(
    Expr::col((book::Entity, book::Column::Title)) // contains "sea"
        .contains("sea")
            .and(Expr::col((book::Entity, book::Column::PublicationYear)) // published after 1976
                .gt(1976))
    )
    .order_by_asc(book::Column::Title)
    .offset(10)
    .limit(10);

let result: Vec<book::Model> = select.clone().all(db).await?;

// another trip to the database to get the pagination information such as total number of items and pages.
let pagination_info = select
   .paginate(db, 10)
   .num_items_and_pages().await?;

2. 使用 Pebble Query

为了使用 Pebble Query,对于每个实体,你需要重新编写查询字段名称和 SeaOrm 列之间的映射器。例如

let book_column_map: HashMap<String, (book::Entity, book::Entity::Column)> = std::collections::HashMap::from([
                       ("id".to_string(), (book::Entity, book::Column::Id)),
                       ("title".to_string(), (book::Entity, book::Column::Title)),
                       ("publication_year".to_string(), (book::Entity, book::Column::PublicationYear)),
                       // and so on to map all the fields you want to be able to query
                   ].into_iter().map(|(k, v)| (k.to_string(), v)).collect();
// then you can populate the standard SearchQuery DTO struct with the same query as above. Our example below is handwritten, but usually is generated and fed to SeaOrm backend. In fact, you can simply provide None for the `SearchQuery` parameter to Pebble Query and it will return all the results filtered by your initial `Select<T>`.
let query = SearchQuery {
            sort: Some(SearchSortOption {
                field: "title".to_string(),
                order: SortDirection::Asc as i32,
            }),
            offset: 10,
            length: 10,
            page: 0,
            filter: Some(SearchFilter {
                must: vec![
                    SearchCondition {
                        field: "title".to_string(),
                            operator: SearchOperator::Contains as i32,
                        value: Some("sea".to_string()),
                        ..Default::default()
                    },
                    SearchCondition {
                            field: "publication_year".to_string(),
                            operator: SearchOperator::GreaterThan as i32,
                            value: Some("1976".to_string()),
                            ..Default::default()
                    },
                ],
                ..Default::default()
            }),
            ..Default::default()
        };

然后你可以这样做

let db = conn();
let book_ids: Vec<i32> = vec![1, 2, 3];
let select: Select<Book> = book::Entity::find()
   .inner_join(author::Entity)
   .filter(
    Expr::col((document::Entity, document::Column::Id)).is_in(book_ids).into_condition() // filter by book ids
   );
let results: PebbleQueryResult<book::Entity> = use_pebble_query(select, query, &BOOK_COLUMN_MAP, db).await?;

或者使用流畅语法

let db = conn();
let result = book::Entity::find()
   .inner_join(author::Entity)
   .filter(
    Expr::col((document::Entity, document::Column::Id)).is_in(doc_ids).into_condition()
   )
   .pebble_query(query, &BOOK_COLUMN_MAP, db).await?; // add this to your existing `Select`.

查询结果,带有分页信息,将返回到这些结构体中,以便返回到前端。

// The result will contain the following information:
pub struct PebbleQueryResult<T: EntityTrait> {
    pub metadata: SearchResultMetadata,
    pub results: Vec<T::Model>,
}

pub struct SearchResultMetadata {
    pub result_items: i32,
    pub offset: i32,
    pub length: i32,
    pub page: i32,
    pub result_total_pages: i32,
    pub result_total_items: i32,
    pub query: Option<SearchQuery>,
    pub filter_count: Option<i32>,
    pub filter_reason: Option<String>,
}

所有这些都是可重用的。对于大多数涉及 book 实体的业务逻辑类型,你只需简单地将 .pebble_query, &BOOK_COLUMN_MAP, db).await?附加到现有的 SeaOrm Select,并期望获得相同结构的结果。

对于新实体,您只需编写一个新的映射器。

一些其他的中间实用方法也被暴露出来,例如将 SearchCondition 转换为 SeaOrm Condition,您可以使用这些方法来减少样板代码。

注意

  1. 这不是最完善的库。 请在审查代码并进行必要的更改后,再在生产环境中使用
  2. 您可以删除示例 Struct 并使用您自己的 DTOs。 prost 依赖仅用于示例 DTOs,这两个都可以被移除。

依赖项

~32–44MB
~754K SLoC