Skip to main content

solverforge_scoring/stream/
existence_target.rs

1/* Existence targets: turn a left filtered stream plus a right stream (direct
2or flattened) into an operator-backed existence constraint.
3
4The relationship is a named equality plan over the two operators' views.
5There is no separate key/row engine: the left input is a `CollectionNode` over
6the left stream (whose membership is the stream's own filter), and the right
7input is a `CollectionNode` over the right stream, or a `FlattenNode` over it
8when the right stream is flattened.
9*/
10
11use std::hash::Hash;
12use std::marker::PhantomData;
13
14use solverforge_core::score::Score;
15
16use super::collection_extract::{CollectionExtract, FlattenExtract};
17use super::existence_stream::{ExistenceMode, ExistsConstraintStream};
18use super::filter::UniFilter;
19use super::joiner::EqualJoiner;
20use super::relational::operator::{CollectionNode, FlattenNode, ParentFlatten};
21use super::relational::view_plan::{ChildKey, EntityKey, ViewEqualPlan};
22use super::uni_stream::UniConstraintStream;
23
24pub struct FlattenedCollectionTarget<S, P, B, EP, FP, Flatten, Sc>
25where
26    Sc: Score,
27{
28    pub(crate) right_stream: UniConstraintStream<S, P, EP, FP, Sc>,
29    pub(crate) flatten: Flatten,
30    pub(crate) _phantom: PhantomData<(fn() -> B, fn() -> Sc)>,
31}
32
33pub trait ExistenceTarget<S, A, EA, FA, Sc: Score>
34where
35    EA: CollectionExtract<S, Item = A>,
36    FA: UniFilter<S, A>,
37{
38    type Output;
39
40    fn apply(self, mode: ExistenceMode, extractor_a: EA, filter_a: FA) -> Self::Output;
41}
42
43/* Direct existence: `.if_exists((other_stream, equal_bi(ka, kb)))`.
44
45Both inputs are collections over their streams, so each input's membership is
46that stream's accumulated filter, and the relationship keys off the leaf views.
47*/
48impl<S, A, B, EA, FA, EB, FB, K, KA, KB, Mode, Sc> ExistenceTarget<S, A, EA, FA, Sc>
49    for (
50        UniConstraintStream<S, B, EB, FB, Sc>,
51        EqualJoiner<KA, KB, K, Mode>,
52    )
53where
54    S: Send + Sync + 'static,
55    A: Clone + Send + Sync + 'static,
56    B: Clone + Send + Sync + 'static,
57    EA: CollectionExtract<S, Item = A> + Send + Sync + 'static,
58    FA: UniFilter<S, A> + Send + Sync + 'static,
59    EB: CollectionExtract<S, Item = B> + Send + Sync + 'static,
60    FB: UniFilter<S, B> + Send + Sync + 'static,
61    K: Eq + Hash + Clone + Send + Sync + 'static,
62    KA: Fn(&A) -> K + Send + Sync + 'static,
63    KB: Fn(&B) -> K + Send + Sync + 'static,
64    Sc: Score + 'static,
65{
66    type Output = ExistsConstraintStream<
67        S,
68        A,
69        CollectionNode<S, UniConstraintStream<S, A, EA, FA, Sc>>,
70        CollectionNode<S, UniConstraintStream<S, B, EB, FB, Sc>>,
71        ViewEqualPlan<K, EntityKey<KA>, EntityKey<KB>>,
72        Sc,
73    >;
74
75    fn apply(self, mode: ExistenceMode, extractor_a: EA, filter_a: FA) -> Self::Output {
76        let (right_stream, joiner) = self;
77        let (key_a, key_b) = joiner.into_keys();
78        let left_stream = UniConstraintStream::from_parts(extractor_a, filter_a);
79        let left = CollectionNode::new(left_stream, 0);
80        let right = CollectionNode::new(right_stream, 1);
81        let condition = ViewEqualPlan::new(EntityKey::new(key_a), EntityKey::new(key_b));
82        ExistsConstraintStream::new(mode, left, right, condition)
83    }
84}
85
86/* Flattened existence: `.if_not_exists((stream.flattened(|p| ..), equal_bi(..)))`.
87
88The right input is a `FlattenNode` over the parent collection; the
89relationship's right key unwraps the flattened child row.
90*/
91impl<S, A, P, B, EA, FA, EP, FP, K, KA, KB, Mode, Flatten, Sc> ExistenceTarget<S, A, EA, FA, Sc>
92    for (
93        FlattenedCollectionTarget<S, P, B, EP, FP, Flatten, Sc>,
94        EqualJoiner<KA, KB, K, Mode>,
95    )
96where
97    S: Send + Sync + 'static,
98    A: Clone + Send + Sync + 'static,
99    P: Clone + Send + Sync + 'static,
100    B: Clone + Send + Sync + 'static,
101    EA: CollectionExtract<S, Item = A> + Send + Sync + 'static,
102    FA: UniFilter<S, A> + Send + Sync + 'static,
103    EP: CollectionExtract<S, Item = P> + Send + Sync + 'static,
104    FP: UniFilter<S, P> + Send + Sync + 'static,
105    K: Eq + Hash + Clone + Send + Sync + 'static,
106    KA: Fn(&A) -> K + Send + Sync + 'static,
107    KB: Fn(&B) -> K + Send + Sync + 'static,
108    Flatten: FlattenExtract<P, Item = B> + Send + Sync + 'static,
109    Sc: Score + 'static,
110{
111    type Output = ExistsConstraintStream<
112        S,
113        A,
114        CollectionNode<S, UniConstraintStream<S, A, EA, FA, Sc>>,
115        FlattenNode<
116            CollectionNode<S, UniConstraintStream<S, P, EP, FP, Sc>>,
117            ParentFlatten<Flatten>,
118        >,
119        ViewEqualPlan<K, EntityKey<KA>, ChildKey<KB>>,
120        Sc,
121    >;
122
123    fn apply(self, mode: ExistenceMode, extractor_a: EA, filter_a: FA) -> Self::Output {
124        let (target, joiner) = self;
125        let FlattenedCollectionTarget {
126            right_stream,
127            flatten,
128            ..
129        } = target;
130        let (key_a, key_b) = joiner.into_keys();
131        let left_stream = UniConstraintStream::from_parts(extractor_a, filter_a);
132        let left = CollectionNode::new(left_stream, 0);
133        let parents = CollectionNode::new(right_stream, 1);
134        let right = FlattenNode::new(parents, ParentFlatten::new(flatten));
135        let condition = ViewEqualPlan::new(EntityKey::new(key_a), ChildKey::new(key_b));
136        ExistsConstraintStream::new(mode, left, right, condition)
137    }
138}