opensearch_client/search/
mod.rs1use std::sync::Arc;
2
3use crate::{Error, OsClient};
4use opensearch_dsl::{util::ShouldSkip, InnerHitsResult, Map};
5use opensearch_dsl::{
6 Explanation, NestedIdentity, Query, SearchResponse, ShardStatistics, SortCollection, Terms,
7 TotalHits,
8};
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> From<&TypedHit<T>> for TypedHit<T> {
132 fn from(value: &TypedHit<T>) -> Self {
133 value.into()
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> = if let Ok(src) = parsed {
141 Some(src)
142 } else {
143 None
144 };
145 TypedHit {
146 explanation: hit.explanation,
147 index: hit.index,
148 id: hit.id,
149 score: hit.score,
150 nested: hit.nested,
151 source,
152 highlight: hit.highlight,
153 inner_hits: hit.inner_hits,
154 matched_queries: hit.matched_queries,
155 sort: hit.sort,
156 fields: hit.fields,
157 }
158 }
159}
160
161#[derive(Clone, Debug, Deserialize, Serialize)]
162pub struct TypedHitsMetadata<T> {
163 #[serde(default, skip_serializing_if = "Vec::is_empty")]
164 pub hits: Vec<TypedHit<T>>,
165 #[serde(default, skip_serializing_if = "Option::is_none")]
166 pub max_score: Option<f32>,
167 #[serde(default, skip_serializing_if = "Option::is_none")]
168 pub total: Option<TotalHits>,
169}
170
171impl<T> TypedHitsMetadata<T> {
172 pub fn get_total_value(&self) -> Option<u64> {
173 self.total.as_ref().map(|t| t.value)
174 }
175}
176
177impl<T> From<&TypedHitsMetadata<T>> for TypedHitsMetadata<T> {
178 fn from(value: &TypedHitsMetadata<T>) -> Self {
179 value.into()
180 }
181}
182
183impl<T> Default for TypedHitsMetadata<T> {
184 fn default() -> Self {
185 Self {
186 hits: Vec::new(),
187 max_score: None,
188 total: None,
189 }
190 }
191}
192
193impl<T: serde::de::DeserializeOwned> TypedHitsMetadata<T> {
194 pub fn from_response(hits: opensearch_dsl::HitsMetadata) -> Result<Self, crate::Error> {
195 let typed_hits = hits
196 .hits
197 .into_iter()
198 .map(|hit| TypedHit::from_hit(hit))
199 .collect::<Vec<_>>();
200
201 Ok(TypedHitsMetadata {
202 hits: typed_hits,
203 max_score: hits.max_score,
204 total: hits.total,
205 })
206 }
207}
208
209