Skip to content

Commit 29aa585

Browse files
Allow a simple predicate to filter the output of joins
1 parent f7ce178 commit 29aa585

File tree

2 files changed

+78
-7
lines changed

2 files changed

+78
-7
lines changed

src/join.rs

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,50 @@ pub(crate) fn join_into<'me, Key: Ord, Val1: Ord, Val2: Ord, Result: Ord>(
1616
mut logic: impl FnMut(&Key, &Val1, &Val2) -> Result,
1717
) {
1818
let mut results = Vec::new();
19+
let push_result = |k: &Key, v1: &Val1, v2: &Val2| results.push(logic(k, v1, v2));
1920

21+
join_delta(input1, input2, push_result);
22+
23+
output.insert(Relation::from_vec(results));
24+
}
25+
26+
pub(crate) fn join_and_filter_into<'me, Key: Ord, Val1: Ord, Val2: Ord, Result: Ord>(
27+
input1: &Variable<(Key, Val1)>,
28+
input2: impl JoinInput<'me, (Key, Val2)>,
29+
output: &Variable<Result>,
30+
mut logic: impl FnMut(&Key, &Val1, &Val2) -> Option<Result>,
31+
) {
32+
let mut results = Vec::new();
33+
let push_result = |k: &Key, v1: &Val1, v2: &Val2| {
34+
if let Some(result) = logic(k, v1, v2) {
35+
results.push(result);
36+
}
37+
};
38+
39+
join_delta(input1, input2, push_result);
40+
41+
output.insert(Relation::from_vec(results));
42+
}
43+
44+
/// Joins the `recent` tuples of each input with the `stable` tuples of the other, then the
45+
/// `recent` tuples of *both* inputs.
46+
fn join_delta<'me, Key: Ord, Val1: Ord, Val2: Ord>(
47+
input1: &Variable<(Key, Val1)>,
48+
input2: impl JoinInput<'me, (Key, Val2)>,
49+
mut result: impl FnMut(&Key, &Val1, &Val2),
50+
) {
2051
let recent1 = input1.recent();
2152
let recent2 = input2.recent();
2253

23-
let mut closure = |k: &Key, v1: &Val1, v2: &Val2| results.push(logic(k, v1, v2));
24-
2554
for batch2 in input2.stable().iter() {
26-
join_helper(&recent1, &batch2, &mut closure);
55+
join_helper(&recent1, &batch2, &mut result);
2756
}
2857

2958
for batch1 in input1.stable().iter() {
30-
join_helper(&batch1, &recent2, &mut closure);
59+
join_helper(&batch1, &recent2, &mut result);
3160
}
3261

33-
join_helper(&recent1, &recent2, &mut closure);
34-
35-
output.insert(Relation::from_vec(results));
62+
join_helper(&recent1, &recent2, &mut result);
3663
}
3764

3865
/// Join, but for two relations.

src/variable.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,50 @@ impl<Tuple: Ord> Variable<Tuple> {
9898
join::join_into(input1, input2, self, logic)
9999
}
100100

101+
/// Same as [`Variable::from_join`], but lets you ignore some of the resulting tuples.
102+
///
103+
/// # Examples
104+
///
105+
/// This is the same example from `Variable::from_join`, but it filters any tuples where the
106+
/// absolute difference is greater than 3. As a result, it generates all pairs (x, y) for x and
107+
/// y in 0 .. 11 such that |x - y| <= 3.
108+
///
109+
/// ```
110+
/// use datafrog::{Iteration, Relation};
111+
///
112+
/// let mut iteration = Iteration::new();
113+
/// let variable = iteration.variable::<(isize, isize)>("source");
114+
/// variable.extend((0 .. 10).map(|x| (x, x + 1)));
115+
/// variable.extend((0 .. 10).map(|x| (x + 1, x)));
116+
///
117+
/// while iteration.changed() {
118+
/// variable.from_join_filtered(&variable, &variable, |&key, &val1, &val2| {
119+
/// ((val1 - val2).abs() <= 3).then(|| (val1, val2))
120+
/// });
121+
/// }
122+
///
123+
/// let result = variable.complete();
124+
///
125+
/// let mut expected_cnt = 0;
126+
/// for i in 0i32..11 {
127+
/// for j in 0i32..11 {
128+
/// if (i - j).abs() <= 3 {
129+
/// expected_cnt += 1;
130+
/// }
131+
/// }
132+
/// }
133+
///
134+
/// assert_eq!(result.len(), expected_cnt);
135+
/// ```
136+
pub fn from_join_filtered<'me, K: Ord, V1: Ord, V2: Ord>(
137+
&self,
138+
input1: &'me Variable<(K, V1)>,
139+
input2: impl JoinInput<'me, (K, V2)>,
140+
logic: impl FnMut(&K, &V1, &V2) -> Option<Tuple>,
141+
) {
142+
join::join_and_filter_into(input1, input2, self, logic)
143+
}
144+
101145
/// Adds tuples from `input1` whose key is not present in `input2`.
102146
///
103147
/// Note that `input1` must be a variable: if you have a relation

0 commit comments

Comments
 (0)