Multiple nested filters to get specific values

I got the following situation: I have multiple chats, wanted to get in my preload the newest message for each chat, but limit: 1 gives only one message, not one for each chat. So I thought about filtering my structure after the preload is done.
Here my code:

|> Enum.filter(fn(x) ->
      Enum.filter(x.chat_users, fn(y) ->
        case y ==, 0) do
          true ->
            Enum.filter(y.chat_messages, fn(z) ->
              case z ==, 0) do
                true -> true
                false -> false
          false ->  false

In my preload I ordered the chat_messages and chat_users desc, so are the newest ones.
I know the inner filter functions have no effect on the outer scope. I know, looks really ugly and is not working.

In simple words I have my result with chats and it has chat_users and each chat_user has chat_messages and i need for each chat the newest user ( and the newest chat_message for this user.

How to accomplish this the elixir way? I don’t know how to use reduce inside my nested structure.



You can probably use a lateral join to preload the newest message for each chat.

As for your code snippet, it might be simplified to

%{chat_users: chat_users} = result, fn
  %{chat_messages: [first_message | _rest]} = chat_user -> {chat_user, first_message}
  chat_user -> {chat_user, nil}

It would return a list of user/message tuples with the latter possibly being nil.

1 Like


here is what I am using as preload:

defp preload_overview_associations(chat, params) do
  updated_since = Map.get(params, "updated_since", "1970-01-01T12:00:00")
  chat_user_query = from cu in Chat.ChatUser,
  where: cu.updated_at > ^updated_since,
  order_by: [desc: cu.updated_at]

  user_query = from u in User,
  where: u.deleted == false

  chat_message_query = from m in Chat.ChatMessage,
  where: m.updated_at > ^updated_since,
  order_by: [desc: m.updated_at]

Repo.preload(chat, [
  [[picture: LimitedVariants.preload_expr(params)],
    [user: [avatar: LimitedVariants.preload_expr(params)]]]]],
      [user: {user_query, [avatar: LimitedVariants.preload_expr(params)]}],
      [chat: [picture: LimitedVariants.preload_expr(params)]],
        [[picture: LimitedVariants.preload_expr(params)], :audio, :video, :attachment]}
  [picture: LimitedVariants.preload_expr(params)]

To be honest, no idea how to add a lateral join to my existing stuff :confused:

Try to write it as SQL, it usually makes things simpler for me. Maybe refactor that function into functions / multis as well while at it.

You can probably replace

  chat_message_query = from m in Chat.ChatMessage,
  where: m.updated_at > ^updated_since,
  order_by: [desc: m.updated_at]

with a lateral join on chat_user_query.

1 Like

I think it is much easier to modify the result after the existing preload. Would take me 5 minutes in languages like C#, JS, Java… so should be also very easy for the elixir pros.

I’ve posted a possible function above …

Never used a lateral join. Need to check first, how to do it. Still reading :wink:

I meant this function.

For a lateral join, just replace chat_message_query with it. :wink:

Ah, I checked the wrong answer. Looked at the one above, not the one before. THX, will try it.

I chose to remove the data after the preload:

|> chat ->
  Map.update!(chat, :chat_users, fn chat_users ->
    case, 0) do
      nil -> nil
      _ ->
        chat_user =
        |> List.first
        |> Map.update!(:chat_messages, & [List.first(&1)])

Seems I still need more practice in Elixir :frowning:

  %{chat_users: [%{chat_messages: chat_messages} = first_chat_user | _rest]} = chat ->
    %{chat | chat_users: [%{first_chat_user | chat_messages: List.first(chat_messages)}]}
  other ->

does the same thing but in a bit more declarative way.


And looks even better :slight_smile: THX