Skip to main content

opensearch_client/search/
mod.rs

1use 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
12/// Represents the state of a search operation that uses the "search after"
13/// feature.
14pub 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 /*+ std::default::Default*/> TypedSearchResult<T> {
43    pub fn from_response(response: SearchResponse) -> Result<Self, crate::Error> {
44        // Implement conversion logic from SearchSuccess to TypedSearchResult<T>
45        // Example stub:
46        let hits: TypedHitsMetadata<T> = TypedHitsMetadata::from_response(response.hits)?;
47
48        Ok(TypedSearchResult {
49            // fill fields from response
50            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    /// Search explanation
62    #[serde(
63        default,
64        skip_serializing_if = "ShouldSkip::should_skip",
65        rename = "_explanation"
66    )]
67    pub explanation: Option<Explanation>,
68
69    /// Document index
70    #[serde(
71        default,
72        skip_serializing_if = "ShouldSkip::should_skip",
73        rename = "_index"
74    )]
75    pub index: String,
76
77    /// Document ID
78    #[serde(
79        default,
80        skip_serializing_if = "ShouldSkip::should_skip",
81        rename = "_id"
82    )]
83    pub id: String,
84
85    /// Document score. [`None`] when documents are implicitly sorted by a
86    /// field other than `_score`
87    #[serde(
88        default,
89        skip_serializing_if = "ShouldSkip::should_skip",
90        rename = "_score"
91    )]
92    pub score: Option<f32>,
93
94    /// Nested document identity
95    #[serde(
96        default,
97        skip_serializing_if = "ShouldSkip::should_skip",
98        rename = "_nested"
99    )]
100    pub nested: Option<NestedIdentity>,
101
102    /// Document source
103    #[serde(
104        default,
105        skip_serializing_if = "ShouldSkip::should_skip",
106        rename = "_source"
107    )]
108    pub source: Option<T>,
109
110    /// Highlighted matches
111    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
112    pub highlight: Map<String, Vec<String>>,
113
114    /// Inner hits
115    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
116    pub inner_hits: Map<String, InnerHitsResult>,
117
118    /// Matched queries
119    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
120    pub matched_queries: Vec<String>,
121
122    /// Values document was sorted by
123    #[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
124    pub sort: Vec<serde_json::Value>,
125
126    /// Field values for the documents. Need to be specified in the request
127    #[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// impl<T> From<&TypedHitsMetadata<T>> for TypedHitsMetadata<T> {
206//     fn from(value: &TypedHitsMetadata<T>) -> Self {
207//         value.clone()
208//     }
209// }
210
211// impl<T> SearchResult<T> {
212//     pub fn builder() -> builder::SearchResult<T> {
213//         builder::SearchResult::default()
214//     }
215// }