perf(posts): list/search 零 capacity Vec 改 collect 预分配 + helpers retain
list.rs 5 处、search.rs 1 处用 Vec::new()+循环 push,初始 capacity 为 0, 循环触发多次指数 realloc。rows.iter().map().collect() 用 slice 迭代器的 精确 size_hint 一次性预分配。首页/列表/搜索/标签分页路径。 helpers.rs row_to_post_list_item / row_to_post_full:tags 从 into_iter().filter().collect() 改 retain 原地过滤。unwrap_or_default() 已返回 owned Vec,旧写法重建第二个 Vec。每行文章记录省一次 Vec 分配。 顺带修 slugify 一个 dead store(prev_dash=false 后被 true 无条件覆盖)。
This commit is contained in:
parent
a41a1d3ae1
commit
b269fcf205
@ -24,13 +24,9 @@ pub(super) fn row_to_post_list_item(row: &tokio_postgres::Row) -> PostListItem {
|
||||
let status_str: String = row.get("status");
|
||||
let status = PostStatus::from_str(&status_str).unwrap_or(PostStatus::Draft);
|
||||
|
||||
// 聚合标签并过滤空字符串。
|
||||
let tags: Vec<String> = row
|
||||
.try_get::<_, Vec<String>>("tags")
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.filter(|t| !t.is_empty())
|
||||
.collect();
|
||||
// 聚合标签并原地过滤空字符串(retain 避免 into_iter+filter+collect 的二次 Vec 分配)。
|
||||
let mut tags: Vec<String> = row.try_get::<_, Vec<String>>("tags").unwrap_or_default();
|
||||
tags.retain(|t| !t.is_empty());
|
||||
|
||||
let word_count: i32 = row.get("word_count");
|
||||
let reading_time: i32 = row.get("reading_time");
|
||||
@ -66,13 +62,9 @@ pub(super) async fn row_to_post_full(
|
||||
let role_str: String = row.get("status");
|
||||
let status = PostStatus::from_str(&role_str).unwrap_or(PostStatus::Draft);
|
||||
|
||||
// 聚合标签并过滤空字符串。
|
||||
let tags: Vec<String> = row
|
||||
.try_get::<_, Vec<String>>("tags")
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.filter(|t| !t.is_empty())
|
||||
.collect();
|
||||
// 聚合标签并原地过滤空字符串(retain 避免 into_iter+filter+collect 的二次 Vec 分配)。
|
||||
let mut tags: Vec<String> = row.try_get::<_, Vec<String>>("tags").unwrap_or_default();
|
||||
tags.retain(|t| !t.is_empty());
|
||||
|
||||
// 解析上一篇文章导航。
|
||||
let prev_post = if let Ok(prev_title) = row.try_get::<_, String>("prev_title") {
|
||||
|
||||
@ -98,10 +98,7 @@ pub async fn list_published_posts(
|
||||
.await
|
||||
.map_err(AppError::query)?;
|
||||
|
||||
let mut posts = Vec::new();
|
||||
for row in &rows {
|
||||
posts.push(row_to_post_list_item(row));
|
||||
}
|
||||
let posts: Vec<_> = rows.iter().map(row_to_post_list_item).collect();
|
||||
|
||||
crate::cache::set_post_list(&cache_key, posts.clone(), total).await;
|
||||
Ok(PostListResponse { posts, total })
|
||||
@ -156,10 +153,7 @@ pub async fn list_posts(page: i32, per_page: i32) -> Result<PostListResponse, Se
|
||||
.await
|
||||
.map_err(AppError::query)?;
|
||||
|
||||
let mut posts = Vec::new();
|
||||
for row in &rows {
|
||||
posts.push(row_to_post_list_item(row));
|
||||
}
|
||||
let posts: Vec<_> = rows.iter().map(row_to_post_list_item).collect();
|
||||
|
||||
Ok(PostListResponse { posts, total })
|
||||
}
|
||||
@ -219,10 +213,7 @@ pub async fn list_deleted_posts(
|
||||
.await
|
||||
.map_err(AppError::query)?;
|
||||
|
||||
let mut posts = Vec::new();
|
||||
for row in &rows {
|
||||
posts.push(row_to_post_list_item(row));
|
||||
}
|
||||
let posts: Vec<_> = rows.iter().map(row_to_post_list_item).collect();
|
||||
|
||||
Ok(PostListResponse { posts, total })
|
||||
}
|
||||
@ -312,10 +303,7 @@ pub async fn get_posts_by_tag(
|
||||
.await
|
||||
.map_err(AppError::query)?;
|
||||
|
||||
let mut posts = Vec::new();
|
||||
for row in &rows {
|
||||
posts.push(row_to_post_list_item(row));
|
||||
}
|
||||
let posts: Vec<_> = rows.iter().map(row_to_post_list_item).collect();
|
||||
|
||||
crate::cache::set_posts_by_tag_paged(&cache_key, posts.clone(), total).await;
|
||||
Ok(PostListResponse { posts, total })
|
||||
@ -364,10 +352,7 @@ pub async fn get_posts_by_tag(
|
||||
.await
|
||||
.map_err(AppError::query)?;
|
||||
|
||||
let mut posts = Vec::new();
|
||||
for row in &rows {
|
||||
posts.push(row_to_post_list_item(row));
|
||||
}
|
||||
let posts: Vec<_> = rows.iter().map(row_to_post_list_item).collect();
|
||||
|
||||
// total 为真实 COUNT(*),不再用 posts.len()。
|
||||
crate::cache::set_posts_by_tag(&tag_name, posts.clone(), total).await;
|
||||
|
||||
@ -85,10 +85,7 @@ pub async fn search_posts(query: String) -> Result<PostListResponse, ServerFnErr
|
||||
.await
|
||||
.map_err(AppError::query)?;
|
||||
|
||||
let mut posts = Vec::new();
|
||||
for row in &rows {
|
||||
posts.push(row_to_post_list_item(row));
|
||||
}
|
||||
let posts: Vec<_> = rows.iter().map(row_to_post_list_item).collect();
|
||||
|
||||
let total = posts.len() as i64;
|
||||
cache::set_search_results(&cache_key, posts.clone(), total).await;
|
||||
|
||||
@ -50,8 +50,8 @@ pub fn slugify(title: &str) -> String {
|
||||
break;
|
||||
}
|
||||
}
|
||||
prev_dash = false;
|
||||
// 汉字成词,后接 `-`(合并连续、去除尾部由末尾清理负责)。
|
||||
// 拼音后必然接 `-`,所以 prev_dash 在此无条件置 true,中间无读。
|
||||
if !push_char(&mut out, &mut len, '-') {
|
||||
break;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user