1
1
use std:: ascii:: AsciiExt ;
2
- use std:: collections:: HashMap ;
3
2
use time:: Timespec ;
4
3
5
4
use conduit:: { Request , Response } ;
6
5
use conduit_router:: RequestParams ;
7
6
use diesel:: pg:: PgConnection ;
8
7
use diesel:: prelude:: * ;
9
8
use diesel;
10
- use pg:: GenericConnection ;
11
9
use pg:: rows:: Row ;
12
10
13
11
use { Model , Crate } ;
14
12
use db:: RequestTransaction ;
15
13
use schema:: * ;
16
- use util:: { RequestUtils , CargoResult , ChainError , internal} ;
17
- use util:: errors:: NotFound ;
14
+ use util:: { RequestUtils , CargoResult } ;
18
15
19
16
#[ derive( Clone , Identifiable , Queryable ) ]
20
17
pub struct Keyword {
@@ -43,12 +40,11 @@ pub struct EncodableKeyword {
43
40
}
44
41
45
42
impl Keyword {
46
- pub fn find_by_keyword ( conn : & GenericConnection , name : & str )
47
- -> CargoResult < Option < Keyword > > {
48
- let stmt = conn. prepare ( "SELECT * FROM keywords \
49
- WHERE keyword = LOWER($1)") ?;
50
- let rows = stmt. query ( & [ & name] ) ?;
51
- Ok ( rows. iter ( ) . next ( ) . map ( |r| Model :: from_row ( & r) ) )
43
+ pub fn find_by_keyword ( conn : & PgConnection , name : & str )
44
+ -> QueryResult < Keyword > {
45
+ keywords:: table
46
+ . filter ( keywords:: keyword. eq ( :: lower ( name) ) )
47
+ . first ( & * conn)
52
48
}
53
49
54
50
pub fn find_or_create_all ( conn : & PgConnection , names : & [ & str ] ) -> QueryResult < Vec < Keyword > > {
@@ -60,58 +56,26 @@ impl Keyword {
60
56
struct NewKeyword < ' a > {
61
57
keyword : & ' a str ,
62
58
}
63
- sql_function ! ( lower, lower_t, ( x: :: diesel:: types:: Text ) -> :: diesel:: types:: Text ) ;
64
59
65
- let ( lowercase_names, new_keywords) : ( Vec < _ > , Vec < _ > ) = names. iter ( )
66
- . map ( |s| ( s. to_lowercase ( ) , NewKeyword { keyword : * s } ) )
67
- . unzip ( ) ;
60
+ let lowercase_names: Vec < _ > = names. iter ( )
61
+ . map ( |s| s. to_lowercase ( ) )
62
+ . collect ( ) ;
63
+
64
+ let new_keywords: Vec < _ > = lowercase_names. iter ( )
65
+ . map ( |s| NewKeyword { keyword : s } )
66
+ . collect ( ) ;
68
67
69
68
// https://github.com/diesel-rs/diesel/issues/797
70
69
if !new_keywords. is_empty ( ) {
71
- diesel:: insert ( & new_keywords. on_conflict_do_nothing ( ) ) . into ( keywords:: table)
70
+ diesel:: insert ( & new_keywords. on_conflict_do_nothing ( ) )
71
+ . into ( keywords:: table)
72
72
. execute ( conn) ?;
73
73
}
74
- keywords:: table. filter ( lower ( keywords:: keyword) . eq ( any ( lowercase_names) ) )
74
+ keywords:: table
75
+ . filter ( :: lower ( keywords:: keyword) . eq ( any ( & lowercase_names) ) )
75
76
. load ( conn)
76
77
}
77
78
78
- pub fn find_or_insert ( conn : & GenericConnection , name : & str )
79
- -> CargoResult < Keyword > {
80
- // TODO: racy (the select then insert is not atomic)
81
- let stmt = conn. prepare ( "SELECT * FROM keywords
82
- WHERE keyword = LOWER($1)" ) ?;
83
- for row in stmt. query ( & [ & name] ) ?. iter ( ) {
84
- return Ok ( Model :: from_row ( & row) )
85
- }
86
-
87
- let stmt = conn. prepare ( "INSERT INTO keywords (keyword) VALUES (LOWER($1))
88
- RETURNING *" ) ?;
89
- let rows = stmt. query ( & [ & name] ) ?;
90
- Ok ( Model :: from_row ( & rows. iter ( ) . next ( ) . chain_error ( || {
91
- internal ( "no version returned" )
92
- } ) ?) )
93
- }
94
-
95
- pub fn all ( conn : & GenericConnection , sort : & str , limit : i64 , offset : i64 )
96
- -> CargoResult < Vec < Keyword > > {
97
-
98
- let sort_sql = match sort {
99
- "crates" => "ORDER BY crates_cnt DESC" ,
100
- _ => "ORDER BY keyword ASC" ,
101
- } ;
102
-
103
- let stmt = conn. prepare ( & format ! ( "SELECT * FROM keywords {}
104
- LIMIT $1 OFFSET $2" ,
105
- sort_sql) ) ?;
106
-
107
- let keywords: Vec < _ > = stmt. query ( & [ & limit, & offset] ) ?
108
- . iter ( )
109
- . map ( |row| Model :: from_row ( & row) )
110
- . collect ( ) ;
111
-
112
- Ok ( keywords)
113
- }
114
-
115
79
pub fn valid_name ( name : & str ) -> bool {
116
80
if name. is_empty ( ) { return false }
117
81
name. chars ( ) . next ( ) . unwrap ( ) . is_alphanumeric ( ) &&
@@ -144,47 +108,6 @@ impl Keyword {
144
108
Ok ( ( ) )
145
109
} )
146
110
}
147
-
148
- pub fn update_crate_old ( conn : & GenericConnection ,
149
- krate : & Crate ,
150
- keywords : & [ String ] ) -> CargoResult < ( ) > {
151
- let old_kws = krate. keywords ( conn) ?;
152
- let old_kws = old_kws. iter ( ) . map ( |kw| {
153
- ( & kw. keyword [ ..] , kw)
154
- } ) . collect :: < HashMap < _ , _ > > ( ) ;
155
- let new_kws = keywords. iter ( ) . map ( |k| {
156
- let kw = Keyword :: find_or_insert ( conn, k) ?;
157
- Ok ( ( k. as_str ( ) , kw) )
158
- } ) . collect :: < CargoResult < HashMap < _ , _ > > > ( ) ?;
159
-
160
- let to_rm = old_kws. iter ( ) . filter ( |& ( kw, _) | {
161
- !new_kws. contains_key ( kw)
162
- } ) . map ( |( _, v) | v. id ) . collect :: < Vec < _ > > ( ) ;
163
- let to_add = new_kws. iter ( ) . filter ( |& ( kw, _) | {
164
- !old_kws. contains_key ( kw)
165
- } ) . map ( |( _, v) | v. id ) . collect :: < Vec < _ > > ( ) ;
166
-
167
- if !to_rm. is_empty ( ) {
168
- conn. execute ( "DELETE FROM crates_keywords
169
- WHERE keyword_id = ANY($1)
170
- AND crate_id = $2" ,
171
- & [ & to_rm, & krate. id ] ) ?;
172
- }
173
-
174
- if !to_add. is_empty ( ) {
175
- let insert = to_add. iter ( ) . map ( |id| {
176
- let crate_id: i32 = krate. id ;
177
- let id: i32 = * id;
178
- format ! ( "({}, {})" , crate_id, id)
179
- } ) . collect :: < Vec < _ > > ( ) . join ( ", " ) ;
180
- conn. execute ( & format ! ( "INSERT INTO crates_keywords
181
- (crate_id, keyword_id) VALUES {}" ,
182
- insert) ,
183
- & [ ] ) ?;
184
- }
185
-
186
- Ok ( ( ) )
187
- }
188
111
}
189
112
190
113
impl Model for Keyword {
@@ -201,34 +124,49 @@ impl Model for Keyword {
201
124
202
125
/// Handles the `GET /keywords` route.
203
126
pub fn index ( req : & mut Request ) -> CargoResult < Response > {
204
- let conn = req. tx ( ) ?;
127
+ use diesel:: expression:: dsl:: sql;
128
+ use diesel:: types:: BigInt ;
129
+ use schema:: keywords;
130
+
131
+ let conn = req. db_conn ( ) ?;
205
132
let ( offset, limit) = req. pagination ( 10 , 100 ) ?;
206
133
let query = req. query ( ) ;
207
134
let sort = query. get ( "sort" ) . map ( |s| & s[ ..] ) . unwrap_or ( "alpha" ) ;
208
135
209
- let keywords = Keyword :: all ( conn, sort, limit, offset) ?;
210
- let keywords = keywords. into_iter ( ) . map ( Keyword :: encodable) . collect ( ) ;
136
+ let mut query = keywords:: table
137
+ . select ( ( keywords:: all_columns, sql :: < BigInt > ( "COUNT(*) OVER ()" ) ) )
138
+ . limit ( limit)
139
+ . offset ( offset)
140
+ . into_boxed ( ) ;
211
141
212
- // Query for the total count of keywords
213
- let total = Keyword :: count ( conn) ?;
142
+ if sort == "crates" {
143
+ query = query. order ( keywords:: crates_cnt. desc ( ) ) ;
144
+ } else {
145
+ query = query. order ( keywords:: keyword. asc ( ) ) ;
146
+ }
147
+
148
+ let data = query. load :: < ( Keyword , i64 ) > ( & * conn) ?;
149
+ let total = data. get ( 0 ) . map ( |& ( _, t) | t) . unwrap_or ( 0 ) ;
150
+ let kws = data. into_iter ( )
151
+ . map ( |( k, _) | k. encodable ( ) ) . collect :: < Vec < _ > > ( ) ;
214
152
215
153
#[ derive( RustcEncodable ) ]
216
154
struct R { keywords : Vec < EncodableKeyword > , meta : Meta }
217
155
#[ derive( RustcEncodable ) ]
218
156
struct Meta { total : i64 }
219
157
220
158
Ok ( req. json ( & R {
221
- keywords : keywords ,
159
+ keywords : kws ,
222
160
meta : Meta { total : total } ,
223
161
} ) )
224
162
}
225
163
226
164
/// Handles the `GET /keywords/:keyword_id` route.
227
165
pub fn show ( req : & mut Request ) -> CargoResult < Response > {
228
166
let name = & req. params ( ) [ "keyword_id" ] ;
229
- let conn = req. tx ( ) ?;
230
- let kw = Keyword :: find_by_keyword ( conn , name ) ? ;
231
- let kw = kw . chain_error ( || NotFound ) ?;
167
+ let conn = req. db_conn ( ) ?;
168
+
169
+ let kw = Keyword :: find_by_keyword ( & conn , name ) ?;
232
170
233
171
#[ derive( RustcEncodable ) ]
234
172
struct R { keyword : EncodableKeyword }
0 commit comments