use super::expr::{Expr, ScalarFn};
use super::query::{Op, WhereExpr};
use super::value::SqlValue;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Weight {
A,
B,
C,
D,
}
impl Weight {
fn as_literal(self) -> Expr {
let s = match self {
Self::A => "A",
Self::B => "B",
Self::C => "C",
Self::D => "D",
};
Expr::Literal(SqlValue::String(s.to_owned()))
}
}
#[derive(Debug, Clone)]
pub struct SearchVector {
expr: Expr,
}
impl SearchVector {
#[must_use]
pub fn single(text: impl Into<Expr>) -> Self {
Self {
expr: Expr::Function {
kind: ScalarFn::ToTsVector,
args: vec![text.into()],
},
}
}
#[must_use]
pub fn weighted<I, E>(pairs: I) -> Self
where
I: IntoIterator<Item = (E, Weight)>,
E: Into<Expr>,
{
let weighted: Vec<Expr> = pairs
.into_iter()
.map(|(text, w)| Expr::Function {
kind: ScalarFn::SetWeight,
args: vec![
Expr::Function {
kind: ScalarFn::ToTsVector,
args: vec![text.into()],
},
w.as_literal(),
],
})
.collect();
let expr = if weighted.len() == 1 {
weighted.into_iter().next().expect("weighted.len() == 1")
} else {
Expr::Function {
kind: ScalarFn::TsConcat,
args: weighted,
}
};
Self { expr }
}
#[must_use]
pub fn as_expr(&self) -> &Expr {
&self.expr
}
#[must_use]
pub fn matches(&self, query: &SearchQuery) -> WhereExpr {
WhereExpr::ExprCompare {
lhs: self.expr.clone(),
op: Op::Search,
rhs: query.as_expr().clone(),
}
}
}
#[derive(Debug, Clone)]
pub struct SearchQuery {
expr: Expr,
}
impl SearchQuery {
#[must_use]
pub fn plain(text: impl Into<String>) -> Self {
Self::from_parser(ScalarFn::PlainToTsQuery, text)
}
#[must_use]
pub fn phrase(text: impl Into<String>) -> Self {
Self::from_parser(ScalarFn::PhraseToTsQuery, text)
}
#[must_use]
pub fn websearch(text: impl Into<String>) -> Self {
Self::from_parser(ScalarFn::WebsearchToTsQuery, text)
}
#[must_use]
pub fn raw(text: impl Into<String>) -> Self {
Self::from_parser(ScalarFn::ToTsQuery, text)
}
fn from_parser(kind: ScalarFn, text: impl Into<String>) -> Self {
Self {
expr: Expr::Function {
kind,
args: vec![Expr::Literal(SqlValue::String(text.into()))],
},
}
}
#[must_use]
pub fn as_expr(&self) -> &Expr {
&self.expr
}
}
#[derive(Debug, Clone)]
pub struct SearchRank;
impl SearchRank {
#[must_use]
pub fn new(vector: &SearchVector, query: &SearchQuery) -> Expr {
Expr::Function {
kind: ScalarFn::TsRank,
args: vec![vector.expr.clone(), query.expr.clone()],
}
}
#[must_use]
pub fn cover_density(vector: &SearchVector, query: &SearchQuery) -> Expr {
Expr::Function {
kind: ScalarFn::TsRankCd,
args: vec![vector.expr.clone(), query.expr.clone()],
}
}
}
#[derive(Debug, Clone)]
pub struct SearchHeadline;
impl SearchHeadline {
#[must_use]
pub fn new(doc: impl Into<Expr>, query: &SearchQuery) -> Expr {
Expr::Function {
kind: ScalarFn::TsHeadline,
args: vec![doc.into(), query.expr.clone()],
}
}
#[must_use]
pub fn with_options(
doc: impl Into<Expr>,
query: &SearchQuery,
options: impl Into<String>,
) -> Expr {
Expr::Function {
kind: ScalarFn::TsHeadline,
args: vec![
doc.into(),
query.expr.clone(),
Expr::Literal(SqlValue::String(options.into())),
],
}
}
}