feat: implement batch queries for improved list performance
Co-authored-by: aider (openai/andrew.openrouter.qwen.qwen3-coder) <aider@aider.chat>
This commit is contained in:
63
src/db.rs
63
src/db.rs
@@ -443,3 +443,66 @@ pub fn get_item_meta_value(conn: &Connection, item: &Item, name: String) -> Resu
|
|||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_tags_for_items(conn: &Connection, item_ids: &[i64]) -> Result<std::collections::HashMap<i64, Vec<Tag>>> {
|
||||||
|
debug!("DB: Getting tags for items: {:?}", item_ids);
|
||||||
|
|
||||||
|
if item_ids.is_empty() {
|
||||||
|
return Ok(std::collections::HashMap::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create placeholders for the IN clause
|
||||||
|
let placeholders: Vec<String> = item_ids.iter().map(|_| "?".to_string()).collect();
|
||||||
|
let placeholders_str = placeholders.join(",");
|
||||||
|
|
||||||
|
let sql = format!("SELECT id, name FROM tags WHERE id IN ({}) ORDER BY id ASC, name ASC", placeholders_str);
|
||||||
|
|
||||||
|
let mut statement = conn
|
||||||
|
.prepare_cached(&sql)
|
||||||
|
.context("Problem preparing SQL statement")?;
|
||||||
|
|
||||||
|
let mut rows = statement.query(rusqlite::params_from_iter(item_ids))?;
|
||||||
|
|
||||||
|
let mut tags_map: std::collections::HashMap<i64, Vec<Tag>> = std::collections::HashMap::new();
|
||||||
|
|
||||||
|
while let Some(row) = rows.next()? {
|
||||||
|
let id: i64 = row.get(0)?;
|
||||||
|
let name: String = row.get(1)?;
|
||||||
|
|
||||||
|
tags_map.entry(id).or_insert_with(Vec::new).push(Tag { id, name });
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(tags_map)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_meta_for_items(conn: &Connection, item_ids: &[i64]) -> Result<std::collections::HashMap<i64, std::collections::HashMap<String, String>>> {
|
||||||
|
debug!("DB: Getting meta for items: {:?}", item_ids);
|
||||||
|
|
||||||
|
if item_ids.is_empty() {
|
||||||
|
return Ok(std::collections::HashMap::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create placeholders for the IN clause
|
||||||
|
let placeholders: Vec<String> = item_ids.iter().map(|_| "?".to_string()).collect();
|
||||||
|
let placeholders_str = placeholders.join(",");
|
||||||
|
|
||||||
|
let sql = format!("SELECT id, name, value FROM metas WHERE id IN ({}) ORDER BY id ASC, name ASC", placeholders_str);
|
||||||
|
|
||||||
|
let mut statement = conn
|
||||||
|
.prepare_cached(&sql)
|
||||||
|
.context("Problem preparing SQL statement")?;
|
||||||
|
|
||||||
|
let mut rows = statement.query(rusqlite::params_from_iter(item_ids))?;
|
||||||
|
|
||||||
|
let mut meta_map: std::collections::HashMap<i64, std::collections::HashMap<String, String>> = std::collections::HashMap::new();
|
||||||
|
|
||||||
|
while let Some(row) = rows.next()? {
|
||||||
|
let id: i64 = row.get(0)?;
|
||||||
|
let name: String = row.get(1)?;
|
||||||
|
let value: String = row.get(2)?;
|
||||||
|
|
||||||
|
meta_map.entry(id).or_insert_with(std::collections::HashMap::new).insert(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(meta_map)
|
||||||
|
}
|
||||||
|
|||||||
@@ -38,31 +38,23 @@ pub fn mode_list(
|
|||||||
|
|
||||||
debug!("MAIN: Items: {:?}", items);
|
debug!("MAIN: Items: {:?}", items);
|
||||||
|
|
||||||
|
// Collect all item IDs for batch queries
|
||||||
|
let item_ids: Vec<i64> = items.iter().map(|item| item.id.unwrap()).collect();
|
||||||
|
|
||||||
|
// Fetch all tags for all items in a single query
|
||||||
|
let all_tags = crate::db::get_tags_for_items(conn, &item_ids)?;
|
||||||
let mut tags_by_item: std::collections::HashMap<i64, Vec<String>> =
|
let mut tags_by_item: std::collections::HashMap<i64, Vec<String>> =
|
||||||
std::collections::HashMap::new();
|
std::collections::HashMap::new();
|
||||||
let mut meta_by_item: std::collections::HashMap<
|
|
||||||
i64,
|
|
||||||
std::collections::HashMap<String, String>,
|
|
||||||
> = std::collections::HashMap::new();
|
|
||||||
|
|
||||||
for item in items.iter() {
|
// Convert Tag structs to just names
|
||||||
let item_id = item.id.unwrap();
|
for (item_id, tags) in all_tags {
|
||||||
|
let tag_names: Vec<String> = tags.into_iter().map(|tag| tag.name).collect();
|
||||||
let item_tags: Vec<String> = crate::db::get_item_tags(conn, item)?
|
tags_by_item.insert(item_id, tag_names);
|
||||||
.into_iter()
|
|
||||||
.map(|x| x.name)
|
|
||||||
.collect();
|
|
||||||
tags_by_item.insert(item_id, item_tags);
|
|
||||||
|
|
||||||
let mut item_meta: std::collections::HashMap<String, String> =
|
|
||||||
std::collections::HashMap::new();
|
|
||||||
|
|
||||||
for meta in crate::db::get_item_meta(conn, item)? {
|
|
||||||
item_meta.insert(meta.name.clone(), meta.value);
|
|
||||||
}
|
|
||||||
meta_by_item.insert(item_id, item_meta);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetch all metadata for all items in a single query
|
||||||
|
let meta_by_item = crate::db::get_meta_for_items(conn, &item_ids)?;
|
||||||
|
|
||||||
let mut table = Table::new();
|
let mut table = Table::new();
|
||||||
table.set_format(*prettytable::format::consts::FORMAT_CLEAN);
|
table.set_format(*prettytable::format::consts::FORMAT_CLEAN);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user