use std::marker::PhantomData;
use serde_json::{Map, Value};
use super::{Common, Sort, common_opts, exists_q, match_all_value};
use crate::query::{AsQuery, Query, Root};
fn nested_value(path: &str, query: Value) -> Value {
let mut body = Map::new();
body.insert("path".to_string(), Value::String(path.to_string()));
body.insert("query".to_string(), query);
let mut outer = Map::new();
outer.insert("nested".to_string(), Value::Object(body));
Value::Object(outer)
}
fn bool_value(clause: &str, items: Vec<Value>) -> Value {
let mut body = Map::new();
body.insert(clause.to_string(), Value::Array(items));
let mut outer = Map::new();
outer.insert("bool".to_string(), Value::Object(body));
Value::Object(outer)
}
#[derive(Debug, Clone)]
pub struct Nested<E = Root, C = serde_json::Value> {
path: String,
_marker: PhantomData<fn() -> (E, C)>,
}
impl<E, C> Nested<E, C> {
pub fn at(path: impl Into<String>) -> Self {
Self {
path: path.into(),
_marker: PhantomData,
}
}
pub fn any(&self, query: impl AsQuery<C>) -> NestedQuery<E> {
let inner = query
.into_query()
.map_or_else(match_all_value, |q| q.to_value());
NestedQuery {
path: self.path.clone(),
query: inner,
opts: Map::new(),
common: Common::default(),
_marker: PhantomData,
}
}
pub fn all(&self, query: impl AsQuery<C>) -> Query<E> {
let inner = query
.into_query()
.map_or_else(match_all_value, |q| q.to_value());
let fails = bool_value("must_not", vec![inner]);
let nested = nested_value(&self.path, fails);
Query::leaf(bool_value("must_not", vec![nested]))
}
pub fn exists(&self) -> Query<E> {
exists_q(&self.path)
}
pub fn matching(&self, query: impl AsQuery<C>) -> NestedProjection {
NestedProjection {
path: self.path.clone(),
query: query.into_query().map(|q| q.to_value()),
sort: Vec::new(),
size: None,
from: None,
}
}
pub fn project(&self) -> NestedProjection {
NestedProjection {
path: self.path.clone(),
query: None,
sort: Vec::new(),
size: None,
from: None,
}
}
}
#[derive(Debug, Clone)]
pub struct NestedQuery<E = Root> {
path: String,
query: Value,
opts: Map<String, Value>,
common: Common,
_marker: PhantomData<fn() -> E>,
}
impl<E> NestedQuery<E> {
#[must_use]
pub fn score_mode(mut self, score_mode: impl Into<String>) -> Self {
self.opts
.insert("score_mode".to_string(), Value::String(score_mode.into()));
self
}
#[must_use]
pub fn ignore_unmapped(mut self, ignore_unmapped: bool) -> Self {
self.opts
.insert("ignore_unmapped".to_string(), Value::Bool(ignore_unmapped));
self
}
common_opts!(common);
}
impl<E> AsQuery<E> for NestedQuery<E> {
fn into_query(self) -> Option<Query<E>> {
let mut body = self.opts;
body.insert("path".to_string(), Value::String(self.path));
body.insert("query".to_string(), self.query);
self.common.write(&mut body);
let mut outer = Map::new();
outer.insert("nested".to_string(), Value::Object(body));
Some(Query::leaf(Value::Object(outer)))
}
}
#[derive(Debug, Clone)]
pub struct NestedProjection {
path: String,
query: Option<Value>,
sort: Vec<Sort>,
size: Option<u64>,
from: Option<u64>,
}
impl NestedProjection {
#[must_use]
pub fn sort(mut self, sort: Sort) -> Self {
self.sort.push(sort);
self
}
#[must_use]
pub fn size(mut self, size: u64) -> Self {
self.size = Some(size);
self
}
#[must_use]
pub fn from(mut self, from: u64) -> Self {
self.from = Some(from);
self
}
pub(crate) fn path(&self) -> &str {
&self.path
}
pub(crate) fn to_value(&self) -> Value {
let query = self.query.clone().unwrap_or_else(match_all_value);
let mut inner_hits = Map::new();
inner_hits.insert("name".to_string(), Value::String(self.path.clone()));
if let Some(size) = self.size {
inner_hits.insert("size".to_string(), Value::from(size));
}
if let Some(from) = self.from {
inner_hits.insert("from".to_string(), Value::from(from));
}
if !self.sort.is_empty() {
let keys = self.sort.iter().map(Sort::to_value).collect();
inner_hits.insert("sort".to_string(), Value::Array(keys));
}
let mut nested = Map::new();
nested.insert("path".to_string(), Value::String(self.path.clone()));
nested.insert("query".to_string(), query);
nested.insert("inner_hits".to_string(), Value::Object(inner_hits));
let mut outer = Map::new();
outer.insert("nested".to_string(), Value::Object(nested));
Value::Object(outer)
}
}