如何对Diesel查询获取的Item向量按用户分组?
问题:将Vector中的Item按用户分组生成UserWithItems列表
已通过Diesel查询获取到Vec<Item>,现在需要将其转换为Vec<UserWithItems>(每个用户对应其所有SmallerItem的列表),但调用group_by时触发 trait bounds 错误。
现有代码结构
用户定义的核心结构体:
#[derive(Debug, Clone, Queryable, Serialize, Deserialize)] pub(crate) struct User { id: i64, firstname: String, lastname: String } #[derive(Debug, Clone, Queryable, Serialize, Deserialize)] pub(crate) struct Item { id: i64, name: String, owner: User, owner_id: i64 } #[derive(Debug, Clone, Queryable, Serialize, Deserialize)] pub(crate) struct UserWithItems { id: i64, firstname: String, lastname: String, items: Vec<SmallerItem> } #[derive(Debug, Clone, Serialize, Deserialize)] pub(crate) struct SmallerItem { id: i64, name: String, owner_id: i64 }
获取Vec<Item>的查询代码(可正常运行):
let mut _items: Vec<Item> = items::dsl::items .inner_join(users::table.on(users::id.eq(items::owner_id))) .select(( items::id, items::name, ( users::id, users::firstname, users::lastname, ), items::owner_id, )) .order_by(items::owner_id) .load::<Item>(&mut conn)?;
错误信息
| 126 | .group_by(|x| (x.user_id, x.firstname, x.lastname) | ^^^^^^^^ method cannot be called on std::slice::Iter<'_, Item> due to | unsatisfied trait bounds | ::: /----/.rustup/toolchains/stable-aarch64-apple- | darwin/lib/rustlib/src/rust/library/core/src/slice/iter.rs:66:1 | 66 | pub struct Iter<'a, T: 'a> { | -------------------------- | | | doesn't satisfy `std::slice::Iter<'_, Item>: Table` | doesn't satisfy `std::slice::Iter<'_, Item>: diesel::QueryDsl` | = note: the following trait bounds were not satisfied: `std::slice::Iter<'_, Item>: Table` which is required by `std::slice::Iter<'_, Item>: diesel::QueryDsl` `&std::slice::Iter<'_, Item>: Table` which is required by `&std::slice::Iter<'_, Item>: diesel::QueryDsl` `&mut std::slice::Iter<'_, Item>: Table` which is required by `&mut std::slice::Iter<'_, Item>: diesel::QueryDsl`
错误原因
你混淆了Diesel数据库查询方法和Rust标准库迭代器方法:
- 代码中调用的
group_by是Diesel为数据库查询构建器提供的方法,仅适用于Diesel的查询类型(需要满足Table等trait约束)。 - 而你是在内存中的
Vec<Item>迭代器上调用该方法,自然不满足Diesel的trait要求。
解决方案
方案1:纯标准库手动分组(无额外依赖)
因为查询已按owner_id排序,可手动遍历完成分组:
let mut user_with_items_list = Vec::new(); if let Some(first_item) = _items.first() { let mut current_user = first_item.owner.clone(); let mut current_items = vec![SmallerItem { id: first_item.id, name: first_item.name.clone(), owner_id: first_item.owner_id, }]; for item in _items.iter().skip(1) { if item.owner.id == current_user.id { current_items.push(SmallerItem { id: item.id, name: item.name.clone(), owner_id: item.owner_id, }); } else { user_with_items_list.push(UserWithItems { id: current_user.id, firstname: current_user.firstname, lastname: current_user.lastname, items: current_items, }); current_user = item.owner.clone(); current_items = vec![SmallerItem { id: item.id, name: item.name.clone(), owner_id: item.owner_id, }]; } } // 加入最后一组用户数据 user_with_items_list.push(UserWithItems { id: current_user.id, firstname: current_user.firstname, lastname: current_user.lastname, items: current_items, }); }
方案2:用itertools库简化分组(推荐)
添加itertools依赖到Cargo.toml:
itertools = "0.11.0"
利用itertools的group_by方法快速处理:
use itertools::Itertools; let user_with_items_list: Vec<UserWithItems> = _items .into_iter() .group_by(|item| item.owner.clone()) // 按User实例分组 .into_iter() .map(|(user, items)| UserWithItems { id: user.id, firstname: user.firstname, lastname: user.lastname, items: items .map(|item| SmallerItem { id: item.id, name: item.name, owner_id: item.owner_id, }) .collect(), }) .collect();
优化:减少User克隆
如果想避免重复克隆User,可以按owner_id分组,从同组第一个元素提取用户信息:
use itertools::Itertools; let user_with_items_list: Vec<UserWithItems> = _items .into_iter() .group_by(|item| item.owner_id) .into_iter() .map(|(owner_id, items)| { let mut items_iter = items.peekable(); let first_item = items_iter.peek().unwrap(); let user = &first_item.owner; UserWithItems { id: user.id, firstname: user.firstname.clone(), lastname: user.lastname.clone(), items: std::iter::once(first_item.clone()) .chain(items_iter) .map(|item| SmallerItem { id: item.id, name: item.name, owner_id: item.owner_id, }) .collect(), } }) .collect();
内容的提问来源于stack exchange,提问作者SaintGuacamole
相关产品推荐
相关产品推荐

