Parsing Ecto Struct and forming our desired map list

I have this query

Repo.all(
                        from(u in CompareProduct,
                              join: c in AmazonProduct,
                              on: u.amazon_product_id == c.amazon_product_id,
                              join: f in FlipkartProduct,
                              on: u.flipkart_product_id == f.flipkart_product_id,
                              where: ilike(u.product_name, ^"%#{name}%"),
                              select: {u, c, f}
                            )
                        )

And the result of that query is

[
  {
    %Market.Compare.CompareProduct{
      __meta__: #Ecto.Schema.Metadata<:loaded, "comparing_products">,
      amazon_product_id: "B01CHVJ0IW",
      amazon_products: #Ecto.Association.NotLoaded<association :amazon_products is not loaded>,
      flipkart_product_id: "CZSTSALT1000005",
      flipkart_products: #Ecto.Association.NotLoaded<association :flipkart_products is not loaded>,
      id: 5,
      product_name: "Aashirvaad Iodised salt 1 kg",
      sub_categories: #Ecto.Association.NotLoaded<association :sub_categories is not loaded>,
      sub_category_id: 12,
    },
    %Market.Amazon.AmazonProduct{
      __meta__: #Ecto.Schema.Metadata<:loaded, "amazon_products">,
      amazon_product_id: "B01CHVJ0IW",
      brand_name: "Aashirvaad",
      comparing_products: #Ecto.Association.NotLoaded<association :comparing_products is not loaded>,
      description: "This salt is tested and certified by pho (Amritsar) at the time of transport ",
      discounts: nil,
      image_url: "https://images-na.ssl-images-amazon.com/images/I/514TEdVI38L.jpg",
      price: 17.0,
    },
    %Market.Flipkart.FlipkartProduct{
      __meta__: #Ecto.Schema.Metadata<:loaded, "flipkart_products">,
      brand_name: "Ashirwad",
      comparing_products: #Ecto.Association.NotLoaded<association :comparing_products is not loaded>,
      description: "",
      discounts: nil,
      flipkart_product_id: "CZSTSALT1000005",
      image_url: "",
      price: 16.0,
>
    }
  },


  {
    %Market.Compare.CompareProduct{
      __meta__: #Ecto.Schema.Metadata<:loaded, "comparing_products">,
      amazon_product_id: "B01CHVJA5A",
      amazon_products: #Ecto.Association.NotLoaded<association :amazon_products is not loaded>,
      flipkart_product_id: "CZSTSALT1000030",
      flipkart_products: #Ecto.Association.NotLoaded<association :flipkart_products is not loaded>,
      id: 12,
      product_name: "Catch Sprinkles Black Salt, 200g",
      sub_categories: #Ecto.Association.NotLoaded<association :sub_categories is not loaded>,
      sub_category_id: 13,
    },
    %Market.Amazon.AmazonProduct{
      __meta__: #Ecto.Schema.Metadata<:loaded, "amazon_products">,
      amazon_product_id: "B01CHVJA5A",
      brand_name: "catch",
      comparing_products: #Ecto.Association.NotLoaded<association :comparing_products is not loaded>,
      description: "This salt is tested and certified by pho (Amritsar) at the time of transport ",
      discounts: nil,
      image_url: "https://images-na.ssl-images-amazon.com/images/I/71fl1yWCOXL._SL1500_.jpg",
      price: 35.0,
      
    },
    %Market.Flipkart.FlipkartProduct{
      __meta__: #Ecto.Schema.Metadata<:loaded, "flipkart_products">,
      brand_name: "Catch",
      comparing_products: #Ecto.Association.NotLoaded<association :comparing_products is not loaded>,
      description: "",
      discounts: nil,
      flipkart_product_id: "CZSTSALT1000030",
      image_url: "https://rukminim1.flixcart.com/image/832/832/ja73ki80/salt/m/b/e/200-sprinkler-black-salt-catch-original-imaeztw5bjehvyuf.jpeg?q=70",
      price: 34.0,
      
    }
  },
  ...
  ....
  ....
  ...
]

So i have three struct in each row of the ecto result.

1.%CompareProduct{
2.%AmazonProduct{
3.%FlipkartProduct{

What I am expecting is i want to form a new map list with taking one field value from the .%CompareProduct{ struct and adding that value alone in the other two struct

For example

From the %CompareProduct struct, i want to fetch the sub_category_id field and add that field in the %AmazonProduct{ and .%FlipkartProduct{ maps.

And the new map formation must be in this format

[
    {   
      amazon_product_id: "B01CHVJ0IW",
      brand_name: "Aashirvaad",
      comparing_products: #Ecto.Association.NotLoaded<association :comparing_products is not loaded>,
      description: "This salt is tested and certified by pho (Amritsar) at the time of transport ",
      discounts: nil,
      image_url: "https://images-na.ssl-images-amazon.com/images/I/514TEdVI38L.jpg",
      price: 17.0,
      
      **sub_category_id: 12,**
    },
    {
      brand_name: "Ashirwad",
      comparing_products: #Ecto.Association.NotLoaded<association :comparing_products is not loaded>,
      description: "",
      discounts: nil,
      flipkart_product_id: "CZSTSALT1000005",
      image_url: "",
      price: 16.0,
      
      **sub_category_id: 12,**
    },
    {
      amazon_product_id: "B01CHVJA5A",
      brand_name: "catch",
      comparing_products: #Ecto.Association.NotLoaded<association :comparing_products is not loaded>,
      description: "This salt is tested and certified by pho (Amritsar) at the time of transport ",
      discounts: nil,
      image_url: "https://images-na.ssl-images-amazon.com/images/I/71fl1yWCOXL._SL1500_.jpg",
      price: 35.0,
      
      **sub_category_id: 13,**
    },
    {
      brand_name: "Catch",
      comparing_products: #Ecto.Association.NotLoaded<association :comparing_products is not loaded>,
      description: "",
      discounts: nil,
      flipkart_product_id: "CZSTSALT1000030",
      image_url: "https://rukminim1.flixcart.com/image/832/832/ja73ki80/salt/m/b/e/200-sprinkler-black-salt-catch-original-imaeztw5bjehvyuf.jpeg?q=70",
      price: 34.0,
      
      **sub_category_id: 13**,
    }
  
  ...
  ....
  ....
  ...
}

I am a beginner in Elixir. Can someone give me an insight on how to achieve this? Your help is much appreciated.

:wave:

Maybe something like

add_sub_category_id = fn %_struct{} = record, sub_category_id ->
  record
  |> Map.from_struct()
  |> Map.put(:sub_category_id, sub_category_id)
end

Enum.flat_map(results, fn {%{sub_category_id: sub_category_id}, amazon, flipkart} ->
  [
    add_sub_category_id.(amazon, sub_category_id),
    add_sub_category_id.(flipkart, sub_category_id)
  ]
end)
1 Like

Should be easier to just do it in the SQL itself, by replacing the select: {u, c, f} call with something like:

    select: %{
      amazon_product_id: u.amazon_product_id,
      brand_name: c.brand_name,
      comparing_products: nil, # This wasn't loaded so unsure why this is in the final map?
      description: c.description,
      discounts: c.discounts,
      image_url: c.image_url,
      price: c.price,
      sub_category_id: 12,
    }

Or change it up however you want. It’s much easier and much FASTER to have the SQL return exactly what you want, especially with the reduced data transferred. :slight_smile:

1 Like

Worked thanks:-)

Unless I misunderstand the question, you can also use the preload expression, if you’re actually only interested in the CompareProduct objects:

from(u in CompareProduct,
       join: c in AmazonProduct,
       on: u.amazon_product_id == c.amazon_product_id,
       join: f in FlipkartProduct,
       on: u.flipkart_product_id == f.flipkart_product_id,
       where: ilike(u.product_name, ^"%#{name}%"),
       preload: [amazon_products: c, flipkart_products: f]
)

https://hexdocs.pm/ecto/Ecto.Query.html#preload/3