You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何对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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 22:40:44