mod stages;
use crate::columnar::Columns;
use crate::error::Result;
use crate::filter::FilterInput;
use crate::pagination;
use crate::search::{self, FuzzyMode, SearchFieldMode, TrigramIndex};
use crate::sort::SortSpec;
use crate::value::Value;
use stages::{filter_stage, sort_stage};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Page {
pub indices: Vec<usize>,
pub total: u64,
pub page: u64,
pub pages: u64,
pub has_next: bool,
pub has_previous: bool,
}
pub struct SearchStage<'a> {
pub query: &'a str,
pub fields: &'a [String],
pub mode: SearchFieldMode,
pub fuzzy: FuzzyMode,
pub threshold: i64,
pub index: Option<&'a TrigramIndex>,
}
pub fn offset_page(
items: &[Value],
columns: Option<&Columns>,
filter: Option<&FilterInput>,
sort_specs: &[SortSpec],
page: u64,
limit: u64,
) -> Result<Page> {
offset_page_searched(items, columns, filter, None, sort_specs, page, limit)
}
#[allow(clippy::too_many_arguments)]
pub fn offset_page_searched(
items: &[Value],
columns: Option<&Columns>,
filter: Option<&FilterInput>,
search: Option<&SearchStage>,
sort_specs: &[SortSpec],
page: u64,
limit: u64,
) -> Result<Page> {
let filtered = filter_stage(items, columns, filter)?;
let matched = match search {
Some(s) => search::retain_matching(
items,
&filtered,
s.query,
s.fields,
s.mode,
s.fuzzy,
s.threshold,
s.index,
)?,
None => filtered,
};
let limit = pagination::Limit::new(limit)?;
let total = matched.len() as u64; let meta = pagination::offset_meta(page, limit, total);
let start = (pagination::offset(page, limit) as usize).min(matched.len());
let end = start
.saturating_add(limit.get() as usize)
.min(matched.len());
let indices = sort_stage(items, columns, matched, sort_specs, Some(end))?;
Ok(Page {
indices: indices[start..end].to_vec(),
total,
page,
pages: meta.pages,
has_next: meta.has_next,
has_previous: meta.has_previous,
})
}
#[cfg(test)]
mod tests;