script

script

Hi:
I am working on this library for querying, and I have this problem for preloading tables. My params are like this:

%{
"customer" => %{
"$binding" => :first,
"$include" => %{"users" => %{"$binding" => :last}}
},
"facilities" => %{
"$binding" => :first,
"$include" => %{"instance" => %{"$binding" => :last}}
},
"instances" => %{"$binding" => :first, "$include" => "facilities"}
}

I am successfully able to make joins for all of these thanks to @Eiji help. binding: :first means bind to the main table and binding_ :last bind to the last table. These params are dynamic and contain any number of nested includes.

The problem I am facing is preloading them dynamically. So far I am successful to build dynamic preloads from the above. I wrote a function that loop through the params and build a list like below

               preload: [ {:instances, :facilities}, [customer: :users, facilities: :instance]]>

But as I go one more level deep like this:

%{
"customer" => %{
"$binding" => :first,
"$include" => %{"users" =>  %{"$binding" => :last, "$include" => %{"items" => %{"$binding" => :last}}}}
}

The preload will look like this

 preload: [ {:instances, :facilities}, [customer: {:users, :items}, facilities: :instance]]>

And it’s not very maintainable and I even don’t know how to approach it.

Can anyone please suggest any workaround for this.

Is there any other approach I can take which is not this complex?

Thanks

Showing Posts 1 to 10

Eiji

Eiji

@script Can you please share your code for collecting preload list and how preload should work for more levels deep? Also some people may not know what you said, so I would just like to your previous question.

script

script OP

Thanks, @Eiji for providing the link to the question. Below is the code. It’s not optimized as your code looks :slightly_smiling_face: but it’s doing the work for now.

def build_preload_query(include) do
Enum.reduce(include, [], fn {k, v}, acc ->
  case v["$binding"] do
    :first ->
      acc ++ preload_last(k, v)

    _ ->
      acc
  end
end)
end

def preload_last(k, v) do
if is_nil(v["$include"]) do
  [string_to_atom(k)]
else
  {include, _map} = Map.pop(v, "$include")

  case include do
    include when is_map(include) ->
      key = Map.keys(include) |> hd()
      [{string_to_atom(k), string_to_atom(key)}]

    _ ->
      []
  end
end
end

This code is looping through the params and if the $include is a map it will use a tuple to preload the nested table with the outer table else just the first table.
The output is this:

    [customer: :users, facilities: :instance]

If I add another $include map inside users as I showed above in my second example. This function should produce preload like this:

 [customer: [users: :items], facilities: :instance]

And if there is another nested include inside items the list looks like this:

[customer: [users: [items: :other_table], facilities: :instance]
Eiji

Eiji

Here is my first version:

defmodule Example do
  def sample(map) when is_map(map), do: do_sample(map)

  defp do_sample(map), do: Enum.reduce(map, [], &do_sample/2)

  defp do_sample({key, %{"$include" => include}}, acc) when is_map(include),
    do: [{String.to_atom(key), do_sample(include)} | acc]

  defp do_sample({key, %{"$include" => include}}, acc) when is_bitstring(include),
    do: [{String.to_atom(key), String.to_atom(include)} | acc]

  defp do_sample({key, %{"$binding" => :last}}, _acc), do: String.to_atom(key)

  defp do_sample({_key, _value}, acc), do: acc
end

Please let me know if it works as expected for you in all cases.

script

script OP

Thanks @Eiji. I will let you know.

script

script OP

Hi @Eiji. so, I give it a try with below params.

%{
  "$include" => %{
    "facilities" => %{
      "$include" => %{"instance" => %{}}
    },
    "instances" => %{"$include" => %{"facilities" => %{}}},
    "customer" => %{
      "$include" => %{"users" => %{"$include" => %{"facilities" => %{}}}}
    }
  }
} 

The output it produced is this:

facilities
[users: :facilities]
:instance
:facilities
[instances: :facilities, facilities: :instance, customer: [users: :facilities]] 

The last line has correct output but it’s returning all other values before the final result. I think it’s reducing over the params and instead if returning the final output returns all the value it’s reducing over.

Eiji

Eiji

@script Sorry, I completely don’t understand.

For me Example.sample(data) returns [] for such input. I have no idea how it become like that. Looks like you did not gave a correct input here.

This is not even 1 list. How 1 call could turn into multiple results? Here looks like you pass few different inputs.

I have no information what happen on your side. Please send me all cases with their expected results. Something like in TDD, so if all tests would pass then implementation is correct.

If you have a public repo simply add tests like:

defmodule ExampleTest do
  use ExUnit.Case

  @first_input […]
  @second_input […]
  @third_input […]

  @first_expected […]
  @second_expected […]
  @third_expected […]

  test "all" do
    assert Example.sample(@first_input) == @first_expected
    assert Example.sample(@second_input) == @second_expected
    assert Example.sample(@third_input) == @third_expected
  end
end

and I will just make a PR for that.

script

script OP

Sorry, @Eiji. you are right. it is an issue on my end. I am debugging it. I will let you know.
Thanks for your quick insight.

script

script OP

@Eiji Thanks. It works perfectly as always.

script

script OP

@Eiji sorry Just his one case I forgot to mention if there is a list.

           “instances” => %{"$binding" => :first, “$include” => [“facilities”, “units”]} }

The prelod list looks like this:

                [instances: [:facilities, :units]]
Eiji

Eiji

@script Here we go:

defmodule Example do
  def sample(map) when is_map(map), do: do_sample(map)

  defp do_sample(map), do: Enum.reduce(map, [], &do_sample/2)

  defp do_sample({key, %{"$include" => include}}, acc) when is_map(include),
    do: [{String.to_atom(key), do_sample(include)} | acc]

  defp do_sample({key, %{"$include" => include}}, acc) when is_bitstring(include),
    do: [{String.to_atom(key), String.to_atom(include)} | acc]

  defp do_sample({key, %{"$include" => include}}, acc) when is_list(include),
    do: [{String.to_atom(key), Enum.map(include, &String.to_atom/1)} | acc]

  defp do_sample({key, %{"$binding" => :last}}, _acc), do: String.to_atom(key)

  defp do_sample({_key, _value}, acc), do: acc
end

Just one extra function clausule. It’s why I love writing configurable and easy to maintain code! :077:

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
apz
I’m new to elixir and just tried to install the elixirLS extension for VScode(ium) and it is throwing some errors that I would like help ...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews