eddyraz

eddyraz

good Morning, I have a function that place a request to an elasticsearch REST API of a library management software, and I am trying to manipulate the response so I get the results(so far all OK), and the total results quantity(using a KErnel.tap/2 function for this). the response manipulation is as follows:

raw_response.body
    |> Jason.decode!()
    |> Map.fetch("hits")
    |> elem(1)
    |> tap(&get_total_results(&1, socket ))
    |> Map.fetch("hits")
    |> elem(1)

I use Kernel.tap/2 since the results is “hidden” in an upper structure of the response.

the get_total_results function is as follows:

@impl true
  def get_total_results(tr,socket) do
    total_res =
      tr["total"]
      |> tap(&IO.inspect(&1))

    socket =
      socket
      |> assign(results_qty: total_res)
  end

as soon as I use this function I get this error:

** (ArgumentError) assign/3 expects a socket from Phoenix.LiveView/Phoenix.LiveComponent  or an assigns map from Phoenix.Component as first argument, got: nil.

I have read the docs but have not got anyhing clear about this, I am using assigns update in otehr parts of this module and all is OK, I thought the problem was the socket id, but is the same that I use in mount/3 in the beggining of the code, the mount/3 function is this one.

  use DedalosPhoenixWeb, :live_view
  use Phoenix.HTML

  alias DedalosPhoenixWeb.{OpacLive, NavBarLive}

  def mount(params, _session, socket) do
    {:ok,
     assign(socket,
       page_number: 1,
       page_size: 10,
       results_qty: 20,
       query_params: params,
       filtered_results: []
     )}
  end

Please anyone could clear this for me, I am still readeing the official docs to see If I get something clear, thanks in advance.

Showing Posts 1 to 9

tomkonidas

tomkonidas

Can you show us the function where you get the raw_response? I see you pass in socket but do not know where it comes from.

eddyraz

eddyraz OP

Here is the complete function

@impl true
  def process_request(terms, socket) do
    index_terms = build_elastic_index(terms, socket)

    uri_q =
      terms
      |> Enum.reject(fn {k, _v} -> k == index_terms.filter_term end)
      |> Enum.map(fn x -> elem(x, 0) <> ": " <> elem(x, 1) <> " " end)
      |> Enum.intersperse(terms[index_terms.filter_term] <> " ")
      |> to_string()

    p_body = %{query: %{query_string: %{query: uri_q}}}
    body = Jason.encode(p_body) |> elem(1)
    body

    raw_response =
      Tesla.post!(
        'http://biblioteca.ccpadrevarela.org:19200/#{index_terms.search_index}/_search?size=10000',
        body,
        headers: ["User-Agent": "Dedalos", "Content-Type": "application/json"]
      )

    raw_response.body
    |> Jason.decode!()
    |> Map.fetch("hits")
    |> elem(1)
    |> tap(&get_total_results(&1, socket ))
    |> Map.fetch("hits")
    |> elem(1)
  end

also the build_elastic_function/2 function is here:

  @impl true
  def build_elastic_index(terms, socket) do
    if terms |> Map.has_key?("QUERY_OPERATOR") do
      %{filter_term: "QUERY_OPERATOR", search_index: "_all"}
    else
      %{filter_term: "choice", search_index: terms["choice"]}
    end
  end

tomkonidas

tomkonidas

If you IO.inspect the socket right before you use the assign/3? Does it return nil?
As a side note, whatever you assign to the socket in get_total_results/2 won’t be persisted as you do not return the updated socket.

trisolaran

trisolaran

This code will likely not do what you think it’s doing. The result from get_total_result will be discarded by tap. tap is used to perform side effects and returns its input unchanged (in this case, the result of elem(1)).
You can add a dbg() statement at the end of the pipeline and you’ll see what I mean/

al2o3cr

al2o3cr

A general style note - Map.fetch + elem is going to crash if the specified key is missing (since fetch returns a bare :error), but not with a particularly clear error message:

iex(1)> elem(:error, 1)
** (ArgumentError) errors were found at the given arguments:

  * 2nd argument: not a tuple

    :erlang.element(2, :error)
    iex:1: (file)

Prefer using Map.fetch! which will also crash, but with a message that clearly states what wasn’t found:

iex(2)> Map.fetch!(%{}, :not_there)
** (KeyError) key :not_there not found in: %{}
    (stdlib 4.1.1) :maps.get(:not_there, %{})
    iex:2: (file)

As others have already pointed out in this thread, you’ll want to return that modified socket from process_request if you expect the modified assigns to stick around.

But there’s a bigger design idea at the heart: keeping “socket manipulation” code separate from “data manipulation” code. A function like get_total_results is trying to do two unrelated things and making both of them harder:

  • get the total from the results
  • put it into the assigns

Separating those, even if just into separate functions in the same module, can make it easier to see what’s going on (and avoid tricky “how do I return socket from here?” conundrums):

  hits =
    raw_response.body
    |> Jason.decode!()
    |> Map.fetch!("hits")

  socket =
    socket
    |> assign(:results_qty, hits["total"])
    |> assign(:results, hits)

I’m guessing on that last one, but it seemed logical that you’d want the results to go with the count in the assigns…

BradS2S

BradS2S

I could be wrong but I think it is better to use MyAppWeb, :html for functional components ( or :component for 1.6) and :live_component.

One hint is that they have the Phoenix.HTML module in a separate section for modules that are “under the hood”.

Also if you look at the documentation Phoenix.HTML is imported in a private function.

Maybe consider separating your LiveView and component(s) into different modules.

eddyraz

eddyraz OP

that was my intent to create a side effect using the calling to the function get_total_results and in the body of that function is where i’ d like to update the assigns. I am seeing now as tomkonidas stated that when I put IO.inspect(socket) right before the assign/3 returns nil.

eddyraz

eddyraz OP

when I do that it returns this,

#Phoenix.LiveView.Socket<
  id: "phx-F06CX3tRkdalVgCF",
  endpoint: DedalosPhoenixWeb.Endpoint,
  view: DedalosPhoenixWeb.ResultsTableLive,
  parent_pid: nil,
  root_pid: #PID<0.1511.0>,
  router: DedalosPhoenixWeb.Router,
  assigns: %{
    __changed__: %{
      filtered_results: true,
      page_number: true,
      page_size: true,
      query_params: true,
      results_qty: true
    },
    filtered_results: [],
    flash: %{},
    live_action: nil,
    page_number: 1,
    page_size: 10,
    query_params: %{"QUERY_OPERATOR" => "OR", "título" => "amelia"},
    results_qty: 20
  },
  transport_pid: #PID<0.1506.0>,
  ...
>
[debug] Replied in 10ms
nil
BradS2S

BradS2S

I think you could do something like this.

raw_response.body
|> Jason.decode!()
|> Map.fetch(“hits”)
|> elem(1)
|> then( fn elem →
elem |> tap(&get_total_results(&1, socket ))
next = elem |> Map.fetch(“hits”)
next
end )
|> elem(1)

— All posts loaded —

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
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
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
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
georgeguimaraes
Just published claude-code-elixir, a plugin marketplace for Claude Code with Elixir support. These are the plugins I’ve been using for my...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews