@@ -116,14 +116,18 @@ pub async fn list_crates(
116
116
. left_join ( versions:: table. on ( default_versions:: version_id. eq ( versions:: id) ) )
117
117
. select ( selection) ;
118
118
119
+ let pagination: PaginationOptions = PaginationOptions :: builder ( )
120
+ . limit_page_numbers ( )
121
+ . enable_seek ( true )
122
+ . gather ( & req) ?;
123
+ let is_forward = !matches ! ( pagination. page, Page :: SeekBackward ( _) ) ;
124
+
119
125
if let Some ( q_string) = & filter_params. q_string {
120
126
if !q_string. is_empty ( ) {
121
127
let q_string = q_string. as_str ( ) ;
122
128
123
129
let sort = sort. unwrap_or ( "relevance" ) ;
124
130
125
- query = query. order ( Crate :: with_name ( q_string) . desc ( ) ) ;
126
-
127
131
if sort == "relevance" {
128
132
let q =
129
133
plainto_tsquery_with_search_config ( TsConfigurationByName ( "english" ) , q_string) ;
@@ -139,7 +143,11 @@ pub async fn list_crates(
139
143
default_versions:: num_versions. nullable ( ) ,
140
144
) ) ;
141
145
seek = Some ( Seek :: Relevance ) ;
142
- query = query. then_order_by ( rank. desc ( ) )
146
+ query = if is_forward {
147
+ query. order ( ( Crate :: with_name ( q_string) . desc ( ) , rank. desc ( ) ) )
148
+ } else {
149
+ query. order ( ( Crate :: with_name ( q_string) . asc ( ) , rank. asc ( ) ) )
150
+ }
143
151
} else {
144
152
query = query. select ( (
145
153
ALL_COLUMNS ,
@@ -152,6 +160,11 @@ pub async fn list_crates(
152
160
default_versions:: num_versions. nullable ( ) ,
153
161
) ) ;
154
162
seek = Some ( Seek :: Query ) ;
163
+ query = if is_forward {
164
+ query. order ( Crate :: with_name ( q_string) . desc ( ) )
165
+ } else {
166
+ query. order ( Crate :: with_name ( q_string) . asc ( ) )
167
+ }
155
168
}
156
169
}
157
170
}
@@ -163,31 +176,49 @@ pub async fn list_crates(
163
176
// to ensure predictable pagination behavior.
164
177
if sort == Some ( "downloads" ) {
165
178
seek = Some ( Seek :: Downloads ) ;
166
- query = query. order ( ( crate_downloads:: downloads. desc ( ) , crates:: id. desc ( ) ) )
179
+ query = if is_forward {
180
+ query. order ( ( crate_downloads:: downloads. desc ( ) , crates:: id. desc ( ) ) )
181
+ } else {
182
+ query. order ( ( crate_downloads:: downloads. asc ( ) , crates:: id. asc ( ) ) )
183
+ } ;
167
184
} else if sort == Some ( "recent-downloads" ) {
168
185
seek = Some ( Seek :: RecentDownloads ) ;
169
- query = query. order ( (
170
- recent_crate_downloads:: downloads. desc ( ) . nulls_last ( ) ,
171
- crates:: id. desc ( ) ,
172
- ) )
186
+ query = if is_forward {
187
+ query. order ( (
188
+ recent_crate_downloads:: downloads. desc ( ) . nulls_last ( ) ,
189
+ crates:: id. desc ( ) ,
190
+ ) )
191
+ } else {
192
+ query. order ( (
193
+ recent_crate_downloads:: downloads. asc ( ) . nulls_first ( ) ,
194
+ crates:: id. asc ( ) ,
195
+ ) )
196
+ } ;
173
197
} else if sort == Some ( "recent-updates" ) {
174
198
seek = Some ( Seek :: RecentUpdates ) ;
175
- query = query. order ( ( crates:: updated_at. desc ( ) , crates:: id. desc ( ) ) ) ;
199
+ query = if is_forward {
200
+ query. order ( ( crates:: updated_at. desc ( ) , crates:: id. desc ( ) ) )
201
+ } else {
202
+ query. order ( ( crates:: updated_at. asc ( ) , crates:: id. asc ( ) ) )
203
+ } ;
176
204
} else if sort == Some ( "new" ) {
177
205
seek = Some ( Seek :: New ) ;
178
- query = query. order ( ( crates:: created_at. desc ( ) , crates:: id. desc ( ) ) ) ;
206
+ query = if is_forward {
207
+ query. order ( ( crates:: created_at. desc ( ) , crates:: id. desc ( ) ) )
208
+ } else {
209
+ query. order ( ( crates:: created_at. asc ( ) , crates:: id. asc ( ) ) )
210
+ } ;
179
211
} else {
180
212
seek = seek. or ( Some ( Seek :: Name ) ) ;
181
213
// Since the name is unique value, the inherent ordering becomes naturally unique.
182
214
// Therefore, an additional auxiliary ordering column is unnecessary in this case.
183
- query = query. then_order_by ( crates:: name. asc ( ) )
215
+ query = if is_forward {
216
+ query. then_order_by ( crates:: name. asc ( ) )
217
+ } else {
218
+ query. then_order_by ( crates:: name. desc ( ) )
219
+ } ;
184
220
}
185
221
186
- let pagination: PaginationOptions = PaginationOptions :: builder ( )
187
- . limit_page_numbers ( )
188
- . enable_seek ( true )
189
- . gather ( & req) ?;
190
-
191
222
let explicit_page = matches ! ( pagination. page, Page :: Numeric ( _) ) ;
192
223
193
224
// To avoid breaking existing users, seek-based pagination is only used if an explicit page has
0 commit comments