opensearch_client/search/
mod.rs1use std::sync::Arc;
2
3use crate::{Error, OsClient};
4use opensearch_dsl::{
5 Explanation, NestedIdentity, Query, SearchResponse, ShardStatistics, SortCollection, Terms,
6 TotalHits,
7};
8use opensearch_dsl::{InnerHitsResult, Map, util::ShouldSkip};
9use serde::{Deserialize, Serialize};
10use serde_json::Value;
11
12pub struct SearchAfterState {
15 pub client: Arc<OsClient>,
16 pub index: String,
17 pub stop: bool,
18 pub size: u64,
19 pub query: Query,
20 pub sort: SortCollection,
21 pub search_after: Option<Terms>,
22}
23
24#[derive(Clone, Debug, Deserialize, Serialize, Default)]
25pub struct TypedSearchResult<T> {
26 #[serde(default)]
27 pub hits: TypedHitsMetadata<T>,
28 #[serde(
29 rename = "_scroll_id",
30 default,
31 skip_serializing_if = "Option::is_none"
32 )]
33 pub scroll_id: Option<String>,
34 #[serde(rename = "_shards", default)]
35 pub shards: ShardStatistics,
36 #[serde(default)]
37 pub timed_out: bool,
38 #[serde(default)]
39 pub took: u32,
40}
41
42impl<T: serde::de::DeserializeOwned > TypedSearchResult<T> {
43 pub fn from_response(response: SearchResponse) -> Result<Self, crate::Error> {
44 let hits: TypedHitsMetadata<T> = TypedHitsMetadata::from_response(response.hits)?;
47
48 Ok(TypedSearchResult {
49 hits,
51 scroll_id: response.scroll_id,
52 shards: response.shards,
53 timed_out: response.timed_out,
54 took: response.took,
55 })
56 }
57}
58
59#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
60pub struct TypedHit<T> {
61 #[serde(
63 default,
64 skip_serializing_if = "ShouldSkip::should_skip",
65 rename = "_explanation"
66 )]
67 pub explanation: Option<Explanation>,
68
69 #[serde(
71 default,
72 skip_serializing_if = "ShouldSkip::should_skip",
73 rename = "_index"
74 )]
75 pub index: String,
76
77 #[serde(
79 default,
80 skip_serializing_if = "ShouldSkip::should_skip",
81 rename = "_id"
82 )]
83 pub id: String,
84
85 #[serde(
88 default,
89 skip_serializing_if = "ShouldSkip::should_skip",
90 rename = "_score"
91 )]
92 pub score: Option<f32>,
93
94 #[serde(
96 default,
97 skip_serializing_if = "ShouldSkip::should_skip",
98 rename = "_nested"
99 )]
100 pub nested: Option<NestedIdentity>,
101
102 #[serde(
104 default,
105 skip_serializing_if = "ShouldSkip::should_skip",
106 rename = "_source"
107 )]
108 pub source: Option<T>,
109
110 #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
112 pub highlight: Map<String, Vec<String>>,
113
114 #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
116 pub inner_hits: Map<String, InnerHitsResult>,
117
118 #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
120 pub matched_queries: Vec<String>,
121
122 #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
124 pub sort: Vec<serde_json::Value>,
125
126 #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
128 pub fields: Map<String, serde_json::Value>,
129}
130
131impl<T: Clone> From<&TypedHit<T>> for TypedHit<T> {
132 fn from(value: &TypedHit<T>) -> Self {
133 value.clone()
134 }
135}
136
137impl<T: serde::de::DeserializeOwned> TypedHit<T> {
138 pub fn from_hit(hit: opensearch_dsl::Hit) -> TypedHit<T> {
139 let parsed: Result<T, serde_json::Error> = hit.source.parse();
140 let source: Option<T> = parsed.ok();
141 TypedHit {
142 explanation: hit.explanation,
143 index: hit.index,
144 id: hit.id,
145 score: hit.score,
146 nested: hit.nested,
147 source,
148 highlight: hit.highlight,
149 inner_hits: hit.inner_hits,
150 matched_queries: hit.matched_queries,
151 sort: hit.sort,
152 fields: hit.fields,
153 }
154 }
155}
156
157#[derive(Clone, Debug, Deserialize, Serialize)]
158pub struct TypedHitsMetadata<T> {
159 #[serde(default, skip_serializing_if = "Vec::is_empty")]
160 pub hits: Vec<TypedHit<T>>,
161 #[serde(default, skip_serializing_if = "Option::is_none")]
162 pub max_score: Option<f32>,
163 #[serde(default, skip_serializing_if = "Option::is_none")]
164 pub total: Option<TotalHits>,
165}
166
167impl<T> TypedHitsMetadata<T> {
168 pub fn get_total_value(&self) -> Option<u64> {
169 self.total.as_ref().map(|t| t.value)
170 }
171}
172
173impl<T: Clone> From<&TypedHitsMetadata<T>> for TypedHitsMetadata<T> {
174 fn from(value: &TypedHitsMetadata<T>) -> Self {
175 value.clone()
176 }
177}
178
179impl<T> Default for TypedHitsMetadata<T> {
180 fn default() -> Self {
181 Self {
182 hits: Vec::new(),
183 max_score: None,
184 total: None,
185 }
186 }
187}
188
189impl<T: serde::de::DeserializeOwned> TypedHitsMetadata<T> {
190 pub fn from_response(hits: opensearch_dsl::HitsMetadata) -> Result<Self, crate::Error> {
191 let typed_hits = hits
192 .hits
193 .into_iter()
194 .map(|hit| TypedHit::from_hit(hit))
195 .collect::<Vec<_>>();
196
197 Ok(TypedHitsMetadata {
198 hits: typed_hits,
199 max_score: hits.max_score,
200 total: hits.total,
201 })
202 }
203}
204
205