Add cursor pagination desc order (#2037)
* feat: add cursor pagination desc order * feat: add cursor pagination desc order * test: add cursor integration tests * test: add cursor integration tests * test: add query cursor_by tests * test: add query cursor_by tests * test: add query cursor_by tests * test: add query cursor_by tests * test: add query cursor_by tests * test: add query cursor_by sort tests * chore: remove unused import
This commit is contained in:
parent
5f9c450027
commit
c5adb9d1d1
File diff suppressed because it is too large
Load Diff
@ -11,7 +11,7 @@ use serde_json::json;
|
||||
feature = "sqlx-sqlite",
|
||||
feature = "sqlx-postgres"
|
||||
))]
|
||||
async fn main() -> Result<(), DbErr> {
|
||||
async fn cursor_tests() -> Result<(), DbErr> {
|
||||
let ctx = TestContext::new("cursor_tests").await;
|
||||
create_tables(&ctx.db).await?;
|
||||
create_insert_default(&ctx.db).await?;
|
||||
@ -103,6 +103,54 @@ pub async fn cursor_pagination(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||
]
|
||||
);
|
||||
|
||||
// Before 5 DESC, i.e. id > 5
|
||||
|
||||
let mut cursor = Entity::find().cursor_by(Column::Id);
|
||||
|
||||
cursor.before(5).desc();
|
||||
|
||||
assert_eq!(
|
||||
cursor.first(4).all(db).await?,
|
||||
[
|
||||
Model { id: 10 },
|
||||
Model { id: 9 },
|
||||
Model { id: 8 },
|
||||
Model { id: 7 },
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cursor.first(5).all(db).await?,
|
||||
[
|
||||
Model { id: 10 },
|
||||
Model { id: 9 },
|
||||
Model { id: 8 },
|
||||
Model { id: 7 },
|
||||
Model { id: 6 },
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cursor.last(4).all(db).await?,
|
||||
[
|
||||
Model { id: 9 },
|
||||
Model { id: 8 },
|
||||
Model { id: 7 },
|
||||
Model { id: 6 },
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cursor.last(5).all(db).await?,
|
||||
[
|
||||
Model { id: 10 },
|
||||
Model { id: 9 },
|
||||
Model { id: 8 },
|
||||
Model { id: 7 },
|
||||
Model { id: 6 },
|
||||
]
|
||||
);
|
||||
|
||||
// After 5, i.e. id > 5
|
||||
|
||||
let mut cursor = Entity::find().cursor_by(Column::Id);
|
||||
@ -173,6 +221,62 @@ pub async fn cursor_pagination(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||
]
|
||||
);
|
||||
|
||||
// After 5 DESC, i.e. id < 5
|
||||
|
||||
let mut cursor = Entity::find().cursor_by(Column::Id);
|
||||
|
||||
cursor.after(5).desc();
|
||||
|
||||
assert_eq!(
|
||||
cursor.first(3).all(db).await?,
|
||||
[Model { id: 4 }, Model { id: 3 }, Model { id: 2 },]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cursor.first(4).all(db).await?,
|
||||
[
|
||||
Model { id: 4 },
|
||||
Model { id: 3 },
|
||||
Model { id: 2 },
|
||||
Model { id: 1 },
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cursor.first(5).all(db).await?,
|
||||
[
|
||||
Model { id: 4 },
|
||||
Model { id: 3 },
|
||||
Model { id: 2 },
|
||||
Model { id: 1 },
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cursor.last(3).all(db).await?,
|
||||
[Model { id: 3 }, Model { id: 2 }, Model { id: 1 },]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cursor.last(4).all(db).await?,
|
||||
[
|
||||
Model { id: 4 },
|
||||
Model { id: 3 },
|
||||
Model { id: 2 },
|
||||
Model { id: 1 },
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cursor.last(5).all(db).await?,
|
||||
[
|
||||
Model { id: 4 },
|
||||
Model { id: 3 },
|
||||
Model { id: 2 },
|
||||
Model { id: 1 },
|
||||
]
|
||||
);
|
||||
|
||||
// Between 5 and 8, i.e. id > 5 AND id < 8
|
||||
|
||||
let mut cursor = Entity::find().cursor_by(Column::Id);
|
||||
@ -203,6 +307,64 @@ pub async fn cursor_pagination(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||
[Model { id: 6 }, Model { id: 7 }]
|
||||
);
|
||||
|
||||
// Between 8 and 5 DESC, i.e. id < 8 AND id > 5
|
||||
|
||||
let mut cursor = Entity::find().cursor_by(Column::Id);
|
||||
|
||||
cursor.after(8).before(5).desc();
|
||||
|
||||
assert_eq!(cursor.first(1).all(db).await?, [Model { id: 7 }]);
|
||||
|
||||
assert_eq!(
|
||||
cursor.first(2).all(db).await?,
|
||||
[Model { id: 7 }, Model { id: 6 }]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cursor.first(3).all(db).await?,
|
||||
[Model { id: 7 }, Model { id: 6 }]
|
||||
);
|
||||
|
||||
assert_eq!(cursor.last(1).all(db).await?, [Model { id: 6 }]);
|
||||
|
||||
assert_eq!(
|
||||
cursor.last(2).all(db).await?,
|
||||
[Model { id: 7 }, Model { id: 6 }]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cursor.last(3).all(db).await?,
|
||||
[Model { id: 7 }, Model { id: 6 }]
|
||||
);
|
||||
|
||||
// Ensure asc/desc order can be changed
|
||||
|
||||
let mut cursor = Entity::find().cursor_by(Column::Id);
|
||||
|
||||
cursor.first(2);
|
||||
|
||||
assert_eq!(cursor.all(db).await?, [Model { id: 1 }, Model { id: 2 },]);
|
||||
|
||||
assert_eq!(
|
||||
cursor.asc().all(db).await?,
|
||||
[Model { id: 1 }, Model { id: 2 },]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cursor.desc().all(db).await?,
|
||||
[Model { id: 10 }, Model { id: 9 },]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cursor.asc().all(db).await?,
|
||||
[Model { id: 1 }, Model { id: 2 },]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cursor.desc().all(db).await?,
|
||||
[Model { id: 10 }, Model { id: 9 },]
|
||||
);
|
||||
|
||||
// Fetch custom struct
|
||||
|
||||
#[derive(FromQueryResult, Debug, PartialEq, Clone)]
|
||||
@ -210,6 +372,10 @@ pub async fn cursor_pagination(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||
id: i32,
|
||||
}
|
||||
|
||||
let mut cursor = Entity::find().cursor_by(Column::Id);
|
||||
|
||||
cursor.after(5).before(8);
|
||||
|
||||
let mut cursor = cursor.into_model::<Row>();
|
||||
|
||||
assert_eq!(
|
||||
@ -222,8 +388,30 @@ pub async fn cursor_pagination(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||
[Row { id: 6 }, Row { id: 7 }]
|
||||
);
|
||||
|
||||
// Fetch custom struct desc
|
||||
|
||||
let mut cursor = Entity::find().cursor_by(Column::Id);
|
||||
|
||||
cursor.after(8).before(5).desc();
|
||||
|
||||
let mut cursor = cursor.into_model::<Row>();
|
||||
|
||||
assert_eq!(
|
||||
cursor.first(2).all(db).await?,
|
||||
[Row { id: 7 }, Row { id: 6 }]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cursor.first(3).all(db).await?,
|
||||
[Row { id: 7 }, Row { id: 6 }]
|
||||
);
|
||||
|
||||
// Fetch JSON value
|
||||
|
||||
let mut cursor = Entity::find().cursor_by(Column::Id);
|
||||
|
||||
cursor.after(5).before(8);
|
||||
|
||||
let mut cursor = cursor.into_json();
|
||||
|
||||
assert_eq!(
|
||||
@ -275,6 +463,54 @@ pub async fn cursor_pagination(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||
]
|
||||
);
|
||||
|
||||
// Fetch JSON value desc
|
||||
|
||||
let mut cursor = Entity::find().cursor_by(Column::Id);
|
||||
|
||||
cursor.after(8).before(5).desc();
|
||||
|
||||
let mut cursor = cursor.into_json();
|
||||
|
||||
assert_eq!(
|
||||
cursor.first(2).all(db).await?,
|
||||
[json!({ "id": 7 }), json!({ "id": 6 })]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cursor.first(3).all(db).await?,
|
||||
[json!({ "id": 7 }), json!({ "id": 6 })]
|
||||
);
|
||||
|
||||
let mut cursor = cursor.into_partial_model::<PartialRow>();
|
||||
|
||||
assert_eq!(
|
||||
cursor.first(2).all(db).await?,
|
||||
[
|
||||
PartialRow {
|
||||
id: 7,
|
||||
id_shifted: 1007,
|
||||
},
|
||||
PartialRow {
|
||||
id: 6,
|
||||
id_shifted: 1006,
|
||||
}
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cursor.first(3).all(db).await?,
|
||||
[
|
||||
PartialRow {
|
||||
id: 7,
|
||||
id_shifted: 1007,
|
||||
},
|
||||
PartialRow {
|
||||
id: 6,
|
||||
id_shifted: 1006,
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -387,6 +623,17 @@ pub async fn cursor_related_pagination(db: &DatabaseConnection) -> Result<(), Db
|
||||
[bakery(1), bakery(2), bakery(3), bakery(4),]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
bakery::Entity::find()
|
||||
.cursor_by(bakery::Column::Id)
|
||||
.before(5)
|
||||
.first(4)
|
||||
.desc()
|
||||
.all(db)
|
||||
.await?,
|
||||
[bakery(10), bakery(9), bakery(8), bakery(7),]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
bakery::Entity::find()
|
||||
.find_also_related(Baker)
|
||||
@ -411,6 +658,31 @@ pub async fn cursor_related_pagination(db: &DatabaseConnection) -> Result<(), Db
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
bakery::Entity::find()
|
||||
.find_also_related(Baker)
|
||||
.cursor_by(bakery::Column::Id)
|
||||
.after(5)
|
||||
.last(20)
|
||||
.desc()
|
||||
.all(db)
|
||||
.await?,
|
||||
[
|
||||
(bakery(4), Some(baker('X'))),
|
||||
(bakery(4), Some(baker('N'))),
|
||||
(bakery(4), Some(baker('D'))),
|
||||
(bakery(3), Some(baker('W'))),
|
||||
(bakery(3), Some(baker('M'))),
|
||||
(bakery(3), Some(baker('C'))),
|
||||
(bakery(2), Some(baker('V'))),
|
||||
(bakery(2), Some(baker('L'))),
|
||||
(bakery(2), Some(baker('B'))),
|
||||
(bakery(1), Some(baker('U'))),
|
||||
(bakery(1), Some(baker('K'))),
|
||||
(bakery(1), Some(baker('A'))),
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
bakery::Entity::find()
|
||||
.find_also_related(Baker)
|
||||
@ -427,6 +699,41 @@ pub async fn cursor_related_pagination(db: &DatabaseConnection) -> Result<(), Db
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
bakery::Entity::find()
|
||||
.find_also_related(Baker)
|
||||
.cursor_by(bakery::Column::Id)
|
||||
.after(5)
|
||||
.last(4)
|
||||
.desc()
|
||||
.all(db)
|
||||
.await?,
|
||||
[
|
||||
(bakery(2), Some(baker('B'))),
|
||||
(bakery(1), Some(baker('U'))),
|
||||
(bakery(1), Some(baker('K'))),
|
||||
(bakery(1), Some(baker('A'))),
|
||||
]
|
||||
);
|
||||
|
||||
// since "10" is before "2" lexicologically, it return that first
|
||||
assert_eq!(
|
||||
bakery::Entity::find()
|
||||
.find_also_related(Baker)
|
||||
.cursor_by(bakery::Column::Name)
|
||||
.after("3")
|
||||
.last(4)
|
||||
.desc()
|
||||
.all(db)
|
||||
.await?,
|
||||
[
|
||||
(bakery(10), Some(baker('J'))),
|
||||
(bakery(1), Some(baker('U'))),
|
||||
(bakery(1), Some(baker('K'))),
|
||||
(bakery(1), Some(baker('A'))),
|
||||
]
|
||||
);
|
||||
|
||||
// since "10" is before "2" lexicologically, it return that first
|
||||
assert_eq!(
|
||||
bakery::Entity::find()
|
||||
@ -444,6 +751,24 @@ pub async fn cursor_related_pagination(db: &DatabaseConnection) -> Result<(), Db
|
||||
]
|
||||
);
|
||||
|
||||
// since "10" is before "2" lexicologically, it return that first
|
||||
assert_eq!(
|
||||
bakery::Entity::find()
|
||||
.find_also_related(Baker)
|
||||
.cursor_by(bakery::Column::Name)
|
||||
.after("3")
|
||||
.last(4)
|
||||
.desc()
|
||||
.all(db)
|
||||
.await?,
|
||||
[
|
||||
(bakery(10), Some(baker('J'))),
|
||||
(bakery(1), Some(baker('U'))),
|
||||
(bakery(1), Some(baker('K'))),
|
||||
(bakery(1), Some(baker('A'))),
|
||||
]
|
||||
);
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||
enum QueryAs {
|
||||
CakeId,
|
||||
@ -473,6 +798,29 @@ pub async fn cursor_related_pagination(db: &DatabaseConnection) -> Result<(), Db
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cake::Entity::find()
|
||||
.find_also_related(Baker)
|
||||
.select_only()
|
||||
.column_as(cake::Column::Id, QueryAs::CakeId)
|
||||
.column_as(cake::Column::Name, QueryAs::CakeName)
|
||||
.column_as(baker::Column::Name, QueryAs::BakerName)
|
||||
.cursor_by(cake::Column::Name)
|
||||
.after("e")
|
||||
.last(4)
|
||||
.desc()
|
||||
.clone()
|
||||
.into_model::<CakeBakerlite>()
|
||||
.all(db)
|
||||
.await?,
|
||||
vec![
|
||||
cakebaker('b', 'B'),
|
||||
cakebaker('b', 'A'),
|
||||
cakebaker('a', 'B'),
|
||||
cakebaker('a', 'A')
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cake::Entity::find()
|
||||
.find_also_related(Baker)
|
||||
@ -487,7 +835,25 @@ pub async fn cursor_related_pagination(db: &DatabaseConnection) -> Result<(), Db
|
||||
.into_model::<CakeBakerlite>()
|
||||
.all(db)
|
||||
.await?,
|
||||
vec![cakebaker('a', 'A'), cakebaker('a', 'B'),]
|
||||
vec![cakebaker('a', 'A'), cakebaker('a', 'B')]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cake::Entity::find()
|
||||
.find_also_related(Baker)
|
||||
.select_only()
|
||||
.column_as(cake::Column::Id, QueryAs::CakeId)
|
||||
.column_as(cake::Column::Name, QueryAs::CakeName)
|
||||
.column_as(baker::Column::Name, QueryAs::BakerName)
|
||||
.cursor_by(cake::Column::Name)
|
||||
.after("b")
|
||||
.last(4)
|
||||
.desc()
|
||||
.clone()
|
||||
.into_model::<CakeBakerlite>()
|
||||
.all(db)
|
||||
.await?,
|
||||
vec![cakebaker('a', 'B'), cakebaker('a', 'A')]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
@ -507,6 +873,24 @@ pub async fn cursor_related_pagination(db: &DatabaseConnection) -> Result<(), Db
|
||||
vec![cakebaker('a', 'A'), cakebaker('b', 'A'),]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cake::Entity::find()
|
||||
.find_also_related(Baker)
|
||||
.select_only()
|
||||
.column_as(cake::Column::Id, QueryAs::CakeId)
|
||||
.column_as(cake::Column::Name, QueryAs::CakeName)
|
||||
.column_as(baker::Column::Name, QueryAs::BakerName)
|
||||
.cursor_by_other(baker::Column::Name)
|
||||
.after("B")
|
||||
.last(4)
|
||||
.desc()
|
||||
.clone()
|
||||
.into_model::<CakeBakerlite>()
|
||||
.all(db)
|
||||
.await?,
|
||||
vec![cakebaker('b', 'A'), cakebaker('a', 'A'),]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cake::Entity::find()
|
||||
.find_also_related(Baker)
|
||||
@ -531,5 +915,30 @@ pub async fn cursor_related_pagination(db: &DatabaseConnection) -> Result<(), Db
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cake::Entity::find()
|
||||
.find_also_related(Baker)
|
||||
.select_only()
|
||||
.column_as(cake::Column::Id, QueryAs::CakeId)
|
||||
.column_as(cake::Column::Name, QueryAs::CakeName)
|
||||
.column_as(baker::Column::Name, QueryAs::BakerName)
|
||||
.cursor_by_other(baker::Column::Name)
|
||||
.after("E")
|
||||
.last(20)
|
||||
.desc()
|
||||
.clone()
|
||||
.into_model::<CakeBakerlite>()
|
||||
.all(db)
|
||||
.await?,
|
||||
vec![
|
||||
cakebaker('d', 'D'),
|
||||
cakebaker('c', 'C'),
|
||||
cakebaker('b', 'B'),
|
||||
cakebaker('a', 'B'),
|
||||
cakebaker('b', 'A'),
|
||||
cakebaker('a', 'A'),
|
||||
]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user