fix: replaced several instances if cloning, reduced allocations

This commit is contained in:
Per Stark
2026-06-06 19:45:18 +02:00
parent ac0d34bfbd
commit 60cf63292a
12 changed files with 254 additions and 110 deletions
+43 -22
View File
@@ -40,7 +40,7 @@ use crate::{
template_with_headers, TemplateResponse, TemplateResult, ResponseResult,
},
},
utils::pagination::{paginate_items, Pagination},
utils::pagination::{paginate_items, paginate_slice, Pagination},
};
use url::form_urlencoded;
@@ -196,16 +196,18 @@ pub async fn create_knowledge_entity(
let source_id = format!("manual::{}", Uuid::new_v4());
let new_entity = KnowledgeEntity::new(
source_id,
name.clone(),
description.clone(),
name,
description,
entity_type,
None,
user.id.clone(),
);
let new_entity_id = new_entity.id.clone();
KnowledgeEntity::store_with_embedding(new_entity.clone(), embedding, &state.db).await?;
KnowledgeEntity::store_with_embedding(new_entity, embedding, &state.db).await?;
let relationship_type = relationship_type_or_default(form.relationship_type.as_deref());
let user_id = user.id.clone();
debug!("form: {:?}", form);
if !form.relationship_ids.is_empty() {
@@ -217,7 +219,7 @@ pub async fn create_knowledge_entity(
let mut unique_ids: HashSet<String> = HashSet::new();
for target_id in form.relationship_ids {
if target_id == new_entity.id {
if target_id == new_entity_id {
continue;
}
if !valid_ids.contains(&target_id) {
@@ -228,10 +230,10 @@ pub async fn create_knowledge_entity(
}
let relationship = KnowledgeRelationship::new(
new_entity.id.clone(),
new_entity_id.clone(),
target_id,
user.id.clone(),
format!("manual::{}", new_entity.id),
user_id.clone(),
format!("manual::{new_entity_id}"),
relationship_type.clone(),
);
relationship.store_relationship(&state.db).await?;
@@ -385,7 +387,7 @@ async fn suggest_related_entities(
let suggestion_min_rrf_score = 1.0 / (tuning.chunk_rrf_k + 1.0);
let (vector_rows, fts_rows) = tokio::try_join!(
KnowledgeEntity::vector_search(take, embedding, db, user_id),
KnowledgeEntity::vector_search(take, &embedding, db, user_id),
async {
if fts_enabled {
KnowledgeEntity::fts_search(take, &fts_query, db, user_id).await
@@ -480,10 +482,13 @@ fn build_relationship_options(
options
}
fn build_relationship_table_data(
entities: Vec<KnowledgeEntity>,
fn build_relationship_rows(
relationships: Vec<KnowledgeRelationship>,
) -> RelationshipTableData {
) -> (
Vec<RelationshipTableRow>,
Vec<String>,
String,
) {
let relationship_type_options = collect_relationship_type_options(&relationships);
let mut frequency: HashMap<String, usize> = HashMap::new();
let relationships = relationships
@@ -503,7 +508,25 @@ fn build_relationship_table_data(
.collect();
let default_relationship_type = frequency
.into_iter()
.max_by_key(|(_, count)| *count).map_or_else(|| DEFAULT_RELATIONSHIP_TYPE.to_string(), |(label, _)| label);
.max_by_key(|(_, count)| *count)
.map_or_else(
|| DEFAULT_RELATIONSHIP_TYPE.to_string(),
|(label, _)| label,
);
(
relationships,
relationship_type_options,
default_relationship_type,
)
}
fn build_relationship_table_data(
entities: Vec<KnowledgeEntity>,
relationships: Vec<KnowledgeRelationship>,
) -> RelationshipTableData {
let (relationships, relationship_type_options, default_relationship_type) =
build_relationship_rows(relationships);
RelationshipTableData {
entities,
@@ -532,7 +555,7 @@ async fn build_knowledge_base_data(
};
let (visible_entities, pagination) =
paginate_items(entities.clone(), params.page, KNOWLEDGE_ENTITIES_PER_PAGE);
paginate_slice(&entities, params.page, KNOWLEDGE_ENTITIES_PER_PAGE);
let page_query = {
let mut serializer = form_urlencoded::Serializer::new(String::new());
@@ -551,17 +574,15 @@ async fn build_knowledge_base_data(
};
let relationships = User::get_knowledge_relationships(&user.id, &state.db).await?;
let entity_id_set: HashSet<String> = entities.iter().map(|e| e.id.clone()).collect();
let entity_id_set: HashSet<&str> = entities.iter().map(|e| e.id.as_str()).collect();
let filtered_relationships: Vec<KnowledgeRelationship> = relationships
.into_iter()
.filter(|rel| entity_id_set.contains(&rel.in_) && entity_id_set.contains(&rel.out))
.filter(|rel| {
entity_id_set.contains(rel.in_.as_str()) && entity_id_set.contains(rel.out.as_str())
})
.collect();
let RelationshipTableData {
entities: _,
relationships,
relationship_type_options,
default_relationship_type,
} = build_relationship_table_data(entities.clone(), filtered_relationships);
let (relationships, relationship_type_options, default_relationship_type) =
build_relationship_rows(filtered_relationships);
Ok(KnowledgeBaseData {
entities,
+52 -1
View File
@@ -57,6 +57,47 @@ impl Pagination {
}
}
/// Returns a cloned page slice and pagination metadata without consuming the source list.
pub fn paginate_slice<T: Clone>(
items: &[T],
requested_page: Option<usize>,
per_page: usize,
) -> (Vec<T>, Pagination) {
let per_page = per_page.max(1);
let total_items = items.len();
let total_pages = if total_items == 0 {
0
} else {
total_items
.saturating_sub(1)
.checked_div(per_page)
.unwrap_or(0)
.saturating_add(1)
};
let mut current_page = requested_page.unwrap_or(1);
if current_page == 0 {
current_page = 1;
}
if total_pages > 0 {
current_page = current_page.min(total_pages);
} else {
current_page = 1;
}
let offset = if total_pages == 0 {
0
} else {
per_page.saturating_mul(current_page.saturating_sub(1))
};
let page_items: Vec<T> = items.iter().skip(offset).take(per_page).cloned().collect();
let page_len = page_items.len();
let pagination = Pagination::new(current_page, per_page, total_items, total_pages, page_len);
(page_items, pagination)
}
/// Returns the items for the requested page along with pagination metadata.
pub fn paginate_items<T>(
items: Vec<T>,
@@ -96,7 +137,7 @@ pub fn paginate_items<T>(
#[cfg(test)]
mod tests {
use super::paginate_items;
use super::{paginate_items, paginate_slice};
#[test]
fn paginates_basic_case() {
@@ -128,6 +169,16 @@ mod tests {
assert_eq!(meta.end_index, 0);
}
#[test]
fn paginate_slice_clones_only_page_items() {
let items: Vec<_> = (1..=25).collect();
let (page, meta) = paginate_slice(&items, Some(2), 10);
assert_eq!(page, vec![11, 12, 13, 14, 15, 16, 17, 18, 19, 20]);
assert_eq!(items.len(), 25);
assert_eq!(meta.current_page, 2);
}
#[test]
fn clamps_page_to_bounds() {
let items: Vec<_> = (1..=5).collect();