@@ -238,10 +238,7 @@ defmodule Hawk.JsonApi.Document do
238238 defp put_included ( document , models , opts ) do
239239 primary_resources = MapSet . new ( models , & resource_identity / 1 )
240240
241- included =
242- models
243- |> included_resources ( Keyword . get ( opts , :preloads , [ ] ) , opts )
244- |> Enum . reject ( & MapSet . member? ( primary_resources , { & 1 . type , & 1 . id } ) )
241+ { included , _seen } = included_resources ( models , Keyword . get ( opts , :preloads , [ ] ) , opts , primary_resources )
245242
246243 if included == [ ] do
247244 document
@@ -254,28 +251,40 @@ defmodule Hawk.JsonApi.Document do
254251 { Schema . metadata ( model ) . type , to_string ( Map . get ( model , Schema . identity ( model ) ) ) }
255252 end
256253
257- defp included_resources ( models , preloads , opts ) do
258- models
259- |> Enum . flat_map ( & included_resources_for_model ( & 1 , preloads , opts ) )
260- |> Enum . uniq_by ( & { & 1 . type , & 1 . id } )
254+ defp included_resources ( models , preloads , opts , seen ) do
255+ Enum . reduce ( models , { [ ] , seen } , fn model , { included , seen } ->
256+ { model_included , seen } = included_resources_for_model ( model , preloads , opts , seen )
257+ { included ++ model_included , seen }
258+ end )
261259 end
262260
263- defp included_resources_for_model ( model , preloads , opts ) do
264- Enum . flat_map ( preloads , fn
265- name when is_atom ( name ) ->
266- direct_included_resources ( model , name , [ ] , opts )
261+ defp included_resources_for_model ( model , preloads , opts , seen ) do
262+ Enum . reduce ( preloads , { [ ] , seen } , fn
263+ name , { included , seen } when is_atom ( name ) ->
264+ { direct , seen } = direct_included_resources ( model , name , [ ] , opts , seen )
265+ { included ++ direct , seen }
267266
268- { name , nested } when is_atom ( name ) ->
269- direct_included_resources ( model , name , nested , opts )
267+ { name , nested } , { included , seen } when is_atom ( name ) ->
268+ { direct , seen } = direct_included_resources ( model , name , nested , opts , seen )
269+ { included ++ direct , seen }
270270 end )
271271 end
272272
273- defp direct_included_resources ( model , name , nested , opts ) do
273+ defp direct_included_resources ( model , name , nested , opts , seen ) do
274274 model
275275 |> related_models ( name )
276- |> Enum . flat_map ( fn related ->
277- [ resource_object ( related , Keyword . put ( opts , :preloads , nested ) ) ] ++
278- included_resources_for_model ( related , nested , opts )
276+ |> Enum . reduce ( { [ ] , seen } , fn related , { included , seen } ->
277+ identity = resource_identity ( related )
278+
279+ if MapSet . member? ( seen , identity ) do
280+ { nested_included , seen } = included_resources_for_model ( related , nested , opts , seen )
281+ { included ++ nested_included , seen }
282+ else
283+ seen = MapSet . put ( seen , identity )
284+ object = resource_object ( related , Keyword . put ( opts , :preloads , nested ) )
285+ { nested_included , seen } = included_resources_for_model ( related , nested , opts , seen )
286+ { included ++ [ object | nested_included ] , seen }
287+ end
279288 end )
280289 end
281290
0 commit comments