@@ -5,11 +5,11 @@ defmodule PhoenixDatatables.Query do
55 import Ecto.Query
66 use PhoenixDatatables.Query.Macros
77 alias Ecto.Query.JoinExpr
8- alias PhoenixDatatables.Request.Params
9- alias PhoenixDatatables.Request.Column
10- alias PhoenixDatatables.Request.Search
118 alias PhoenixDatatables.Query.Attribute
129 alias PhoenixDatatables.QueryException
10+ alias PhoenixDatatables.Request.Column
11+ alias PhoenixDatatables.Request.Params
12+ alias PhoenixDatatables.Request.Search
1313
1414 @ doc """
1515 Add order_by clauses to the provided queryable based on the "order" params provided
@@ -24,30 +24,32 @@ defmodule PhoenixDatatables.Query do
2424 else
2525 build_schema_sorts ( queryable , params )
2626 end
27+
2728 do_sorts ( queryable , sorts , options )
2829 end
2930
3031 defp build_column_sorts ( % Params { order: orders } = params , columns ) do
3132 for order <- orders do
3233 with dir when is_atom ( dir ) <- cast_dir ( order . dir ) ,
33- % Column { } = column <- params . columns [ order . column ] ,
34- true <- column . orderable ,
35- { column , join_index } when is_number ( join_index )
36- <- cast_column ( column . data , columns ) do
34+ % Column { } = column <- params . columns [ order . column ] ,
35+ true <- column . orderable ,
36+ { column , join_index } when is_number ( join_index ) <-
37+ cast_column ( column . data , columns ) do
3738 { dir , column , join_index }
3839 end
3940 end
4041 end
4142
4243 defp build_schema_sorts ( queryable , % Params { order: orders } = params ) do
4344 schema = schema ( queryable )
45+
4446 for order <- orders do
4547 with dir when is_atom ( dir ) <- cast_dir ( order . dir ) ,
46- % Column { } = column <- params . columns [ order . column ] ,
47- true <- column . orderable ,
48- % Attribute { } = attribute <- Attribute . extract ( column . data , schema ) ,
49- join_index when is_number ( join_index )
50- <- join_order ( queryable , attribute . parent ) do
48+ % Column { } = column <- params . columns [ order . column ] ,
49+ true <- column . orderable ,
50+ % Attribute { } = attribute <- Attribute . extract ( column . data , schema ) ,
51+ join_index when is_number ( join_index ) <-
52+ join_order ( queryable , attribute . parent ) do
5153 { dir , attribute . name , join_index }
5254 end
5355 end
@@ -61,28 +63,31 @@ defmodule PhoenixDatatables.Query do
6163
6264 @ doc false
6365 def join_order ( _ , nil ) , do: 0
66+
6467 def join_order ( % Ecto.Query { } = queryable , parent ) do
6568 case Enum . find_index ( queryable . joins , & ( join_relation ( & 1 ) == parent ) ) do
6669 nil -> nil
6770 number when is_number ( number ) -> number + 1
6871 end
6972 end
73+
7074 def join_order ( queryable , parent ) do
7175 QueryException . raise ( :join_order , """
7276
73- An attempt was made to interrogate the join structure of #{ inspect queryable }
77+ An attempt was made to interrogate the join structure of #{ inspect ( queryable ) }
7478 This is not an %Ecto.Query{}. The most likely cause for this error is using
7579 dot-notation(e.g. 'category.name') in the column name defined in the datatables
7680 client config but a simple Schema (no join) is used as the underlying queryable.
7781
78- Please check the client config for the fields belonging to #{ inspect parent } . If
82+ Please check the client config for the fields belonging to #{ inspect ( parent ) } . If
7983 the required field does belong to a different parent schema, that schema needs to
8084 be joined in the Ecto query.
8185
8286 """ )
8387 end
8488
8589 defp join_relation ( % JoinExpr { assoc: { _ , relation } } ) , do: relation
90+
8691 defp join_relation ( _ ) do
8792 QueryException . raise ( :join_relation , """
8893
@@ -107,27 +112,36 @@ defmodule PhoenixDatatables.Query do
107112
108113 """ )
109114 end
115+
110116 defp check_from ( from ) , do: from
111117
112118 defp cast_column ( column_name , sortable )
113- when is_list ( sortable )
114- and is_tuple ( hd ( sortable ) )
115- and is_atom ( elem ( hd ( sortable ) , 0 ) ) do #Keyword
119+ # Keyword
120+ when is_list ( sortable ) and
121+ is_tuple ( hd ( sortable ) ) and
122+ is_atom ( elem ( hd ( sortable ) , 0 ) ) do
116123 [ parent | child ] = String . split ( column_name , "." )
124+
117125 if parent in Enum . map ( Keyword . keys ( sortable ) , & Atom . to_string / 1 ) do
118126 member = Keyword . fetch! ( sortable , String . to_atom ( parent ) )
127+
119128 case member do
120129 children when is_list ( children ) ->
121130 with [ child ] <- child ,
122- [ child ] <- Enum . filter ( Keyword . keys ( children ) ,
123- & ( Atom . to_string ( & 1 ) == child ) ) ,
124- { :ok , order } when is_number ( order )
125- <- Keyword . fetch ( children , child ) do
131+ [ child ] <-
132+ Enum . filter (
133+ Keyword . keys ( children ) ,
134+ & ( Atom . to_string ( & 1 ) == child )
135+ ) ,
136+ { :ok , order } when is_number ( order ) <-
137+ Keyword . fetch ( children , child ) do
126138 { child , order }
127139 else
128140 _ -> { :error , "#{ column_name } is not a sortable column." }
129141 end
130- order when is_number ( order ) -> { String . to_atom ( parent ) , order }
142+
143+ order when is_number ( order ) ->
144+ { String . to_atom ( parent ) , order }
131145 end
132146 else
133147 { :error , "#{ column_name } is not a sortable column." }
@@ -166,7 +180,9 @@ defmodule PhoenixDatatables.Query do
166180 true ->
167181 { num , _ } = Integer . parse ( num )
168182 num
169- false -> num
183+
184+ false ->
185+ num
170186 end
171187 end
172188
@@ -180,41 +196,91 @@ defmodule PhoenixDatatables.Query do
180196 columns = options [ :columns ]
181197 do_search ( queryable , params , columns )
182198 end
199+
183200 defp do_search ( queryable , % Params { search: % Search { value: "" } } , _ ) , do: queryable
201+
184202 defp do_search ( queryable , % Params { } = params , searchable ) when is_list ( searchable ) do
185203 search_term = "%#{ params . search . value } %"
186204 dynamic = dynamic ( [ ] , false )
187- dynamic = Enum . reduce params . columns , dynamic , fn ( { _ , v } , acc_dynamic ) ->
188- with { column , join_index } when is_number ( join_index )
189- <- v . data |> cast_column ( searchable ) ,
190- true <- v . searchable do
191- acc_dynamic
192- |> search_relation ( join_index ,
193- column ,
194- search_term )
195- else
196- _ -> acc_dynamic
197- end
198- end
205+
206+ dynamic =
207+ Enum . reduce ( params . columns , dynamic , fn { _ , v } , acc_dynamic ->
208+ with { column , join_index } when is_number ( join_index ) <-
209+ v . data |> cast_column ( searchable ) ,
210+ true <- v . searchable do
211+ acc_dynamic
212+ |> search_relation (
213+ join_index ,
214+ column ,
215+ search_term
216+ )
217+ else
218+ _ -> acc_dynamic
219+ end
220+ end )
221+
199222 where ( queryable , [ ] , ^ dynamic )
200223 end
201224
202225 defp do_search ( queryable , % Params { search: search , columns: columns } , _searchable ) do
203226 search_term = "%#{ search . value } %"
204227 schema = schema ( queryable )
205228 dynamic = dynamic ( [ ] , false )
229+
206230 dynamic =
207- Enum . reduce columns , dynamic , fn ( { _ , v } , acc_dynamic ) ->
231+ Enum . reduce ( columns , dynamic , fn { _ , v } , acc_dynamic ->
208232 with % Attribute { } = attribute <- v . data |> Attribute . extract ( schema ) ,
209- true <- v . searchable do
233+ true <- v . searchable do
210234 acc_dynamic
211- |> search_relation ( join_order ( queryable , attribute . parent ) ,
212- attribute . name ,
213- search_term )
235+ |> search_relation (
236+ join_order ( queryable , attribute . parent ) ,
237+ attribute . name ,
238+ search_term
239+ )
214240 else
215241 _ -> acc_dynamic
216242 end
217- end
243+ end )
244+
245+ where ( queryable , [ ] , ^ dynamic )
246+ end
247+
248+ def search_columns ( queryable , params , options \\ [ ] ) do
249+ if has_column_search? ( params . columns ) do
250+ columns = options [ :columns ] || [ ]
251+ do_search_columns ( queryable , params , columns )
252+ else
253+ queryable
254+ end
255+ end
256+
257+ defp has_column_search? ( columns ) when is_map ( columns ) do
258+ columns = Map . values ( columns )
259+ Enum . any? ( columns , & ( & 1 . search . value != "" ) )
260+ end
261+
262+ defp has_column_search? ( _ ) , do: false
263+
264+ defp do_search_columns ( queryable , params , columns ) do
265+ dynamic = dynamic ( [ ] , true )
266+
267+ dynamic =
268+ Enum . reduce ( params . columns , dynamic , fn { _ , v } , acc_dynamic ->
269+ with { column , join_index } when is_number ( join_index ) <-
270+ cast_column ( v . data , columns ) ,
271+ true <- v . searchable ,
272+ true <- v . search . value != "" do
273+ acc_dynamic
274+ |> search_relation_and (
275+ join_index ,
276+ column ,
277+ "%#{ v . search . value } %"
278+ )
279+ else
280+ _ -> acc_dynamic
281+ end
282+ end )
283+
218284 where ( queryable , [ ] , ^ dynamic )
219285 end
220286
@@ -239,15 +305,15 @@ defmodule PhoenixDatatables.Query do
239305
240306 total_entries || 0
241307 end
242-
243308end
244309
245310defmodule PhoenixDatatables.QueryException do
246311 defexception [ :message , :operation ]
247312
248- @ dialyzer { :no_return , raise: 1 } #yes we know it raises
313+ # yes we know it raises
314+ @ dialyzer { :no_return , raise: 1 }
249315
250316 def raise ( operation , message \\ "" ) do
251- Kernel . raise __MODULE__ , [ operation: operation , message: message ]
317+ Kernel . raise ( __MODULE__ , operation: operation , message: message )
252318 end
253319end
0 commit comments