From 12b5eabd5db7a5e5e529d15b9c0a74bb4bbcd2cf Mon Sep 17 00:00:00 2001 From: Tamo Date: Thu, 2 Jun 2022 13:31:46 +0200 Subject: [PATCH] chore(http): unify the pagination of the index and documents route behind a common type --- .../src/routes/indexes/documents.rs | 18 ++--- meilisearch-http/src/routes/indexes/mod.rs | 34 +++----- meilisearch-http/src/routes/mod.rs | 81 +++++++++++++++++++ .../tests/documents/add_documents.rs | 2 +- 4 files changed, 100 insertions(+), 35 deletions(-) diff --git a/meilisearch-http/src/routes/indexes/documents.rs b/meilisearch-http/src/routes/indexes/documents.rs index 4c87044db..f506e587c 100644 --- a/meilisearch-http/src/routes/indexes/documents.rs +++ b/meilisearch-http/src/routes/indexes/documents.rs @@ -14,7 +14,7 @@ use mime::Mime; use once_cell::sync::Lazy; use serde::Deserialize; use serde_cs::vec::CS; -use serde_json::{json, Value}; +use serde_json::Value; use tokio::sync::mpsc; use crate::analytics::Analytics; @@ -22,7 +22,7 @@ use crate::error::MeilisearchHttpError; use crate::extractors::authentication::{policies::*, GuardedData}; use crate::extractors::payload::Payload; use crate::extractors::sequential_extractor::SeqHandler; -use crate::routes::{fold_star_or, StarOr}; +use crate::routes::{fold_star_or, PaginationView, StarOr}; use crate::task::SummarizedTaskView; static ACCEPTED_CONTENT_TYPE: Lazy> = Lazy::new(|| { @@ -122,14 +122,12 @@ pub async fn delete_document( Ok(HttpResponse::Accepted().json(task)) } -const PAGINATION_DEFAULT_LIMIT: fn() -> usize = || 20; - #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct BrowseQuery { #[serde(default)] offset: usize, - #[serde(default = "PAGINATION_DEFAULT_LIMIT")] + #[serde(default = "crate::routes::PAGINATION_DEFAULT_LIMIT")] limit: usize, fields: Option>>, } @@ -141,8 +139,8 @@ pub async fn get_all_documents( ) -> Result { debug!("called with params: {:?}", params); let BrowseQuery { - offset, limit, + offset, fields, } = params.into_inner(); let attributes_to_retrieve = fields.map(CS::into_inner).and_then(fold_star_or); @@ -151,10 +149,10 @@ pub async fn get_all_documents( .documents(path.into_inner(), offset, limit, attributes_to_retrieve) .await?; - debug!("returns: {:?}", documents); - Ok(HttpResponse::Ok().json(json!( - { "limit": limit, "offset": offset, "total": total, "results": documents } - ))) + let ret = PaginationView::new(offset, limit, total as usize, documents); + + debug!("returns: {:?}", ret); + Ok(HttpResponse::Ok().json(ret)) } #[derive(Deserialize, Debug)] diff --git a/meilisearch-http/src/routes/indexes/mod.rs b/meilisearch-http/src/routes/indexes/mod.rs index 37f4ee7b8..70170ebb7 100644 --- a/meilisearch-http/src/routes/indexes/mod.rs +++ b/meilisearch-http/src/routes/indexes/mod.rs @@ -12,6 +12,8 @@ use crate::extractors::authentication::{policies::*, GuardedData}; use crate::extractors::sequential_extractor::SeqHandler; use crate::task::SummarizedTaskView; +use super::Pagination; + pub mod documents; pub mod search; pub mod settings; @@ -37,38 +39,22 @@ pub fn configure(cfg: &mut web::ServiceConfig) { ); } -const PAGINATION_DEFAULT_LIMIT: fn() -> usize = || 20; - -#[derive(Deserialize, Debug)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct Paginate { - #[serde(default)] - offset: usize, - #[serde(default = "PAGINATION_DEFAULT_LIMIT")] - limit: usize, -} - pub async fn list_indexes( data: GuardedData, MeiliSearch>, - paginate: web::Query, + paginate: web::Query, ) -> Result { let search_rules = &data.filters().search_rules; let indexes: Vec<_> = data.list_indexes().await?; let nb_indexes = indexes.len(); - let indexes: Vec<_> = indexes + let iter = indexes .into_iter() - .filter(|i| search_rules.is_index_authorized(&i.uid)) - .skip(paginate.offset) - .take(paginate.limit) - .collect(); + .filter(|i| search_rules.is_index_authorized(&i.uid)); + let ret = paginate + .into_inner() + .auto_paginate_unsized(nb_indexes, iter); - debug!("returns: {:?}", indexes); - Ok(HttpResponse::Ok().json(json!({ - "results": indexes, - "offset": paginate.offset, - "limit": paginate.limit, - "total": nb_indexes, - }))) + debug!("returns: {:?}", ret); + Ok(HttpResponse::Ok().json(ret)) } #[derive(Debug, Deserialize)] diff --git a/meilisearch-http/src/routes/mod.rs b/meilisearch-http/src/routes/mod.rs index a34b7578d..1b37396e9 100644 --- a/meilisearch-http/src/routes/mod.rs +++ b/meilisearch-http/src/routes/mod.rs @@ -3,6 +3,7 @@ use std::str::FromStr; use actix_web::{web, HttpResponse}; use log::debug; use serde::{Deserialize, Serialize}; + use time::OffsetDateTime; use meilisearch_error::ResponseError; @@ -58,6 +59,86 @@ pub fn fold_star_or(content: impl IntoIterator>) -> Option usize = || 20; + +#[derive(Debug, Clone, Copy, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct Pagination { + #[serde(default)] + pub offset: usize, + #[serde(default = "PAGINATION_DEFAULT_LIMIT")] + pub limit: usize, +} + +#[derive(Debug, Clone, Serialize)] +pub struct PaginationView { + pub results: Vec, + pub offset: usize, + pub limit: usize, + pub total: usize, +} + +impl Pagination { + /// Given the full data to paginate, returns the selected section. + pub fn auto_paginate_sized( + self, + content: impl IntoIterator + ExactSizeIterator, + ) -> PaginationView + where + T: Serialize, + { + let total = content.len(); + let content: Vec<_> = content + .into_iter() + .skip(self.offset) + .take(self.limit) + .collect(); + self.format_with(total, content) + } + + /// Given an iterator and the total number of elements, returns the selected section. + pub fn auto_paginate_unsized( + self, + total: usize, + content: impl IntoIterator, + ) -> PaginationView + where + T: Serialize, + { + let content: Vec<_> = content + .into_iter() + .skip(self.offset) + .take(self.limit) + .collect(); + self.format_with(total, content) + } + + /// Given the data already paginated + the total number of elements, it stores + /// everything in a [PaginationResult]. + pub fn format_with(self, total: usize, results: Vec) -> PaginationView + where + T: Serialize, + { + PaginationView { + results, + offset: self.offset, + limit: self.limit, + total, + } + } +} + +impl PaginationView { + pub fn new(offset: usize, limit: usize, total: usize, results: Vec) -> Self { + Self { + offset, + limit, + results, + total, + } + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] #[allow(clippy::large_enum_variant)] #[serde(tag = "name")] diff --git a/meilisearch-http/tests/documents/add_documents.rs b/meilisearch-http/tests/documents/add_documents.rs index 8ef8c54fd..d6235c8b7 100644 --- a/meilisearch-http/tests/documents/add_documents.rs +++ b/meilisearch-http/tests/documents/add_documents.rs @@ -827,7 +827,7 @@ async fn add_larger_dataset() { ..Default::default() }) .await; - assert_eq!(code, 200); + assert_eq!(code, 200, "failed with `{}`", response); assert_eq!(response["results"].as_array().unwrap().len(), 77); }