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"] }
|
tracing = { version = "0.1", features = ["log"] }
|
||||||
rust_decimal = { version = "^1", optional = true }
|
rust_decimal = { version = "^1", optional = true }
|
||||||
sea-orm-macros = { version = "^0.8.0", path = "sea-orm-macros", 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"] }
|
sea-strum = { version = "^0.23", features = ["derive", "sea-orm"] }
|
||||||
serde = { version = "^1.0", features = ["derive"] }
|
serde = { version = "^1.0", features = ["derive"] }
|
||||||
serde_json = { version = "^1", optional = true }
|
serde_json = { version = "^1", optional = true }
|
||||||
|
@ -23,7 +23,7 @@ clap = { version = "^2.33" }
|
|||||||
dotenv = { version = "^0.15" }
|
dotenv = { version = "^0.15" }
|
||||||
sea-orm = { version = "^0.8.0", path = "../", default-features = false, features = ["macros"] }
|
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-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 = { version = "0.1", features = ["log"] }
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
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-tokio-native-tls = [ "sea-orm/runtime-tokio-native-tls" ]
|
||||||
runtime-actix-rustls = [ "sea-orm/runtime-actix-rustls" ]
|
runtime-actix-rustls = [ "sea-orm/runtime-actix-rustls" ]
|
||||||
runtime-async-std-rustls = [ "sea-orm/runtime-async-std-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::{
|
pub use crate::{
|
||||||
error::*, ActiveEnum, ActiveModelBehavior, ActiveModelTrait, ColumnDef, ColumnTrait,
|
error::*, ActiveEnum, ActiveModelBehavior, ActiveModelTrait, ColumnDef, ColumnTrait,
|
||||||
ColumnType, CursorTrait, DatabaseConnection, DbConn, EntityName, EntityTrait, EnumIter,
|
ColumnType, DatabaseConnection, DbConn, EntityName, EntityTrait, EnumIter, ForeignKeyAction,
|
||||||
ForeignKeyAction, Iden, IdenStatic, Linked, ModelTrait, PaginatorTrait, PrimaryKeyToColumn,
|
Iden, IdenStatic, Linked, ModelTrait, PaginatorTrait, PrimaryKeyToColumn, PrimaryKeyTrait,
|
||||||
PrimaryKeyTrait, QueryFilter, QueryResult, Related, RelationDef, RelationTrait, Select, Value,
|
QueryFilter, QueryResult, Related, RelationDef, RelationTrait, Select, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "macros")]
|
#[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 delete;
|
||||||
mod execute;
|
mod execute;
|
||||||
mod insert;
|
mod insert;
|
||||||
@ -7,7 +6,6 @@ mod query;
|
|||||||
mod select;
|
mod select;
|
||||||
mod update;
|
mod update;
|
||||||
|
|
||||||
pub use cursor::*;
|
|
||||||
pub use delete::*;
|
pub use delete::*;
|
||||||
pub use execute::*;
|
pub use execute::*;
|
||||||
pub use insert::*;
|
pub use insert::*;
|
||||||
|
@ -23,6 +23,6 @@ pub use update::*;
|
|||||||
pub use util::*;
|
pub use util::*;
|
||||||
|
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
ConnectionTrait, CursorTrait, InsertResult, PaginatorTrait, Statement, StreamTrait,
|
ConnectionTrait, InsertResult, PaginatorTrait, Statement, StreamTrait, TransactionTrait,
|
||||||
TransactionTrait, UpdateResult, Value, Values,
|
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