Skip to main content

wundergraph/query_builder/selection/filter/
filter_helper.rs

1use super::build_filter::BuildFilter;
2use super::common_filter::FilterOption;
3use super::filter_value::FilterValue;
4use super::inner_filter::InnerFilter;
5use super::nullable_filter::IsNull;
6use super::reference_filter::ReferenceFilter;
7use super::Filter;
8use crate::diesel_ext::BoxableFilter;
9use crate::helper::tuple::ConcatTuples;
10use crate::juniper_ext::{FromLookAheadValue, NameBuilder, Nameable};
11use crate::query_builder::selection::fields::WundergraphBelongsTo;
12use crate::query_builder::selection::fields::{FieldListExtractor, NonTableFieldExtractor};
13use crate::query_builder::selection::offset::ApplyOffset;
14use crate::query_builder::selection::LoadingHandler;
15use crate::query_builder::types::{HasMany, HasOne};
16use crate::scalar::WundergraphScalarValue;
17use diesel::associations::HasTable;
18use diesel::backend::Backend;
19use diesel::expression::{NonAggregate, SelectableExpression};
20use diesel::query_builder::QueryFragment;
21use diesel::sql_types::Bool;
22use diesel::Expression;
23use diesel::QuerySource;
24use diesel::Table;
25use indexmap::IndexMap;
26use juniper::meta::Argument;
27use juniper::{FromInputValue, GraphQLType, InputValue, LookAheadValue, Registry, ToInputValue};
28use std::fmt::{self, Debug};
29use std::marker::PhantomData;
30
31pub use wundergraph_derive::BuildFilterHelper;
32
33#[doc(hidden)]
34pub struct FilterWrapper<L, DB, Ctx>
35where
36    FilterConverter<L, DB, Ctx>: CreateFilter,
37{
38    filter: <FilterConverter<L, DB, Ctx> as CreateFilter>::Filter,
39}
40
41impl<L, DB, Ctx> Clone for FilterWrapper<L, DB, Ctx>
42where
43    FilterConverter<L, DB, Ctx>: CreateFilter,
44    <FilterConverter<L, DB, Ctx> as CreateFilter>::Filter: Clone,
45{
46    fn clone(&self) -> Self {
47        Self {
48            filter: self.filter.clone(),
49        }
50    }
51}
52
53impl<L, DB, Ctx> Debug for FilterWrapper<L, DB, Ctx>
54where
55    FilterConverter<L, DB, Ctx>: CreateFilter,
56    <FilterConverter<L, DB, Ctx> as CreateFilter>::Filter: Debug,
57{
58    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59        f.debug_struct("FilterWrapper")
60            .field("inner", &self.filter)
61            .finish()
62    }
63}
64
65#[doc(hidden)]
66#[derive(Debug)]
67pub struct FilterConverter<L, DB, Ctx>(PhantomData<(L, DB, Ctx)>);
68
69#[doc(hidden)]
70#[derive(Debug)]
71pub struct ColumnFilterConverter<N, C, DB, Ctx>(PhantomData<(N, C, DB, Ctx)>);
72
73#[doc(hidden)]
74#[derive(Debug)]
75pub struct NonColumnFilterConveter<N, L, DB, Ctx>(PhantomData<(N, L, DB, Ctx)>);
76
77/// Helper trait to build a compound filter from filter nodes
78/// that exist for each field
79pub trait CreateFilter {
80    /// Final filter type
81    type Filter;
82}
83
84impl<L, DB, Ctx> CreateFilter for FilterConverter<L, DB, Ctx>
85where
86    DB: Backend + ApplyOffset + 'static,
87    DB::QueryBuilder: Default,
88    L::Table: 'static,
89    <L::Table as QuerySource>::FromClause: QueryFragment<DB>,
90    L: LoadingHandler<DB, Ctx>,
91    L::FieldList: FieldListExtractor + NonTableFieldExtractor,
92    ColumnFilterConverter<<L::FieldList as FieldListExtractor>::Out, L::Columns, DB, Ctx>: CreateFilter,
93    NonColumnFilterConveter<<L::FieldList as NonTableFieldExtractor>::Out, L, DB, Ctx>: CreateFilter,
94    <ColumnFilterConverter<<L::FieldList as FieldListExtractor>::Out, L::Columns, DB, Ctx> as CreateFilter>::Filter: ConcatTuples<<
95    NonColumnFilterConveter<<L::FieldList as NonTableFieldExtractor>::Out, L, DB, Ctx> as CreateFilter>::Filter>
96{
97    type Filter = <<ColumnFilterConverter<<L::FieldList as FieldListExtractor>::Out, L::Columns, DB, Ctx> as CreateFilter>::Filter as ConcatTuples<<
98    NonColumnFilterConveter<<L::FieldList as NonTableFieldExtractor>::Out, L, DB, Ctx> as CreateFilter>::Filter>>::Out;
99}
100
101impl<DB, Ctx> CreateFilter for ColumnFilterConverter<(), (), DB, Ctx> {
102    type Filter = ();
103}
104
105impl<DB, L, Ctx> CreateFilter for NonColumnFilterConveter<(), L, DB, Ctx> {
106    type Filter = ();
107}
108
109/// Marker trait indicating that some value has a filter that is
110/// applied to a specific  database column
111pub trait AsColumnFilter<C, DB, Ctx> {
112    /// Type of the filter
113    type Filter;
114}
115
116/// Marker trait indicating that some value has a filter
117/// that is not attached to a column
118pub trait AsNonColumnFilter<L, DB, Ctx> {
119    /// Type of the filter
120    type Filter;
121}
122
123impl<L, O, DB, Ctx, FK> AsNonColumnFilter<L, DB, Ctx> for HasMany<O, FK>
124where
125    L: HasTable,
126    FK: Default + NonAggregate + QueryFragment<DB> + SelectableExpression<O::Table>,
127    O: WundergraphBelongsTo<L::Table, DB, Ctx, FK>,
128    O::Table: 'static,
129    DB: Backend + ApplyOffset + 'static,
130    <O::Table as QuerySource>::FromClause: QueryFragment<DB>,
131    DB::QueryBuilder: Default,
132{
133    type Filter =
134        ReferenceFilter<<L::Table as Table>::PrimaryKey, Filter<O::Filter, O::Table>, FK, ()>;
135}
136
137impl<C, DB, Ctx> AsColumnFilter<C, DB, Ctx> for i16 {
138    type Filter = FilterOption<Self, C>;
139}
140
141impl<C, DB, Ctx> AsColumnFilter<C, DB, Ctx> for i32 {
142    type Filter = FilterOption<Self, C>;
143}
144
145impl<C, DB, Ctx> AsColumnFilter<C, DB, Ctx> for i64 {
146    type Filter = FilterOption<Self, C>;
147}
148
149impl<C, DB, Ctx> AsColumnFilter<C, DB, Ctx> for bool {
150    type Filter = FilterOption<Self, C>;
151}
152
153impl<C, DB, Ctx> AsColumnFilter<C, DB, Ctx> for f32 {
154    type Filter = FilterOption<Self, C>;
155}
156
157impl<C, DB, Ctx> AsColumnFilter<C, DB, Ctx> for f64 {
158    type Filter = FilterOption<Self, C>;
159}
160
161impl<C, DB, Ctx> AsColumnFilter<C, DB, Ctx> for String {
162    type Filter = FilterOption<Self, C>;
163}
164
165impl<C, DB, T, Ctx> AsColumnFilter<C, DB, Ctx> for Vec<T>
166where
167    T: FromLookAheadValue
168        + FromInputValue<WundergraphScalarValue>
169        + ToInputValue<WundergraphScalarValue>
170        + FilterValue<C>
171        + Clone,
172{
173    type Filter = FilterOption<Self, C>;
174}
175
176impl<C, DB, T, Ctx> AsColumnFilter<C, DB, Ctx> for Option<T>
177where
178    T: FilterValue<C>
179        + Clone
180        + FromInputValue<WundergraphScalarValue>
181        + FromLookAheadValue
182        + ToInputValue<WundergraphScalarValue>,
183    T: AsColumnFilter<C, DB, Ctx, Filter = FilterOption<T, C>>,
184{
185    type Filter = FilterOption<Self, C>;
186}
187
188impl<C, K, I, DB, Ctx> AsColumnFilter<C, DB, Ctx> for HasOne<K, I>
189where
190    DB: Backend + ApplyOffset + 'static,
191    I::Table: 'static,
192    I: LoadingHandler<DB, Ctx>,
193    <I::Table as QuerySource>::FromClause: QueryFragment<DB>,
194    DB::QueryBuilder: Default,
195{
196    type Filter =
197        ReferenceFilter<C, Filter<I::Filter, I::Table>, <I::Table as Table>::PrimaryKey, ()>;
198}
199
200// That's a false positve
201#[allow(clippy::use_self)]
202impl<C, K, I, DB, Ctx> AsColumnFilter<C, DB, Ctx> for Option<HasOne<K, I>>
203where
204    DB: Backend + ApplyOffset + 'static,
205    I::Table: 'static,
206    I: LoadingHandler<DB, Ctx>,
207    <I::Table as QuerySource>::FromClause: QueryFragment<DB>,
208    DB::QueryBuilder: Default,
209{
210    type Filter = ReferenceFilter<
211        C,
212        Filter<I::Filter, I::Table>,
213        <I::Table as Table>::PrimaryKey,
214        Option<IsNull<C>>,
215    >;
216}
217
218impl<L, DB, Ctx> Nameable for FilterWrapper<L, DB, Ctx>
219where
220    DB: Backend + ApplyOffset + 'static,
221    L::Table: 'static,
222    L: LoadingHandler<DB, Ctx>,
223    <L::Table as QuerySource>::FromClause: QueryFragment<DB>,
224    DB::QueryBuilder: Default,
225    FilterConverter<L, DB, Ctx>: CreateFilter,
226{
227    fn name() -> String {
228        format!("{}Filter", L::TYPE_NAME)
229    }
230}
231
232/// A helper trait describing a single node of a filter argument
233///
234/// # Type paramaters
235/// * `Self`:
236/// * `DB`: Backend from diesel, so one of `Pg` or `Sqlite`
237/// * `F`:
238/// * `Ctx`: The used wundergraph context type
239///
240/// # Deriving
241/// An implementation of this trait is automatically generated by
242/// [`#[derive(WundergraphEntity)]`](../derive.WundergraphEntity.html)
243/// or [`#[derive(BuildFilterHelper)]`](derive.BuildFilterHelper.html)
244///
245/// # Manual implementation
246/// Manually implementing this trait should only be the last resort if none of
247/// the provided derives generate compatible code. Below an expanded version of
248/// the generated implementation is shown.
249///
250/// ```
251/// # #[macro_use] extern crate diesel;
252/// # use wundergraph::helper::TupleIndex0;
253/// # use wundergraph::query_builder::selection::LoadingHandler;
254/// # use wundergraph::WundergraphEntity;
255/// # use wundergraph::query_builder::selection::filter::{
256/// #    CreateFilter, FilterBuildHelper, FilterConverter
257/// # };
258/// # use crate::wundergraph::query_builder::selection::filter::InnerFilter;
259///
260/// use wundergraph::query_builder::selection::filter::{BuildFilter, BuildFilterHelper};
261/// use wundergraph::query_builder::types::HasOne;
262/// use wundergraph::diesel_ext::BoxableFilter;
263/// use wundergraph::juniper_ext::NameBuilder;
264/// use wundergraph::query_builder::selection::fields::WundergraphBelongsTo;
265/// use wundergraph::WundergraphContext;
266/// use wundergraph::scalar::WundergraphScalarValue;
267/// use wundergraph::error::Result;
268/// use juniper::{LookAheadValue, Selection, Executor, InputValue, meta, Registry};
269/// # #[cfg(feature = "postgres")]
270/// use diesel::pg::Pg;
271/// use diesel::prelude::*;
272/// use diesel::sql_types::Bool;
273/// use std::collections::HashMap;
274/// use indexmap::IndexMap;
275///
276/// table! {
277///     heros {
278///         id -> Integer,
279///         name -> Text,
280///         species -> Integer,
281///     }
282/// }
283///
284/// #[derive(Identifiable)]
285/// struct Hero {
286///   id: i32,
287///   name: String,
288/// }
289///
290/// # #[cfg(feature = "postgres")]
291/// # impl<Ctx> LoadingHandler<Pg, Ctx> for Hero
292/// # where
293/// #    Ctx: WundergraphContext + 'static,
294/// #    <Ctx as WundergraphContext>::Connection: Connection<Backend = Pg>,
295/// # {
296/// #    type Columns = (heros::id, heros::name);
297/// #    type FieldList = (i32, String);
298/// #    type PrimaryKeyIndex = TupleIndex0;
299/// #    type Filter = ();
300/// #
301/// #    const FIELD_NAMES: &'static [&'static str] = &["id", "name"];
302/// #    const TYPE_NAME: &'static str = "Hero";
303/// # }
304///
305/// type Filter<DB, Ctx> = <FilterConverter<Hero, DB, Ctx> as CreateFilter>::Filter;
306///
307/// # #[cfg(feature = "postgres")]
308/// impl<Ctx> BuildFilterHelper<Pg, Filter<Pg, Ctx>, Ctx> for heros::table
309/// where
310///     Ctx: WundergraphContext + 'static,
311///     Ctx::Connection: Connection<Backend = Pg>,
312/// {
313///     type Ret = Box<dyn BoxableFilter<heros::table, Pg, SqlType = Bool>>;
314///     const FIELD_COUNT: usize = 2;
315///
316///      fn into_filter(
317///          f: Filter<Pg, Ctx>
318///      ) -> Option<Self::Ret> {
319///          BuildFilter::<Pg>::into_filter(f)
320///      }
321///
322///      fn from_inner_look_ahead(
323///          objs: &[(&str, LookAheadValue<WundergraphScalarValue>)]
324///      ) -> Filter<Pg, Ctx> {
325///          FilterBuildHelper::<Filter<Pg, Ctx>, Hero, Pg, Ctx>::from_inner_look_ahead(objs).0
326///      }
327///
328///      fn from_inner_input_value(
329///          obj: IndexMap<&str, &InputValue<WundergraphScalarValue>>
330///      ) -> Option<Filter<Pg, Ctx>> {
331///          Some(FilterBuildHelper::<Filter<Pg, Ctx>, Hero, Pg, Ctx>::from_inner_input_value(obj)?.0)
332///      }
333///
334///      fn to_inner_input_value(
335///          f: &Filter<Pg, Ctx>,
336///          v: &mut IndexMap<&str, InputValue<WundergraphScalarValue>>
337///      ) {}
338///
339///      fn register_fields<'r>(
340///          info: &NameBuilder<()>,
341///          registry: &mut Registry<'r, WundergraphScalarValue>,
342///      ) -> Vec<meta::Argument<'r, WundergraphScalarValue>> {
343///          FilterBuildHelper::<Filter<Pg, Ctx>, Hero, Pg, Ctx>::register_fields(
344///              &Default::default(),
345///              registry
346///          )
347///      }
348/// }
349///
350/// # fn main() {}
351/// ```
352pub trait BuildFilterHelper<DB, F, Ctx>
353where
354    DB: Backend,
355{
356    /// Diesel expression type returned by this filter node
357    type Ret: Expression<SqlType = Bool> + NonAggregate + QueryFragment<DB>;
358    /// Number of fields written by `to_inner_input_value`
359    const FIELD_COUNT: usize;
360
361    /// Convert the filter in a expression
362    ///
363    /// Returns the expression if the filter represents a query part, otherwise null
364    fn into_filter(f: F) -> Option<Self::Ret>;
365
366    /// Build the filter from a set of juniper look ahead values
367    fn from_inner_look_ahead(objs: &[(&str, LookAheadValue<'_, WundergraphScalarValue>)]) -> F;
368    /// Build the filter from a set of juniper input values
369    fn from_inner_input_value(
370        obj: IndexMap<&str, &InputValue<WundergraphScalarValue>>,
371    ) -> Option<F>;
372
373    /// Serialize the filter into a juniper input value
374    fn to_inner_input_value(f: &F, _v: &mut IndexMap<&str, InputValue<WundergraphScalarValue>>);
375
376    /// Register the filter as graphql argument
377    fn register_fields<'r>(
378        _info: &NameBuilder<()>,
379        registry: &mut Registry<'r, WundergraphScalarValue>,
380    ) -> Vec<Argument<'r, WundergraphScalarValue>>;
381}
382
383impl<L, DB, Ctx> BuildFilter<DB> for FilterWrapper<L, DB, Ctx>
384where
385    DB: Backend + ApplyOffset + 'static,
386    L::Table: 'static,
387    L: LoadingHandler<DB, Ctx>,
388    <L::Table as QuerySource>::FromClause: QueryFragment<DB>,
389    DB::QueryBuilder: Default,
390    FilterConverter<L, DB, Ctx>: CreateFilter,
391    L::Table: BuildFilterHelper<DB, <FilterConverter<L, DB, Ctx> as CreateFilter>::Filter, Ctx>,
392{
393    type Ret = <L::Table as BuildFilterHelper<
394        DB,
395        <FilterConverter<L, DB, Ctx> as CreateFilter>::Filter,
396        Ctx,
397    >>::Ret;
398
399    fn into_filter(self) -> Option<Self::Ret> {
400        <L::Table as BuildFilterHelper<DB, _, Ctx>>::into_filter(self.filter)
401    }
402}
403
404#[doc(hidden)]
405#[derive(Debug)]
406pub struct FilterBuildHelper<F, L, DB, Ctx>(pub F, PhantomData<(L, DB, Ctx)>);
407
408impl<F, L, DB, Ctx> Nameable for FilterBuildHelper<F, L, DB, Ctx>
409where
410    DB: Backend + ApplyOffset + 'static,
411    L::Table: 'static,
412    L: LoadingHandler<DB, Ctx>,
413    <L::Table as QuerySource>::FromClause: QueryFragment<DB>,
414    DB::QueryBuilder: Default,
415{
416    fn name() -> String {
417        format!("{}Filter", L::TYPE_NAME)
418    }
419}
420
421impl<L, DB, Ctx> InnerFilter for FilterWrapper<L, DB, Ctx>
422where
423    DB: Backend + ApplyOffset + 'static,
424    L::Table: 'static,
425    L: LoadingHandler<DB, Ctx>,
426    <L::Table as QuerySource>::FromClause: QueryFragment<DB>,
427    DB::QueryBuilder: Default,
428    FilterConverter<L, DB, Ctx>: CreateFilter,
429    L::Table: BuildFilterHelper<DB, <FilterConverter<L, DB, Ctx> as CreateFilter>::Filter, Ctx>,
430{
431    type Context = ();
432
433    const FIELD_COUNT: usize = L::Table::FIELD_COUNT;
434
435    fn from_inner_input_value(
436        obj: IndexMap<&str, &InputValue<WundergraphScalarValue>>,
437    ) -> Option<Self> {
438        Some(Self {
439            filter: L::Table::from_inner_input_value(obj)?,
440        })
441    }
442
443    fn from_inner_look_ahead(objs: &[(&str, LookAheadValue<'_, WundergraphScalarValue>)]) -> Self {
444        Self {
445            filter: L::Table::from_inner_look_ahead(objs),
446        }
447    }
448
449    fn to_inner_input_value(&self, v: &mut IndexMap<&str, InputValue<WundergraphScalarValue>>) {
450        L::Table::to_inner_input_value(&self.filter, v)
451    }
452
453    fn register_fields<'r>(
454        _info: &NameBuilder<Self>,
455        registry: &mut Registry<'r, WundergraphScalarValue>,
456    ) -> Vec<Argument<'r, WundergraphScalarValue>> {
457        L::Table::register_fields(&NameBuilder::default(), registry)
458    }
459}
460
461macro_rules! __impl_build_filter_for_tuples {
462    ($(
463        $Tuple:tt {
464            $(($idx:tt) -> $T:ident, $ST: ident, $TT: ident,) +
465        }
466    )+) => {
467        $(
468            impl<$($T,)* Back, Table> BuildFilter<Back> for ($($T,)*)
469            where $($T: BuildFilter<Back, Ret = Box<dyn BoxableFilter<Table, Back, SqlType = Bool>>> + 'static,)*
470                  Back: Backend + 'static,
471                  Table: 'static
472            {
473                type Ret = Box<dyn BoxableFilter<Table, Back, SqlType = Bool>>;
474
475                fn into_filter(self) -> Option<Self::Ret> {
476                    use crate::query_builder::selection::filter::collector::{AndCollector, FilterCollector};
477
478                    let mut and = AndCollector::<_, Back>::default();
479                    $(
480                        and.append_filter(self.$idx);
481                    )*
482
483                        and.into_filter()
484                }
485            }
486
487            impl<$($T,)* Loading, Back, Ctx> InnerFilter for FilterBuildHelper<($(Option<$T>,)*), Loading, Back, Ctx>
488            where Back: Backend + ApplyOffset + 'static,
489                Loading::Table: 'static,
490                Loading: LoadingHandler<Back, Ctx>,
491                <Loading::Table as QuerySource>::FromClause: QueryFragment<Back>,
492                Back::QueryBuilder: Default,
493                $($T: GraphQLType<WundergraphScalarValue, TypeInfo = NameBuilder<$T>> + ToInputValue<WundergraphScalarValue> + FromInputValue<WundergraphScalarValue> + Nameable + FromLookAheadValue,)*
494            {
495                type Context = ();
496
497                const FIELD_COUNT: usize = $Tuple;
498
499                fn from_inner_input_value(
500                    obj: IndexMap<&str, &InputValue<WundergraphScalarValue>>
501                ) -> Option<Self> {
502                    let mut values = ($(Option::<$T>::default(),)*);
503                    for (name, value) in obj {
504                        match name {
505                            $(
506                                n if n == Loading::FIELD_NAMES[$idx] => {
507                                    values.$idx = <$T as FromInputValue<WundergraphScalarValue>>::from_input_value(value);
508                                }
509                            )*
510                                _  => {}
511                        }
512                    }
513                    Some(FilterBuildHelper(values, PhantomData))
514                }
515
516                fn from_inner_look_ahead(
517                    objs: &[(&str, LookAheadValue<'_, WundergraphScalarValue>)]
518                ) -> Self {
519                    let mut values = ($(Option::<$T>::default(),)*);
520                    for (name, value) in objs {
521                        match name {
522                            $(
523                                n if *n == Loading::FIELD_NAMES[$idx] => {
524                                    values.$idx = <$T as FromLookAheadValue>::from_look_ahead(value);
525                                }
526                            )*
527                            _  => {}
528                        }
529                    }
530                    FilterBuildHelper(values, PhantomData)
531                }
532
533                fn to_inner_input_value(
534                    &self, v: &mut IndexMap<&str, InputValue<WundergraphScalarValue>>
535                ) {
536                    let inner = &self.0;
537                    $(
538                        let value = <Option<$T> as ToInputValue<WundergraphScalarValue>>::to_input_value(&inner.$idx);
539                        v.insert(Loading::FIELD_NAMES[$idx], value);
540                    )*
541                }
542
543                fn register_fields<'r>(
544                    _info: &NameBuilder<Self>,
545                    registry: &mut Registry<'r, WundergraphScalarValue>,
546                ) -> Vec<Argument<'r, WundergraphScalarValue>> {
547                    vec![
548                        $(
549                            registry.arg_with_default::<Option<$T>>(
550                                Loading::FIELD_NAMES[$idx],
551                                &None,
552                                &NameBuilder::default()
553                            ),
554                        )*
555                    ]
556                }
557            }
558
559            impl<$($T,)* $($ST,)* Back, Ctx> CreateFilter for ColumnFilterConverter<($($T,)*), ($($ST,)*), Back, Ctx>
560                where $($T: AsColumnFilter<$ST, Back, Ctx>,)*
561            {
562                type Filter = ($(Option<<$T as AsColumnFilter<$ST, Back, Ctx>>::Filter>,)*);
563            }
564
565            impl<$($T,)* Loading, Back, Ctx> CreateFilter for NonColumnFilterConveter<($($T,)*), Loading, Back, Ctx>
566                where $($T: AsNonColumnFilter<Loading, Back, Ctx>,)*
567            {
568                type Filter = ($(Option<<$T as AsNonColumnFilter<Loading, Back, Ctx>>::Filter>,)*);
569            }
570        )*
571    }
572}
573
574__diesel_for_each_tuple!(__impl_build_filter_for_tuples);