standing_relations/convenience/
join.rs

1use std::hash::Hash;
2
3use crate::{pair::Pair, Op, Relation};
4
5impl<K: Clone + Eq + Hash, V: Clone + Eq + Hash, C: Op<D = (K, V)>> Relation<C> {
6    pub fn semijoin(self, other: Relation<impl Op<D = K>>) -> Relation<impl Op<D = (K, V)>> {
7        self.join(other.map_h(|x| (x, ())))
8            .map_h(|(k, v, ())| (k, v))
9            .type_named("semijoin")
10    }
11    pub fn join_values<V2: Clone + Eq + Hash>(
12        self,
13        other: Relation<impl Op<D = (K, V2)>>,
14    ) -> Relation<impl Op<D = (V, V2)>> {
15        self.join(other).map_h(|(_, v1, v2)| (v1, v2))
16    }
17    // TODO Make this native rather than a convenience function
18    pub fn left_join<V2: Clone + Eq + Hash>(
19        self,
20        other: Relation<impl Op<D = (K, V2)>>,
21    ) -> Relation<impl Op<D = (K, V, Option<V2>)>> {
22        let self_saved = self.save();
23        let other_saved = other.save();
24        self_saved
25            .get()
26            .join(other_saved.get())
27            .map_h(|(k, l, r)| (k, l, Some(r)))
28            .concat(
29                self_saved
30                    .get()
31                    .antijoin(other_saved.get().map(Pair::fst))
32                    .map(|(k, l)| (k, l, None)),
33            )
34    }
35}
36
37impl<C: Op> Relation<C>
38where
39    C::D: Clone + Eq + Hash,
40{
41    pub fn intersection(self, other: Relation<impl Op<D = C::D>>) -> Relation<impl Op<D = C::D>> {
42        self.map_h(|x| (x, ()))
43            .semijoin(other)
44            .map_h(|(x, ())| x)
45            .type_named("intersection")
46    }
47    pub fn set_minus(self, other: Relation<impl Op<D = C::D>>) -> Relation<impl Op<D = C::D>> {
48        self.map_h(|x| (x, ()))
49            .antijoin(other)
50            .map_h(|(x, ())| x)
51            .type_named("set_minus")
52    }
53}