#join #iterable #traits #values #joining #left #records

joinable

实现类似于 SQL 联接的值迭代器的特质

3 个不稳定版本

0.2.0 2022 年 12 月 8 日
0.2.0-rc.12022 年 11 月 28 日
0.1.0 2022 年 1 月 18 日

#1192 in 数据库接口

MIT/Apache

33KB
588

joinable

joinable 定义了用于连接值迭代器的特质。就像您可以在 SQL 中连接两个数据库表以生成匹配的记录一样,joinable 提供了在 Rust 代码中实现相同功能的简单功能。

Joinable 特质允许您连接左右两侧,对于内部连接产生 (&L, &R),对于外部连接产生 (&L, Option<&R>)。由于相同的左值可能会因为多个右匹配而多次产生,Joinable 使用来自 LHS 的借用值

use joinable::Joinable;
let customers = get_customers();
let orders = get_orders();
let it = customers
    .iter()
    .outer_join(&orders[..], |c, o| c.id.cmp(&o.customer_id));

JoinableGrouped 特质通过将右侧值收集到 Vec 中来连接左右两侧。这个 '分组' 版本最多为每个左值产生一次,因此它可以拥有左边的迭代器

use joinable::JoinableGrouped;
let customers = get_customers();
let orders = get_orders();
let it = customers
    .into_iter()
    .outer_join_grouped(&orders[..], |c, o| c.id.cmp(&o.customer_id));
for (cust, ords) in it {
    if ords.is_empty() {
        println!("Customer '{}' has no orders", cust.name);
    } else {
        let total_spend = ords.iter().map(|o| o.amount_usd).sum::<f32>();
        println!("Customer '{}' has spent ${:0.2}", cust.name, total_spend);
    }
}

JoinableGrouped 还公开了 SEMIJOIN 和 ANTISEMIJOIN 功能,分别只产生在右侧找到或不找到匹配的左侧的行

use joinable::JoinableGrouped;
let customers = get_customers();
let orders = get_orders();
let customers_with_orders : Vec<&Customer> = customers
    .iter()
    .semi_join(&orders[..], |c, o| c.id.cmp(&o.customer_id))
    .collect();
let customers_without_orders : Vec<Customer> = customers
    .into_iter()
    .anti_join(&orders[..], |c, o| c.id.cmp(&o.customer_id))
    .collect();

搜索谓词

对于所有连接,搜索谓词的类型为 Fn(&L, &R) -> std::cmp::Ordering;也就是说,给定一些来自左右两侧的值,您的谓词必须确定这两个值如何比较。如果用于匹配的类型没有实现 PartialOrd,您可以简单地检查相等并返回 Ordering::Equal/某个非 Equal 值。

使用 RHS::Sorted

枚举 RHS 包装了连接的右侧。默认情况下,RHS 假设您的数据是无序的

let customers_with_orders : Vec<&Customer> = customers
    .iter()
    .semi_join(&orders[..], |c, o| c.id.cmp(&o.customer_id))
    //          ^^^^^^ orders is implicitly converted Into<RHS>
    .collect();

如果您的用例允许并且这样做有意义,您可以按照搜索谓词对右侧进行排序,使得搜索可以在 O(ln n) 的时间复杂度内进行二分搜索,而不是线性的 O(n)

let customers_with_orders : Vec<&Customer> = customers
    .iter()
    .semi_join(RHS::Sorted(&orders[..]), |c, o| c.id.cmp(&o.customer_id))
    //         ^^^^^^^^^^^^^^^^^^^^^^^^ signal that orders is sorted by customer_id
    .collect();

joinable 假设您的有序数据是按 升序 排序的。如果您有降序排序的数据,则可以反转排序顺序。

无运行时依赖