flusso_query/handles/
nested.rs1use std::marker::PhantomData;
6
7use serde_json::{Map, Value};
8
9use super::{Common, NestedScoreMode, Sort, common_opts, exists_q, match_all_value};
10use crate::query::{AsQuery, Query, Root};
11
12fn nested_value(path: &str, query: Value) -> Value {
14 let mut body = Map::new();
15 body.insert("path".to_string(), Value::String(path.to_string()));
16 body.insert("query".to_string(), query);
17 let mut outer = Map::new();
18 outer.insert("nested".to_string(), Value::Object(body));
19 Value::Object(outer)
20}
21
22fn bool_value(clause: &str, items: Vec<Value>) -> Value {
24 let mut body = Map::new();
25 body.insert(clause.to_string(), Value::Array(items));
26 let mut outer = Map::new();
27 outer.insert("bool".to_string(), Value::Object(body));
28 Value::Object(outer)
29}
30
31#[derive(Debug, Clone)]
36pub struct Nested<E = Root, C = serde_json::Value> {
37 path: String,
38 _marker: PhantomData<fn() -> (E, C)>,
39}
40
41impl<E, C> Nested<E, C> {
42 pub fn at(path: impl Into<String>) -> Self {
43 Self {
44 path: path.into(),
45 _marker: PhantomData,
46 }
47 }
48
49 pub fn any(&self, query: impl AsQuery<C>) -> NestedQuery<E> {
53 let inner = query
54 .into_query()
55 .map_or_else(match_all_value, |q| q.to_value());
56 NestedQuery {
57 path: self.path.clone(),
58 query: inner,
59 opts: Map::new(),
60 common: Common::default(),
61 _marker: PhantomData,
62 }
63 }
64
65 pub fn all(&self, query: impl AsQuery<C>) -> Query<E> {
67 let inner = query
68 .into_query()
69 .map_or_else(match_all_value, |q| q.to_value());
70 let fails = bool_value("must_not", vec![inner]);
71 let nested = nested_value(&self.path, fails);
72 Query::leaf(bool_value("must_not", vec![nested]))
73 }
74
75 pub fn exists(&self) -> Query<E> {
77 exists_q(&self.path)
78 }
79
80 pub fn matching(&self, query: impl AsQuery<C>) -> NestedProjection {
83 NestedProjection {
84 path: self.path.clone(),
85 query: query.into_query().map(|q| q.to_value()),
86 sort: Vec::new(),
87 size: None,
88 from: None,
89 }
90 }
91
92 pub fn project(&self) -> NestedProjection {
94 NestedProjection {
95 path: self.path.clone(),
96 query: None,
97 sort: Vec::new(),
98 size: None,
99 from: None,
100 }
101 }
102}
103
104#[derive(Debug, Clone)]
107pub struct NestedQuery<E = Root> {
108 path: String,
109 query: Value,
110 opts: Map<String, Value>,
111 common: Common,
112 _marker: PhantomData<fn() -> E>,
113}
114
115impl<E> NestedQuery<E> {
116 #[must_use]
120 pub fn score_mode(mut self, score_mode: NestedScoreMode) -> Self {
121 self.opts.insert(
122 "score_mode".to_string(),
123 Value::String(score_mode.as_str().to_string()),
124 );
125 self
126 }
127
128 #[must_use]
130 pub fn ignore_unmapped(mut self, ignore_unmapped: bool) -> Self {
131 self.opts
132 .insert("ignore_unmapped".to_string(), Value::Bool(ignore_unmapped));
133 self
134 }
135
136 common_opts!(common);
137}
138
139impl<E> AsQuery<E> for NestedQuery<E> {
140 fn into_query(self) -> Option<Query<E>> {
141 let mut body = self.opts;
142 body.insert("path".to_string(), Value::String(self.path));
143 body.insert("query".to_string(), self.query);
144 self.common.write(&mut body);
145 let mut outer = Map::new();
146 outer.insert("nested".to_string(), Value::Object(body));
147 Some(Query::leaf(Value::Object(outer)))
148 }
149}
150
151#[derive(Debug, Clone)]
153pub struct NestedProjection {
154 path: String,
155 query: Option<Value>,
156 sort: Vec<Sort>,
157 size: Option<u64>,
158 from: Option<u64>,
159}
160
161impl NestedProjection {
162 #[must_use]
166 pub fn sort(mut self, sort: Sort) -> Self {
167 self.sort.push(sort.without_nested_context());
168 self
169 }
170
171 #[must_use]
174 pub fn sorts(mut self, sorts: impl IntoIterator<Item = Sort>) -> Self {
175 self.sort
176 .extend(sorts.into_iter().map(Sort::without_nested_context));
177 self
178 }
179
180 #[must_use]
182 pub fn size(mut self, size: u64) -> Self {
183 self.size = Some(size);
184 self
185 }
186
187 #[must_use]
189 pub fn from(mut self, from: u64) -> Self {
190 self.from = Some(from);
191 self
192 }
193
194 pub(crate) fn path(&self) -> &str {
195 &self.path
196 }
197
198 pub(crate) fn to_value(&self) -> Value {
201 let query = self.query.clone().unwrap_or_else(match_all_value);
202 let mut inner_hits = Map::new();
203 inner_hits.insert("name".to_string(), Value::String(self.path.clone()));
204 if let Some(size) = self.size {
205 inner_hits.insert("size".to_string(), Value::from(size));
206 }
207 if let Some(from) = self.from {
208 inner_hits.insert("from".to_string(), Value::from(from));
209 }
210 if !self.sort.is_empty() {
211 let keys = self.sort.iter().map(Sort::to_value).collect();
212 inner_hits.insert("sort".to_string(), Value::Array(keys));
213 }
214 let mut nested = Map::new();
215 nested.insert("path".to_string(), Value::String(self.path.clone()));
216 nested.insert("query".to_string(), query);
217 nested.insert("inner_hits".to_string(), Value::Object(inner_hits));
218 let mut outer = Map::new();
219 outer.insert("nested".to_string(), Value::Object(nested));
220 Value::Object(outer)
221 }
222}