Refactor pagination

This commit is contained in:
Magnus Hoff 2017-10-20 20:48:38 +02:00
parent 29c4180496
commit d918d614b4
3 changed files with 65 additions and 38 deletions

View file

@ -13,12 +13,12 @@ use super::pagination::Pagination;
pub struct ChangesResource { pub struct ChangesResource {
state: State, state: State,
before: Option<i32>, pagination: Pagination<i32>,
} }
impl ChangesResource { impl ChangesResource {
pub fn new(state: State, pagination: Pagination<i32>) -> Self { pub fn new(state: State, pagination: Pagination<i32>) -> Self {
Self { state, before: match pagination { Pagination::Before(x) => Some(x), _ => None } } Self { state, pagination }
} }
} }
@ -60,27 +60,55 @@ impl Resource for ChangesResource {
const PAGE_SIZE: i32 = 30; const PAGE_SIZE: i32 = 30;
let data = self.state.get_article_revision_stubs(self.before, PAGE_SIZE); let pagination = self.pagination.clone();
let data = self.state.query_article_revision_stubs(move |query| {
use diesel::prelude::*;
use schema::article_revisions::dsl::*;
let query = query
.limit(PAGE_SIZE as i64 + 1);
match pagination {
Pagination::After(x) => query
.filter(sequence_number.gt(x))
.order(sequence_number.asc()),
Pagination::Before(x) => query
.filter(sequence_number.lt(x))
.order(sequence_number.desc()),
Pagination::None => query
.order(sequence_number.desc()),
}
});
let head = self.head(); let head = self.head();
Box::new(data.join(head) Box::new(data.join(head)
.and_then(move |(data, head)| { .and_then(move |(mut data, head)| {
use std::iter::Iterator; use std::iter::Iterator;
let link_newer = self.before.and_then(|_| { let extra_element = if data.len() > PAGE_SIZE as usize {
data.first().and_then(|x| { data.pop()
match x.sequence_number { } else {
seq => Some(format!("?before={}", seq + PAGE_SIZE)), None
} };
})
});
let link_older = data.last().and_then(|x| { let (link_newer, link_older) = match self.pagination {
match x.sequence_number { Pagination::After(x) => {
1 => None, data.reverse();
seq => Some(format!("?before={}", seq)), (
} extra_element.map(|_| format!("?after={}", data.first().unwrap().sequence_number)),
}); Some(format!("?before={}", x + 1))
)
},
Pagination::Before(x) => (
Some(format!("?after={}", x - 1)),
extra_element.map(|_| format!("?before={}", data.last().unwrap().sequence_number)),
),
Pagination::None => (
None,
extra_element.map(|_| format!("?before={}", data.last().unwrap().sequence_number)),
),
};
let changes = &data.into_iter().map(|x| { let changes = &data.into_iter().map(|x| {
Row { Row {

View file

@ -24,6 +24,7 @@ struct PaginationStruct<T> {
before: Option<T>, before: Option<T>,
} }
#[derive(Clone)]
pub enum Pagination<T> { pub enum Pagination<T> {
After(T), After(T),
Before(T), Before(T),

View file

@ -101,33 +101,31 @@ impl State {
}) })
} }
pub fn get_article_revision_stubs(&self, before: Option<i32>, limit: i32) -> CpuFuture<Vec<models::ArticleRevisionStub>, Error> { pub fn query_article_revision_stubs<F>(&self, f: F) -> CpuFuture<Vec<models::ArticleRevisionStub>, Error>
where
F: 'static + Send + Sync,
for <'a> F:
FnOnce(article_revisions::BoxedQuery<'a, diesel::sqlite::Sqlite>) ->
article_revisions::BoxedQuery<'a, diesel::sqlite::Sqlite>,
{
let connection_pool = self.connection_pool.clone(); let connection_pool = self.connection_pool.clone();
self.cpu_pool.spawn_fn(move || { self.cpu_pool.spawn_fn(move || {
use schema::article_revisions; use schema::article_revisions::dsl::*;
let query = article_revisions::table Ok(f(article_revisions.into_boxed())
.order(article_revisions::sequence_number.desc())
.limit(limit as i64)
.select(( .select((
article_revisions::sequence_number, sequence_number,
article_revisions::article_id, article_id,
article_revisions::revision, revision,
article_revisions::created, created,
article_revisions::slug, slug,
article_revisions::title, title,
article_revisions::latest, latest,
article_revisions::author, author,
)) ))
.into_boxed(); .load(&*connection_pool.get()?)?
)
let query = match before {
Some(before) => query.filter(article_revisions::sequence_number.lt(before)),
None => query
};
Ok(query.load(&*connection_pool.get()?)?)
}) })
} }