1
- //! Implementation of `/genes/search` that allows to search for genes by symbol etc.
1
+ //! Implementation of endpoint `/api/v1/genes/search`.
2
+ //!
3
+ //! Also includes the implementation of the `/genes/search` endpoint (deprecated).
2
4
//!
3
5
//! Gene identifiers (HGNC, NCBI, ENSEMBL) must match. As for symbols and names, the
4
6
//! search string may also be a substring.
5
7
use actix_web:: {
6
8
get,
7
9
web:: { self , Data , Json , Path } ,
8
- Responder ,
9
10
} ;
10
11
11
12
use crate :: server:: run:: GeneNames ;
@@ -15,19 +16,19 @@ use serde_with::{formats::CommaSeparator, StringWithSeparator};
15
16
16
17
/// The allowed fields to search in.
17
18
#[ derive(
18
- serde:: Serialize ,
19
- serde:: Deserialize ,
20
- strum:: Display ,
21
- strum:: EnumString ,
22
19
Debug ,
23
20
Clone ,
24
21
Copy ,
25
22
PartialEq ,
26
23
Eq ,
24
+ strum:: Display ,
25
+ strum:: EnumString ,
26
+ serde:: Serialize ,
27
+ serde:: Deserialize ,
28
+ utoipa:: ToSchema ,
27
29
) ]
28
30
#[ serde( rename_all = "snake_case" ) ]
29
- #[ strum( serialize_all = "snake_case" ) ]
30
- enum Fields {
31
+ pub ( crate ) enum GenesFields {
31
32
/// HGNC ID field
32
33
HgncId ,
33
34
/// Symbol field
@@ -47,46 +48,48 @@ enum Fields {
47
48
/// Parameters for `handle`.
48
49
#[ serde_with:: skip_serializing_none]
49
50
#[ serde_with:: serde_as]
50
- #[ derive( serde:: Serialize , serde:: Deserialize , Debug , Clone ) ]
51
+ #[ derive(
52
+ Debug , Clone , serde:: Serialize , serde:: Deserialize , utoipa:: ToSchema , utoipa:: IntoParams ,
53
+ ) ]
51
54
#[ serde( rename_all = "snake_case" ) ]
52
- struct Request {
55
+ pub ( crate ) struct GenesSearchQuery {
53
56
/// The string to search for.
54
57
pub q : String ,
55
58
/// The fields to search in.
56
- #[ serde_as( as = "Option<StringWithSeparator::<CommaSeparator, Fields >>" ) ]
57
- pub fields : Option < Vec < Fields > > ,
59
+ #[ serde_as( as = "Option<StringWithSeparator::<CommaSeparator, GenesFields >>" ) ]
60
+ pub fields : Option < Vec < GenesFields > > ,
58
61
/// Enable case sensitive search.
59
62
pub case_sensitive : Option < bool > ,
60
63
}
61
64
62
65
/// A scored result.
63
- #[ derive( serde:: Serialize , serde:: Deserialize , Debug , Clone ) ]
64
- struct Scored < T > {
66
+ #[ derive( Debug , Clone , serde:: Serialize , serde:: Deserialize , utoipa :: ToSchema ) ]
67
+ pub ( crate ) struct Scored < T > {
65
68
/// The score.
66
69
pub score : f32 ,
67
70
/// The result.
68
71
pub data : T ,
69
72
}
70
73
74
+ /// Alias for scored genes names.
75
+ pub ( crate ) type GenesScoredGeneNames = Scored < GeneNames > ;
76
+
71
77
/// Result for `handle`.
72
- #[ derive( serde:: Serialize , serde:: Deserialize , Debug , Clone ) ]
78
+ #[ derive( Debug , Clone , serde:: Serialize , serde:: Deserialize , utoipa :: ToSchema ) ]
73
79
#[ serde_with:: skip_serializing_none]
74
- struct Container {
75
- // TODO: add data version
80
+ pub ( crate ) struct GenesSearchResponse {
76
81
/// The resulting gene information.
77
- pub genes : Vec < Scored < GeneNames > > ,
82
+ pub genes : Vec < GenesScoredGeneNames > ,
78
83
}
79
84
80
- /// Query for annotations for one variant.
81
- #[ allow( clippy:: option_map_unit_fn) ]
82
- #[ get( "/genes/search" ) ]
83
- async fn handle (
85
+ /// Implementation of both endpoints.
86
+ async fn handle_impl (
84
87
data : Data < crate :: server:: run:: WebServerData > ,
85
88
_path : Path < ( ) > ,
86
- query : web:: Query < Request > ,
87
- ) -> actix_web:: Result < impl Responder , CustomError > {
89
+ query : web:: Query < GenesSearchQuery > ,
90
+ ) -> actix_web:: Result < Json < GenesSearchResponse > , CustomError > {
88
91
if query. q . len ( ) < 2 {
89
- return Ok ( Json ( Container {
92
+ return Ok ( Json ( GenesSearchResponse {
90
93
// server_version: VERSION.to_string(),
91
94
// builder_version,
92
95
genes : Vec :: new ( ) ,
@@ -120,35 +123,36 @@ async fn handle(
120
123
val. to_lowercase ( ) . contains ( & q)
121
124
}
122
125
} ;
123
- let fields: Vec < Fields > = if let Some ( fields) = query. fields . as_ref ( ) {
126
+ let fields: Vec < GenesFields > = if let Some ( fields) = query. fields . as_ref ( ) {
124
127
fields. clone ( )
125
128
} else {
126
129
Vec :: new ( )
127
130
} ;
128
131
129
132
// The fields contain the given field or are empty.
130
- let fields_contains = |field : & Fields | -> bool { fields. is_empty ( ) || fields. contains ( field) } ;
133
+ let fields_contains =
134
+ |field : & GenesFields | -> bool { fields. is_empty ( ) || fields. contains ( field) } ;
131
135
132
136
let mut genes = genes_db
133
137
. data
134
138
. gene_names
135
139
. iter ( )
136
140
. map ( |gn| -> Scored < GeneNames > {
137
- let score = if ( fields_contains ( & Fields :: HgncId ) && equals_q ( & gn. hgnc_id ) )
138
- || ( fields_contains ( & Fields :: Symbol ) && equals_q ( & gn. symbol ) )
139
- || ( fields_contains ( & Fields :: Symbol ) && equals_q ( & gn. symbol ) )
140
- || ( fields_contains ( & Fields :: Name ) && equals_q ( & gn. name ) )
141
- || ( fields_contains ( & Fields :: EnsemblGeneId )
141
+ let score = if ( fields_contains ( & GenesFields :: HgncId ) && equals_q ( & gn. hgnc_id ) )
142
+ || ( fields_contains ( & GenesFields :: Symbol ) && equals_q ( & gn. symbol ) )
143
+ || ( fields_contains ( & GenesFields :: Symbol ) && equals_q ( & gn. symbol ) )
144
+ || ( fields_contains ( & GenesFields :: Name ) && equals_q ( & gn. name ) )
145
+ || ( fields_contains ( & GenesFields :: EnsemblGeneId )
142
146
&& gn. ensembl_gene_id . iter ( ) . any ( |s| equals_q ( s) ) )
143
- || ( fields_contains ( & Fields :: NcbiGeneId )
147
+ || ( fields_contains ( & GenesFields :: NcbiGeneId )
144
148
&& gn. ncbi_gene_id . iter ( ) . any ( |s| equals_q ( s) ) )
145
149
{
146
150
1f32
147
- } else if fields_contains ( & Fields :: Symbol ) && contains_q ( & gn. symbol ) {
151
+ } else if fields_contains ( & GenesFields :: Symbol ) && contains_q ( & gn. symbol ) {
148
152
q. len ( ) as f32 / gn. symbol . len ( ) as f32
149
- } else if fields_contains ( & Fields :: Name ) && contains_q ( & gn. name ) {
153
+ } else if fields_contains ( & GenesFields :: Name ) && contains_q ( & gn. name ) {
150
154
q. len ( ) as f32 / gn. name . len ( ) as f32
151
- } else if fields_contains ( & Fields :: AliasSymbol )
155
+ } else if fields_contains ( & GenesFields :: AliasSymbol )
152
156
&& gn. alias_symbol . iter ( ) . any ( |s| contains_q ( s) )
153
157
{
154
158
gn. alias_symbol
@@ -162,7 +166,7 @@ async fn handle(
162
166
} )
163
167
. max_by ( |a, b| a. partial_cmp ( b) . unwrap_or ( std:: cmp:: Ordering :: Equal ) )
164
168
. unwrap_or ( 0f32 )
165
- } else if fields_contains ( & Fields :: AliasName )
169
+ } else if fields_contains ( & GenesFields :: AliasName )
166
170
&& gn. alias_name . iter ( ) . any ( |s| contains_q ( s) )
167
171
{
168
172
gn. alias_name
@@ -194,9 +198,38 @@ async fn handle(
194
198
. unwrap_or ( std:: cmp:: Ordering :: Equal )
195
199
} ) ;
196
200
197
- Ok ( Json ( Container {
201
+ Ok ( Json ( GenesSearchResponse {
198
202
// server_version: VERSION.to_string(),
199
203
// builder_version,
200
204
genes,
201
205
} ) )
202
206
}
207
+
208
+ /// Search for genes.
209
+ #[ get( "/genes/search" ) ]
210
+ async fn handle (
211
+ data : Data < crate :: server:: run:: WebServerData > ,
212
+ path : Path < ( ) > ,
213
+ query : web:: Query < GenesSearchQuery > ,
214
+ ) -> actix_web:: Result < Json < GenesSearchResponse > , CustomError > {
215
+ handle_impl ( data, path, query) . await
216
+ }
217
+
218
+ /// Search for genes.
219
+ #[ utoipa:: path(
220
+ get,
221
+ operation_id = "genesSearch" ,
222
+ params( GenesSearchQuery ) ,
223
+ responses(
224
+ ( status = 200 , description = "Genes search results." , body = GenesSearchResponse ) ,
225
+ ( status = 500 , description = "Internal server error." , body = CustomError )
226
+ )
227
+ ) ]
228
+ #[ get( "/api/v1/genes/search" ) ]
229
+ async fn handle_with_openapi (
230
+ data : Data < crate :: server:: run:: WebServerData > ,
231
+ path : Path < ( ) > ,
232
+ query : web:: Query < GenesSearchQuery > ,
233
+ ) -> actix_web:: Result < Json < GenesSearchResponse > , CustomError > {
234
+ handle_impl ( data, path, query) . await
235
+ }
0 commit comments