Revert "Cursor Pagination (#754)"
This reverts commit 6f9dbd260e6e004c369abe0019b4374253db7d3b.
This commit is contained in:
parent
6f9dbd260e
commit
12ec00272c
@ -31,7 +31,7 @@ futures-util = { version = "^0.3" }
|
||||
tracing = { version = "0.1", features = ["log"] }
|
||||
rust_decimal = { version = "^1", optional = true }
|
||||
sea-orm-macros = { version = "^0.8.0", path = "sea-orm-macros", optional = true }
|
||||
sea-query = { version = "^0.24.0", git = "https://github.com/SeaQL/sea-query", branch = "clear-order-by", features = ["thread-safe"] }
|
||||
sea-query = { version = "^0.24.5", features = ["thread-safe"] }
|
||||
sea-strum = { version = "^0.23", features = ["derive", "sea-orm"] }
|
||||
serde = { version = "^1.0", features = ["derive"] }
|
||||
serde_json = { version = "^1", optional = true }
|
||||
|
@ -23,7 +23,7 @@ clap = { version = "^2.33" }
|
||||
dotenv = { version = "^0.15" }
|
||||
sea-orm = { version = "^0.8.0", path = "../", default-features = false, features = ["macros"] }
|
||||
sea-orm-cli = { version = "^0.8.1", path = "../sea-orm-cli", default-features = false }
|
||||
sea-schema = { version = "^0.8.1", git = "https://github.com/SeaQL/sea-schema", branch = "bump-for-sea-orm-cursor-pagination" }
|
||||
sea-schema = { version = "^0.8.1" }
|
||||
tracing = { version = "0.1", features = ["log"] }
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
|
||||
@ -39,4 +39,4 @@ runtime-async-std-native-tls = [ "sea-orm/runtime-async-std-native-tls" ]
|
||||
runtime-tokio-native-tls = [ "sea-orm/runtime-tokio-native-tls" ]
|
||||
runtime-actix-rustls = [ "sea-orm/runtime-actix-rustls" ]
|
||||
runtime-async-std-rustls = [ "sea-orm/runtime-async-std-rustls" ]
|
||||
runtime-tokio-rustls = [ "sea-orm/runtime-tokio-rustls" ]
|
||||
runtime-tokio-rustls = [ "sea-orm/runtime-tokio-rustls" ]
|
@ -1,8 +1,8 @@
|
||||
pub use crate::{
|
||||
error::*, ActiveEnum, ActiveModelBehavior, ActiveModelTrait, ColumnDef, ColumnTrait,
|
||||
ColumnType, CursorTrait, DatabaseConnection, DbConn, EntityName, EntityTrait, EnumIter,
|
||||
ForeignKeyAction, Iden, IdenStatic, Linked, ModelTrait, PaginatorTrait, PrimaryKeyToColumn,
|
||||
PrimaryKeyTrait, QueryFilter, QueryResult, Related, RelationDef, RelationTrait, Select, Value,
|
||||
ColumnType, DatabaseConnection, DbConn, EntityName, EntityTrait, EnumIter, ForeignKeyAction,
|
||||
Iden, IdenStatic, Linked, ModelTrait, PaginatorTrait, PrimaryKeyToColumn, PrimaryKeyTrait,
|
||||
QueryFilter, QueryResult, Related, RelationDef, RelationTrait, Select, Value,
|
||||
};
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
|
@ -1,437 +0,0 @@
|
||||
use crate::{
|
||||
ConnectionTrait, DbErr, EntityTrait, FromQueryResult, Identity, IntoIdentity, QueryOrder,
|
||||
Select, SelectModel, SelectorTrait,
|
||||
};
|
||||
use sea_query::{
|
||||
Condition, DynIden, Expr, IntoValueTuple, Order, OrderedStatement, SeaRc, SelectStatement,
|
||||
SimpleExpr, Value, ValueTuple,
|
||||
};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// Cursor pagination
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Cursor<S>
|
||||
where
|
||||
S: SelectorTrait,
|
||||
{
|
||||
pub(crate) query: SelectStatement,
|
||||
pub(crate) table: DynIden,
|
||||
pub(crate) order_columns: Identity,
|
||||
pub(crate) last: bool,
|
||||
pub(crate) phantom: PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<S> Cursor<S>
|
||||
where
|
||||
S: SelectorTrait,
|
||||
{
|
||||
/// Initialize a cursor
|
||||
pub fn new<C>(query: SelectStatement, table: DynIden, order_columns: C) -> Self
|
||||
where
|
||||
C: IntoIdentity,
|
||||
{
|
||||
Self {
|
||||
query,
|
||||
table,
|
||||
order_columns: order_columns.into_identity(),
|
||||
last: false,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Filter paginated result with corresponding column less than the input value
|
||||
pub fn before<V>(&mut self, values: V) -> &mut Self
|
||||
where
|
||||
V: IntoValueTuple,
|
||||
{
|
||||
let condition = self.apply_filter(values, |c, v| {
|
||||
Expr::tbl(SeaRc::clone(&self.table), SeaRc::clone(c)).lt(v)
|
||||
});
|
||||
self.query.cond_where(condition);
|
||||
self
|
||||
}
|
||||
|
||||
/// Filter paginated result with corresponding column greater than the input value
|
||||
pub fn after<V>(&mut self, values: V) -> &mut Self
|
||||
where
|
||||
V: IntoValueTuple,
|
||||
{
|
||||
let condition = self.apply_filter(values, |c, v| {
|
||||
Expr::tbl(SeaRc::clone(&self.table), SeaRc::clone(c)).gt(v)
|
||||
});
|
||||
self.query.cond_where(condition);
|
||||
self
|
||||
}
|
||||
|
||||
fn apply_filter<V, F>(&self, values: V, f: F) -> Condition
|
||||
where
|
||||
V: IntoValueTuple,
|
||||
F: Fn(&DynIden, Value) -> SimpleExpr,
|
||||
{
|
||||
match (&self.order_columns, values.into_value_tuple()) {
|
||||
(Identity::Unary(c1), ValueTuple::One(v1)) => Condition::all().add(f(c1, v1)),
|
||||
(Identity::Binary(c1, c2), ValueTuple::Two(v1, v2)) => {
|
||||
Condition::all().add(f(c1, v1)).add(f(c2, v2))
|
||||
}
|
||||
(Identity::Ternary(c1, c2, c3), ValueTuple::Three(v1, v2, v3)) => Condition::all()
|
||||
.add(f(c1, v1))
|
||||
.add(f(c2, v2))
|
||||
.add(f(c3, v3)),
|
||||
_ => panic!("column arity mismatch"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Limit result set to only first N rows in ascending order of the order by column
|
||||
pub fn first(&mut self, num_rows: u64) -> &mut Self {
|
||||
self.query.limit(num_rows).clear_order_by();
|
||||
let table = SeaRc::clone(&self.table);
|
||||
self.apply_order_by(|query, col| {
|
||||
query.order_by((SeaRc::clone(&table), SeaRc::clone(col)), Order::Asc);
|
||||
});
|
||||
self.last = false;
|
||||
self
|
||||
}
|
||||
|
||||
/// Limit result set to only last N rows in ascending order of the order by column
|
||||
pub fn last(&mut self, num_rows: u64) -> &mut Self {
|
||||
self.query.limit(num_rows).clear_order_by();
|
||||
let table = SeaRc::clone(&self.table);
|
||||
self.apply_order_by(|query, col| {
|
||||
query.order_by((SeaRc::clone(&table), SeaRc::clone(col)), Order::Desc);
|
||||
});
|
||||
self.last = true;
|
||||
self
|
||||
}
|
||||
|
||||
fn apply_order_by<F>(&mut self, f: F)
|
||||
where
|
||||
F: Fn(&mut SelectStatement, &DynIden),
|
||||
{
|
||||
let query = &mut self.query;
|
||||
match &self.order_columns {
|
||||
Identity::Unary(c1) => {
|
||||
f(query, c1);
|
||||
}
|
||||
Identity::Binary(c1, c2) => {
|
||||
f(query, c1);
|
||||
f(query, c2);
|
||||
}
|
||||
Identity::Ternary(c1, c2, c3) => {
|
||||
f(query, c1);
|
||||
f(query, c2);
|
||||
f(query, c3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch the paginated result
|
||||
pub async fn all<C>(&mut self, db: &C) -> Result<Vec<S::Item>, DbErr>
|
||||
where
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
let stmt = db.get_database_backend().build(&self.query);
|
||||
let rows = db.query_all(stmt).await?;
|
||||
let mut buffer = Vec::with_capacity(rows.len());
|
||||
for row in rows.into_iter() {
|
||||
buffer.push(S::from_raw_query_result(row)?);
|
||||
}
|
||||
if self.last {
|
||||
buffer.reverse()
|
||||
}
|
||||
Ok(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> QueryOrder for Cursor<S>
|
||||
where
|
||||
S: SelectorTrait,
|
||||
{
|
||||
type QueryStatement = SelectStatement;
|
||||
|
||||
fn query(&mut self) -> &mut SelectStatement {
|
||||
&mut self.query
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for any type that can be turn into a cursor
|
||||
pub trait CursorTrait {
|
||||
/// Select operation
|
||||
type Selector: SelectorTrait + Send + Sync;
|
||||
|
||||
/// Convert current type into a cursor
|
||||
fn cursor<C>(self, order_columns: C) -> Cursor<Self::Selector>
|
||||
where
|
||||
C: IntoIdentity;
|
||||
}
|
||||
|
||||
impl<E, M> CursorTrait for Select<E>
|
||||
where
|
||||
E: EntityTrait<Model = M>,
|
||||
M: FromQueryResult + Sized + Send + Sync,
|
||||
{
|
||||
type Selector = SelectModel<M>;
|
||||
|
||||
fn cursor<C>(self, order_columns: C) -> Cursor<Self::Selector>
|
||||
where
|
||||
C: IntoIdentity,
|
||||
{
|
||||
Cursor::new(self.query, SeaRc::new(E::default()), order_columns)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "mock")]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::entity::prelude::*;
|
||||
use crate::tests_cfg::*;
|
||||
use crate::{DbBackend, MockDatabase, Statement, Transaction};
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[smol_potat::test]
|
||||
async fn first_2_before_10() -> Result<(), DbErr> {
|
||||
use fruit::*;
|
||||
|
||||
let models = vec![
|
||||
Model {
|
||||
id: 1,
|
||||
name: "Blueberry".into(),
|
||||
cake_id: Some(1),
|
||||
},
|
||||
Model {
|
||||
id: 2,
|
||||
name: "Rasberry".into(),
|
||||
cake_id: Some(1),
|
||||
},
|
||||
];
|
||||
|
||||
let db = MockDatabase::new(DbBackend::Postgres)
|
||||
.append_query_results(vec![models.clone()])
|
||||
.into_connection();
|
||||
|
||||
assert_eq!(
|
||||
Entity::find()
|
||||
.cursor(Column::Id)
|
||||
.before(10)
|
||||
.first(2)
|
||||
.all(&db)
|
||||
.await?,
|
||||
models
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
db.into_transaction_log(),
|
||||
vec![Transaction::many(vec![Statement::from_sql_and_values(
|
||||
DbBackend::Postgres,
|
||||
[
|
||||
r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id""#,
|
||||
r#"FROM "fruit""#,
|
||||
r#"WHERE "fruit"."id" < $1"#,
|
||||
r#"ORDER BY "fruit"."id" ASC"#,
|
||||
r#"LIMIT $2"#,
|
||||
]
|
||||
.join(" ")
|
||||
.as_str(),
|
||||
vec![10_i32.into(), 2_u64.into()]
|
||||
),])]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[smol_potat::test]
|
||||
async fn last_2_after_10() -> Result<(), DbErr> {
|
||||
use fruit::*;
|
||||
|
||||
let db = MockDatabase::new(DbBackend::Postgres)
|
||||
.append_query_results(vec![vec![
|
||||
Model {
|
||||
id: 22,
|
||||
name: "Rasberry".into(),
|
||||
cake_id: Some(1),
|
||||
},
|
||||
Model {
|
||||
id: 21,
|
||||
name: "Blueberry".into(),
|
||||
cake_id: Some(1),
|
||||
},
|
||||
]])
|
||||
.into_connection();
|
||||
|
||||
assert_eq!(
|
||||
Entity::find()
|
||||
.cursor(Column::Id)
|
||||
.after(10)
|
||||
.last(2)
|
||||
.all(&db)
|
||||
.await?,
|
||||
vec![
|
||||
Model {
|
||||
id: 21,
|
||||
name: "Blueberry".into(),
|
||||
cake_id: Some(1),
|
||||
},
|
||||
Model {
|
||||
id: 22,
|
||||
name: "Rasberry".into(),
|
||||
cake_id: Some(1),
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
db.into_transaction_log(),
|
||||
vec![Transaction::many(vec![Statement::from_sql_and_values(
|
||||
DbBackend::Postgres,
|
||||
[
|
||||
r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id""#,
|
||||
r#"FROM "fruit""#,
|
||||
r#"WHERE "fruit"."id" > $1"#,
|
||||
r#"ORDER BY "fruit"."id" DESC"#,
|
||||
r#"LIMIT $2"#,
|
||||
]
|
||||
.join(" ")
|
||||
.as_str(),
|
||||
vec![10_i32.into(), 2_u64.into()]
|
||||
),])]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[smol_potat::test]
|
||||
async fn last_2_after_25_before_30() -> Result<(), DbErr> {
|
||||
use fruit::*;
|
||||
|
||||
let db = MockDatabase::new(DbBackend::Postgres)
|
||||
.append_query_results(vec![vec![
|
||||
Model {
|
||||
id: 27,
|
||||
name: "Rasberry".into(),
|
||||
cake_id: Some(1),
|
||||
},
|
||||
Model {
|
||||
id: 26,
|
||||
name: "Blueberry".into(),
|
||||
cake_id: Some(1),
|
||||
},
|
||||
]])
|
||||
.into_connection();
|
||||
|
||||
assert_eq!(
|
||||
Entity::find()
|
||||
.cursor(Column::Id)
|
||||
.after(25)
|
||||
.before(30)
|
||||
.last(2)
|
||||
.all(&db)
|
||||
.await?,
|
||||
vec![
|
||||
Model {
|
||||
id: 26,
|
||||
name: "Blueberry".into(),
|
||||
cake_id: Some(1),
|
||||
},
|
||||
Model {
|
||||
id: 27,
|
||||
name: "Rasberry".into(),
|
||||
cake_id: Some(1),
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
db.into_transaction_log(),
|
||||
vec![Transaction::many(vec![Statement::from_sql_and_values(
|
||||
DbBackend::Postgres,
|
||||
[
|
||||
r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id""#,
|
||||
r#"FROM "fruit""#,
|
||||
r#"WHERE "fruit"."id" > $1"#,
|
||||
r#"AND "fruit"."id" < $2"#,
|
||||
r#"ORDER BY "fruit"."id" DESC"#,
|
||||
r#"LIMIT $3"#,
|
||||
]
|
||||
.join(" ")
|
||||
.as_str(),
|
||||
vec![25_i32.into(), 30_i32.into(), 2_u64.into()]
|
||||
),])]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[smol_potat::test]
|
||||
async fn composite_keys() -> Result<(), DbErr> {
|
||||
use cake_filling::*;
|
||||
|
||||
let db = MockDatabase::new(DbBackend::Postgres)
|
||||
.append_query_results(vec![vec![
|
||||
Model {
|
||||
cake_id: 1,
|
||||
filling_id: 2,
|
||||
},
|
||||
Model {
|
||||
cake_id: 1,
|
||||
filling_id: 3,
|
||||
},
|
||||
Model {
|
||||
cake_id: 2,
|
||||
filling_id: 3,
|
||||
},
|
||||
]])
|
||||
.into_connection();
|
||||
|
||||
assert_eq!(
|
||||
Entity::find()
|
||||
.cursor((Column::CakeId, Column::FillingId))
|
||||
.after((0, 1))
|
||||
.before((10, 11))
|
||||
.first(3)
|
||||
.all(&db)
|
||||
.await?,
|
||||
vec![
|
||||
Model {
|
||||
cake_id: 1,
|
||||
filling_id: 2,
|
||||
},
|
||||
Model {
|
||||
cake_id: 1,
|
||||
filling_id: 3,
|
||||
},
|
||||
Model {
|
||||
cake_id: 2,
|
||||
filling_id: 3,
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
db.into_transaction_log(),
|
||||
vec![Transaction::many(vec![Statement::from_sql_and_values(
|
||||
DbBackend::Postgres,
|
||||
[
|
||||
r#"SELECT "cake_filling"."cake_id", "cake_filling"."filling_id""#,
|
||||
r#"FROM "cake_filling""#,
|
||||
r#"WHERE "cake_filling"."cake_id" > $1"#,
|
||||
r#"AND "cake_filling"."filling_id" > $2"#,
|
||||
r#"AND ("cake_filling"."cake_id" < $3"#,
|
||||
r#"AND "cake_filling"."filling_id" < $4)"#,
|
||||
r#"ORDER BY "cake_filling"."cake_id" ASC, "cake_filling"."filling_id" ASC"#,
|
||||
r#"LIMIT $5"#,
|
||||
]
|
||||
.join(" ")
|
||||
.as_str(),
|
||||
vec![
|
||||
0_i32.into(),
|
||||
1_i32.into(),
|
||||
10_i32.into(),
|
||||
11_i32.into(),
|
||||
3_u64.into()
|
||||
]
|
||||
),])]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
mod cursor;
|
||||
mod delete;
|
||||
mod execute;
|
||||
mod insert;
|
||||
@ -7,7 +6,6 @@ mod query;
|
||||
mod select;
|
||||
mod update;
|
||||
|
||||
pub use cursor::*;
|
||||
pub use delete::*;
|
||||
pub use execute::*;
|
||||
pub use insert::*;
|
||||
|
@ -23,6 +23,6 @@ pub use update::*;
|
||||
pub use util::*;
|
||||
|
||||
pub use crate::{
|
||||
ConnectionTrait, CursorTrait, InsertResult, PaginatorTrait, Statement, StreamTrait,
|
||||
TransactionTrait, UpdateResult, Value, Values,
|
||||
ConnectionTrait, InsertResult, PaginatorTrait, Statement, StreamTrait, TransactionTrait,
|
||||
UpdateResult, Value, Values,
|
||||
};
|
||||
|
@ -1,203 +0,0 @@
|
||||
pub mod common;
|
||||
|
||||
pub use common::{features::*, setup::*, TestContext};
|
||||
use pretty_assertions::assert_eq;
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[sea_orm_macros::test]
|
||||
#[cfg(any(
|
||||
feature = "sqlx-mysql",
|
||||
feature = "sqlx-sqlite",
|
||||
feature = "sqlx-postgres"
|
||||
))]
|
||||
async fn main() -> Result<(), DbErr> {
|
||||
let ctx = TestContext::new("cursor_tests").await;
|
||||
create_tables(&ctx.db).await?;
|
||||
create_insert_default(&ctx.db).await?;
|
||||
cursor_pagination(&ctx.db).await?;
|
||||
ctx.delete().await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn create_insert_default(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||
use insert_default::*;
|
||||
|
||||
for _ in 0..10 {
|
||||
ActiveModel {
|
||||
..Default::default()
|
||||
}
|
||||
.insert(db)
|
||||
.await?;
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Entity::find().all(db).await?,
|
||||
vec![
|
||||
Model { id: 1 },
|
||||
Model { id: 2 },
|
||||
Model { id: 3 },
|
||||
Model { id: 4 },
|
||||
Model { id: 5 },
|
||||
Model { id: 6 },
|
||||
Model { id: 7 },
|
||||
Model { id: 8 },
|
||||
Model { id: 9 },
|
||||
Model { id: 10 },
|
||||
]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn cursor_pagination(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||
use insert_default::*;
|
||||
|
||||
// Before 5, i.e. id < 5
|
||||
|
||||
let mut cursor = Entity::find().cursor(Column::Id);
|
||||
|
||||
cursor.before(5);
|
||||
|
||||
assert_eq!(
|
||||
cursor.first(4).all(db).await?,
|
||||
vec![
|
||||
Model { id: 1 },
|
||||
Model { id: 2 },
|
||||
Model { id: 3 },
|
||||
Model { id: 4 },
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cursor.first(5).all(db).await?,
|
||||
vec![
|
||||
Model { id: 1 },
|
||||
Model { id: 2 },
|
||||
Model { id: 3 },
|
||||
Model { id: 4 },
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cursor.last(4).all(db).await?,
|
||||
vec![
|
||||
Model { id: 1 },
|
||||
Model { id: 2 },
|
||||
Model { id: 3 },
|
||||
Model { id: 4 },
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cursor.last(5).all(db).await?,
|
||||
vec![
|
||||
Model { id: 1 },
|
||||
Model { id: 2 },
|
||||
Model { id: 3 },
|
||||
Model { id: 4 },
|
||||
]
|
||||
);
|
||||
|
||||
// After 5, i.e. id > 5
|
||||
|
||||
let mut cursor = Entity::find().cursor(Column::Id);
|
||||
|
||||
cursor.after(5);
|
||||
|
||||
assert_eq!(
|
||||
cursor.first(4).all(db).await?,
|
||||
vec![
|
||||
Model { id: 6 },
|
||||
Model { id: 7 },
|
||||
Model { id: 8 },
|
||||
Model { id: 9 },
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cursor.first(5).all(db).await?,
|
||||
vec![
|
||||
Model { id: 6 },
|
||||
Model { id: 7 },
|
||||
Model { id: 8 },
|
||||
Model { id: 9 },
|
||||
Model { id: 10 },
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cursor.first(6).all(db).await?,
|
||||
vec![
|
||||
Model { id: 6 },
|
||||
Model { id: 7 },
|
||||
Model { id: 8 },
|
||||
Model { id: 9 },
|
||||
Model { id: 10 },
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cursor.last(4).all(db).await?,
|
||||
vec![
|
||||
Model { id: 7 },
|
||||
Model { id: 8 },
|
||||
Model { id: 9 },
|
||||
Model { id: 10 },
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cursor.last(5).all(db).await?,
|
||||
vec![
|
||||
Model { id: 6 },
|
||||
Model { id: 7 },
|
||||
Model { id: 8 },
|
||||
Model { id: 9 },
|
||||
Model { id: 10 },
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cursor.last(6).all(db).await?,
|
||||
vec![
|
||||
Model { id: 6 },
|
||||
Model { id: 7 },
|
||||
Model { id: 8 },
|
||||
Model { id: 9 },
|
||||
Model { id: 10 },
|
||||
]
|
||||
);
|
||||
|
||||
// Between 5 and 8, i.e. id > 5 AND id < 8
|
||||
|
||||
let mut cursor = Entity::find().cursor(Column::Id);
|
||||
|
||||
cursor.after(5).before(8);
|
||||
|
||||
assert_eq!(cursor.first(1).all(db).await?, vec![Model { id: 6 }]);
|
||||
|
||||
assert_eq!(
|
||||
cursor.first(2).all(db).await?,
|
||||
vec![Model { id: 6 }, Model { id: 7 }]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cursor.first(3).all(db).await?,
|
||||
vec![Model { id: 6 }, Model { id: 7 }]
|
||||
);
|
||||
|
||||
assert_eq!(cursor.last(1).all(db).await?, vec![Model { id: 7 }]);
|
||||
|
||||
assert_eq!(
|
||||
cursor.last(2).all(db).await?,
|
||||
vec![Model { id: 6 }, Model { id: 7 }]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cursor.last(3).all(db).await?,
|
||||
vec![Model { id: 6 }, Model { id: 7 }]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user