Skip to content

Commit 365c353

Browse files
committed
DocumentPartial
1 parent 245b9a4 commit 365c353

File tree

21 files changed

+383
-162
lines changed

21 files changed

+383
-162
lines changed

openapi-generator-template/reqwest/api.mustache

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,20 @@ use super::{Error, configuration, ContentType};
88
{{#operations}}
99
{{#operation}}
1010
{{#vendorExtensions.x-group-parameters}}
11-
{{#allParams}}
12-
{{#-first}}
11+
{{^allParams}}
12+
/*
13+
{{/allParams}}
1314
/// struct for passing parameters to the method [`{{operationId}}`]
1415
#[derive(Clone, Debug)]
15-
pub struct {{{operationIdCamelCase}}}Params {
16-
{{/-first}}
16+
pub struct {{{operationIdCamelCase}}}Params{{{vendorExtensions.x-rust-params-generic-parameter}}} {
17+
{{#allParams}}
1718
{{#description}}
1819
/// {{{.}}}
1920
{{/description}}
21+
{{#vendorExtensions.x-rust-type}}
22+
pub {{{paramName}}}: {{{.}}},
23+
{{/vendorExtensions.x-rust-type}}
24+
{{^vendorExtensions.x-rust-type}}
2025
pub {{{paramName}}}: {{!
2126
### Option Start
2227
}}{{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{!
@@ -27,14 +32,14 @@ pub struct {{{operationIdCamelCase}}}Params {
2732
### Models and primative types
2833
}}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}models::{{/isContainer}}{{/isPrimitiveType}}{{{dataType}}}{{/isUuid}}{{/isString}}{{!
2934
### Option End
30-
}}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{!
31-
### Comma for next arguement
32-
}}{{^-last}},{{/-last}}
33-
{{#-last}}
35+
}}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}},
36+
{{/vendorExtensions.x-rust-type}}
37+
{{/allParams}}
3438
}
35-
36-
{{/-last}}
39+
{{^allParams}}
40+
*/
3741
{{/allParams}}
42+
3843
{{/vendorExtensions.x-group-parameters}}
3944
{{/operation}}
4045
{{/operations}}
@@ -92,14 +97,19 @@ pub enum {{{operationIdCamelCase}}}Error {
9297
/// {{{.}}}
9398
{{/notes}}
9499
{{#vendorExtensions.x-group-parameters}}
95-
pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}{{{vendorExtensions.x-rust-generic-parameter}}}(configuration: &configuration::Configuration{{#allParams}}{{#-first}}, {{!
96-
### Params
97-
}}params: &{{{operationIdCamelCase}}}Params{{/-first}}{{/allParams}}{{!
100+
pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}{{{vendorExtensions.x-rust-generic-parameter}}}(configuration: &configuration::Configuration
101+
{{^allParams}}
102+
/*
103+
{{/allParams}}
104+
, params: &{{{operationIdCamelCase}}}Params{{{vendorExtensions.x-rust-params-generic-parameter}}}
105+
{{^allParams}}
106+
*/
107+
{{/allParams}}{{!
98108
### Function return type
99109
}}) -> Result<{{#vendorExtensions.x-rust-return-type}}{{{.}}}{{/vendorExtensions.x-rust-return-type}}{{^vendorExtensions.x-rust-return-type}}{{#isResponseFile}}{{#supportAsync}}reqwest::Response{{/supportAsync}}{{^supportAsync}}reqwest::blocking::Response{{/supportAsync}}{{/isResponseFile}}{{^isResponseFile}}{{#supportMultipleResponses}}ResponseContent<{{{operationIdCamelCase}}}Success>{{/supportMultipleResponses}}{{^supportMultipleResponses}}{{^returnType}}(){{/returnType}}{{{returnType}}}{{/supportMultipleResponses}}{{/isResponseFile}}{{/vendorExtensions.x-rust-return-type}}, Error<{{{operationIdCamelCase}}}Error>> {
100110
{{/vendorExtensions.x-group-parameters}}
101111
{{^vendorExtensions.x-group-parameters}}
102-
pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}{{{vendorExtensions.x-rust-generic-parameter}}}(configuration: &configuration::Configuration, {{#allParams}}{{{paramName}}}: {{!
112+
pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}{{{vendorExtensions.x-rust-generic-parameter}}}{{{vendorExtensions.x-rust-params-generic-parameter}}}(configuration: &configuration::Configuration, {{#allParams}}{{{paramName}}}: {{!
103113
### Option Start
104114
}}{{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{!
105115
### &str and Vec<&str>

preprocessed_openapi.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,9 @@ paths:
348348
description: Can be any key-value pair
349349
x-go-type: interface{}
350350
required: true
351+
x-rust-type: B
352+
x-rust-params-generic-parameter: <B>
353+
x-rust-generic-parameter: '<B: Serialize>'
351354
delete:
352355
tags:
353356
- documents
@@ -1253,6 +1256,9 @@ paths:
12531256
description: Can be any key-value pair
12541257
x-go-type: interface{}
12551258
required: true
1259+
x-rust-type: B
1260+
x-rust-params-generic-parameter: <B>
1261+
x-rust-generic-parameter: '<B: Serialize>'
12561262
responses:
12571263
'200':
12581264
description: The document referenced by the ID was updated

typesense/src/client/collection/document.rs

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//! via a parent `Collection` struct, for example:
55
//! `client.collection::<Book>().document("123")`
66
7-
use crate::{Client, Error, execute_wrapper};
7+
use crate::{Client, Error, execute_wrapper, traits};
88
use serde::{Serialize, de::DeserializeOwned};
99
use typesense_codegen::apis::documents_api;
1010

@@ -55,6 +55,28 @@ where
5555
serde_json::from_value(result_value).map_err(Error::from)
5656
}
5757

58+
/// Deletes this individual document from the collection.
59+
/// The deleted document is returned.
60+
///
61+
/// # Returns
62+
/// A `Result` containing the deleted document deserialized into `D`.
63+
pub async fn delete(&self) -> Result<D, Error<documents_api::DeleteDocumentError>> {
64+
let params = documents_api::DeleteDocumentParams {
65+
collection_name: self.collection_name.to_owned(),
66+
document_id: self.document_id.to_owned(),
67+
};
68+
69+
let result_value = execute_wrapper!(self, documents_api::delete_document, params)?;
70+
71+
// Deserialize the raw JSON value of the deleted document into T.
72+
serde_json::from_value(result_value).map_err(Error::from)
73+
}
74+
}
75+
76+
impl<'c, 'n, D> Document<'c, 'n, D>
77+
where
78+
D: traits::Document,
79+
{
5880
/// Updates this individual document. The update can be partial.
5981
/// The updated full document is returned.
6082
///
@@ -68,9 +90,9 @@ where
6890
/// # Example
6991
/// ```no_run
7092
/// # use serde::{Serialize, Deserialize};
71-
/// # use typesense::{Client, models};
93+
/// # use typesense::{Client, Typesense, models};
7294
/// # use reqwest::Url;
73-
/// # #[derive(Serialize, Deserialize)]
95+
/// # #[derive(Typesense, Serialize, Deserialize)]
7496
/// # struct Book { id: String, title: String, pages: i32 }
7597
/// #
7698
/// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
@@ -79,7 +101,7 @@ where
79101
/// # .api_key("xyz")
80102
/// # .build()
81103
/// # .unwrap();
82-
/// let book_update = serde_json::json!({ "pages": 654 });
104+
/// let book_update = BookPartial { pages: Some(654), ..Default::default() };
83105
///
84106
/// // Simple update
85107
/// let updated_book = client.collection_named::<Book>("books").document("123")
@@ -97,15 +119,15 @@ where
97119
/// # Ok(())
98120
/// # }
99121
/// ```
100-
pub async fn update<U: Serialize>(
122+
pub async fn update(
101123
&self,
102-
partial_document: U,
124+
partial_document: &D::Partial,
103125
params: Option<crate::models::DocumentIndexParameters>,
104126
) -> Result<D, Error<documents_api::UpdateDocumentError>> {
105127
let params = documents_api::UpdateDocumentParams {
106128
collection_name: self.collection_name.to_owned(),
107129
document_id: self.document_id.to_owned(),
108-
body: serde_json::to_value(partial_document)?,
130+
body: partial_document,
109131
dirty_values: params.and_then(|d| d.dirty_values),
110132
};
111133

@@ -114,21 +136,4 @@ where
114136
// Deserialize the raw JSON value of the updated document into T.
115137
serde_json::from_value(result_value).map_err(Error::from)
116138
}
117-
118-
/// Deletes this individual document from the collection.
119-
/// The deleted document is returned.
120-
///
121-
/// # Returns
122-
/// A `Result` containing the deleted document deserialized into `D`.
123-
pub async fn delete(&self) -> Result<D, Error<documents_api::DeleteDocumentError>> {
124-
let params = documents_api::DeleteDocumentParams {
125-
collection_name: self.collection_name.to_owned(),
126-
document_id: self.document_id.to_owned(),
127-
};
128-
129-
let result_value = execute_wrapper!(self, documents_api::delete_document, params)?;
130-
131-
// Deserialize the raw JSON value of the deleted document into T.
132-
serde_json::from_value(result_value).map_err(Error::from)
133-
}
134139
}

typesense/src/client/collection/documents.rs

Lines changed: 62 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use crate::{
88
Client, Error, execute_wrapper,
99
models::{DocumentIndexParameters, SearchResult},
10+
traits,
1011
};
1112
use serde::{Serialize, de::DeserializeOwned};
1213
use typesense_codegen::{
@@ -63,43 +64,6 @@ where
6364
execute_wrapper!(self, documents_api::index_document, params)
6465
}
6566

66-
/// Creates a new document in the collection.
67-
///
68-
/// Fails if a document with the same ID already exists. If the document has an `id` field
69-
/// of type `string`, it will be used as the document's ID. Otherwise, Typesense will
70-
/// auto-generate an ID. The newly indexed document is returned.
71-
///
72-
/// # Arguments
73-
/// * `document` - A serializable struct or a `serde_json::Value` representing the document to create.
74-
/// * `params` - Optional parameters like `dirty_values`.
75-
pub async fn create<U: Serialize>(
76-
&self,
77-
document: U,
78-
params: Option<DocumentIndexParameters>,
79-
) -> Result<D, Error<documents_api::IndexDocumentError>> {
80-
let doc_value = serde_json::to_value(document)?;
81-
let result_value = self.index(doc_value, "create", params).await?;
82-
serde_json::from_value(result_value).map_err(Error::from)
83-
}
84-
85-
/// Creates a new document or updates an existing one if an ID match is found.
86-
///
87-
/// This method requires the full document to be sent. For partial updates, use
88-
/// `collection().document("...").update()`. The indexed document is returned.
89-
///
90-
/// # Arguments
91-
/// * `document` - A serializable struct or a `serde_json::Value` representing the document to upsert.
92-
/// * `params` - Optional parameters like `dirty_values`.
93-
pub async fn upsert<U: Serialize>(
94-
&self,
95-
document: U,
96-
params: Option<DocumentIndexParameters>,
97-
) -> Result<D, Error<documents_api::IndexDocumentError>> {
98-
let doc_value = serde_json::to_value(document)?;
99-
let result_value = self.index(doc_value, "upsert", params).await?;
100-
serde_json::from_value(result_value).map_err(Error::from)
101-
}
102-
10367
// --- Bulk Operation Methods ---
10468

10569
/// Imports a batch of documents in JSONL format.
@@ -164,25 +128,6 @@ where
164128
execute_wrapper!(self, documents_api::delete_documents, params)
165129
}
166130

167-
/// Updates a batch of documents matching a specific filter condition.
168-
///
169-
/// # Arguments
170-
/// * `document` - A serializable struct or a `serde_json::Value` containing the fields to update.
171-
/// * `params` - A `UpdateDocumentsParameters` describing the conditions for updating documents.
172-
pub async fn update<U: Serialize>(
173-
&self,
174-
document: U,
175-
params: UpdateDocumentsParameters,
176-
) -> Result<raw_models::UpdateDocuments200Response, Error<documents_api::UpdateDocumentsError>>
177-
{
178-
let params = documents_api::UpdateDocumentsParams {
179-
collection_name: self.collection_name.to_owned(),
180-
filter_by: params.filter_by,
181-
body: serde_json::to_value(document)?,
182-
};
183-
execute_wrapper!(self, documents_api::update_documents, params)
184-
}
185-
186131
/// Searches for documents in the collection that match the given criteria.
187132
/// The search results will have their `document` field deserialized into type `D`.
188133
///
@@ -271,3 +216,64 @@ where
271216
execute_wrapper!(self, documents_api::search_collection, search_params)
272217
}
273218
}
219+
220+
impl<'c, 'n, D> Documents<'c, 'n, D>
221+
where
222+
D: traits::Document,
223+
{
224+
/// Creates a new document in the collection.
225+
///
226+
/// Fails if a document with the same ID already exists. If the document has an `id` field
227+
/// of type `string`, it will be used as the document's ID. Otherwise, Typesense will
228+
/// auto-generate an ID. The newly indexed document is returned.
229+
///
230+
/// # Arguments
231+
/// * `document` - A document struct to create.
232+
/// * `params` - Optional parameters like `dirty_values`.
233+
pub async fn create(
234+
&self,
235+
document: &D,
236+
params: Option<DocumentIndexParameters>,
237+
) -> Result<D, Error<documents_api::IndexDocumentError>> {
238+
let doc_value = serde_json::to_value(document)?;
239+
let result_value = self.index(doc_value, "create", params).await?;
240+
serde_json::from_value(result_value).map_err(Error::from)
241+
}
242+
243+
/// Creates a new document or updates an existing one if an ID match is found.
244+
///
245+
/// This method requires the full document to be sent. For partial updates, use
246+
/// `collection().document("...").update()`. The indexed document is returned.
247+
///
248+
/// # Arguments
249+
/// * `document` - A document struct to upsert.
250+
/// * `params` - Optional parameters like `dirty_values`.
251+
pub async fn upsert(
252+
&self,
253+
document: &D,
254+
params: Option<DocumentIndexParameters>,
255+
) -> Result<D, Error<documents_api::IndexDocumentError>> {
256+
let doc_value = serde_json::to_value(document)?;
257+
let result_value = self.index(doc_value, "upsert", params).await?;
258+
serde_json::from_value(result_value).map_err(Error::from)
259+
}
260+
261+
/// Updates a batch of documents matching a specific filter condition.
262+
///
263+
/// # Arguments
264+
/// * `document` - A struct containing the fields to update.
265+
/// * `params` - A `UpdateDocumentsParameters` describing the conditions for updating documents.
266+
pub async fn update(
267+
&self,
268+
document: &D::Partial,
269+
params: UpdateDocumentsParameters,
270+
) -> Result<raw_models::UpdateDocuments200Response, Error<documents_api::UpdateDocumentsError>>
271+
{
272+
let params = documents_api::UpdateDocumentsParams {
273+
collection_name: self.collection_name.to_owned(),
274+
filter_by: params.filter_by,
275+
body: document,
276+
};
277+
execute_wrapper!(self, documents_api::update_documents, params)
278+
}
279+
}

typesense/src/traits/document.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,16 @@
66
use crate::models::CollectionSchema;
77
use serde::{Serialize, de::DeserializeOwned};
88

9-
/// Trait that should implement every struct that wants to be represented as a Typesense
9+
/// Trait for partial structs
10+
pub trait DocumentPartial: Serialize {}
11+
12+
/// Trait that every struct should implement that wants to be represented as a Typesense
1013
/// Document
1114
pub trait Document: DeserializeOwned + Serialize {
1215
/// Collection name
1316
const COLLECTION_NAME: &'static str;
17+
/// A struct for partial updates
18+
type Partial: DocumentPartial;
1419

1520
/// Collection schema associated with the document.
1621
fn collection_schema() -> CollectionSchema;

typesense/src/traits/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ mod document;
44
mod field_type;
55
mod multi_search_ext;
66

7-
pub use document::Document;
7+
pub use document::*;
88
pub use field_type::*;
99
pub use multi_search_ext::MultiSearchResultExt;

0 commit comments

Comments
 (0)