use std::marker::PhantomData;
use serde_json::{Map, Value};
use super::{FlussoValue, Sort, SortOrder, exists_q, kind, single};
use crate::query::{Query, Root};
fn keyword_term(value: &impl serde::Serialize) -> Value {
match serde_json::to_value(value) {
Ok(Value::String(string)) => Value::String(string),
Ok(other) => Value::String(other.to_string()),
Err(_) => Value::String(String::new()),
}
}
#[derive(Debug, Clone)]
pub struct Keyword<S = Root> {
path: String,
_scope: PhantomData<fn() -> S>,
}
impl<S> Keyword<S> {
pub fn at(path: impl Into<String>) -> Self {
Self {
path: path.into(),
_scope: PhantomData,
}
}
pub fn eq(&self, value: impl FlussoValue<kind::Keyword> + serde::Serialize) -> Query<S> {
single("term", &self.path, keyword_term(&value))
}
pub fn in_(
&self,
values: impl IntoIterator<Item = impl FlussoValue<kind::Keyword> + serde::Serialize>,
) -> Query<S> {
let array = values.into_iter().map(|v| keyword_term(&v)).collect();
single("terms", &self.path, Value::Array(array))
}
pub fn prefix(&self, value: impl Into<String>) -> Query<S> {
single("prefix", &self.path, Value::String(value.into()))
}
pub fn wildcard(&self, pattern: impl Into<String>) -> Query<S> {
single("wildcard", &self.path, Value::String(pattern.into()))
}
pub fn regexp(&self, pattern: impl Into<String>) -> Query<S> {
single("regexp", &self.path, Value::String(pattern.into()))
}
pub fn fuzzy(&self, value: impl Into<String>) -> Query<S> {
single("fuzzy", &self.path, Value::String(value.into()))
}
pub fn exists(&self) -> Query<S> {
exists_q(&self.path)
}
pub fn asc(&self) -> Sort {
Sort::new(&self.path, SortOrder::Asc)
}
pub fn desc(&self) -> Sort {
Sort::new(&self.path, SortOrder::Desc)
}
}
#[derive(Debug, Clone)]
pub struct Text<S = Root> {
path: String,
_scope: PhantomData<fn() -> S>,
}
impl<S> Text<S> {
pub fn at(path: impl Into<String>) -> Self {
Self {
path: path.into(),
_scope: PhantomData,
}
}
pub fn matches(&self, value: impl Into<String>) -> Query<S> {
single("match", &self.path, Value::String(value.into()))
}
pub fn match_phrase(&self, value: impl Into<String>) -> Query<S> {
single("match_phrase", &self.path, Value::String(value.into()))
}
pub fn match_phrase_prefix(&self, value: impl Into<String>) -> Query<S> {
single(
"match_phrase_prefix",
&self.path,
Value::String(value.into()),
)
}
pub fn matches_fuzzy(&self, value: impl Into<String>) -> Query<S> {
let mut params = Map::new();
params.insert("query".to_string(), Value::String(value.into()));
params.insert("fuzziness".to_string(), Value::String("AUTO".to_string()));
single("match", &self.path, Value::Object(params))
}
pub fn exists(&self) -> Query<S> {
exists_q(&self.path)
}
}
pub fn multi_match<S>(
query: impl Into<String>,
fields: impl IntoIterator<Item = Text<S>>,
) -> Query<S> {
let paths = fields
.into_iter()
.map(|field| Value::String(field.path))
.collect();
let mut body = Map::new();
body.insert("query".to_string(), Value::String(query.into()));
body.insert("fields".to_string(), Value::Array(paths));
let mut outer = Map::new();
outer.insert("multi_match".to_string(), Value::Object(body));
Query::leaf(Value::Object(outer))
}