Lists to Maps

I don’t know how to structure this question well so here it goes:
I have this:

[%{"first_name" => "John", "id" => "651029dc-636d-4e54-a03d-d80097c7567e", "last_name" => "Doe", "user_id" => "23232323"}, %{"first_name" => "John", "id" => "41188ffc-4304-4b01-9c45-7565aa8a4aab", "last_name" => "Doe", "user_id" => "23232323"}, %{"first_name" => "John", "id" => "277dd91f-c8e6-4104-b7b0-fecedf933b54", "last_name" => "Doe", "user_id" => "23232323"}]

I need to retrieve
%{"first_name" => "John", "id" => "651029dc-636d-4e54-a03d-d80097c7567e", "last_name" => "Doe", "user_id" => "23232323"}, %{"first_name" => "John", "id" => "41188ffc-4304-4b01-9c45-7565aa8a4aab", "last_name" => "Doe", "user_id" => "23232323"}, %{"first_name" => "John", "id" => "277dd91f-c8e6-4104-b7b0-fecedf933b54", "last_name" => "Doe", "user_id" => "23232323"}

Because of how I rendering these results on my GraphQL endpoint. How can I extract the data without the [ ] ?

2 Likes

I am a little confused by the question, but maybe you want to use Enum.each/2 or some of the other Enum functions. I hope that helps!

1 Like

The first part of your question shows a List of Maps, the second example you show, is not valid Elixir… At least not for itself…

So please try to elaborate further what exactly you want to achieve, where your data comes from and where you want to send it into.

2 Likes

My data is coming from a RethinkDB store, let me investigate further because I am using a resolver I got from a git repo to send that data into my GraphQL resolvers. If I have my data in the second format

%{"first_name" => "John", "id" => "651029dc-636d-4e54-a03d-d80097c7567e",
"last_name" => "Doe", "user_id" => "23232323"},

it works but not when it’s a list of Maps hence my frustration.

I am using rethinkdb-elixir and I the absinthe-graphql plugin. If I query my database using |> get(args.user_id) it returns data in %{} format but when I use |> filter(%{user_id: args.user_id}) it returns [%{}] and I get an error saying

expected a map, got: [%{

I am hitting a snag within this code:

# convert db keys to symbols since we're not using Ecto
  default_resolve fn _, %{source: source, definition: %{name: name}} ->
    if is_boolean(Map.get(source, name)) do
      {:ok, Map.get(source, name)}
    else
      {:ok, Map.get(source, name) || Map.get(source, String.to_existing_atom(name))}
    end
  end

Generally I am following this repo

The source part of the code above is:

[%{"first_name" => "John", "id" => "651029dc-636d-4e54-a03d-d80097c7567e",
"last_name" => "Doe", "user_id" => "23232323"},
%{"first_name" => "John", "id" => "41188ffc-4304-4b01-9c45-7565aa8a4aab",
"last_name" => "Doe", "user_id" => "23232323"},
%{"first_name" => "John", "id" => "277dd91f-c8e6-4104-b7b0-fecedf933b54",
"last_name" => "Doe", "user_id" => "23232323"}]
2 Likes

Since you not really did go into my questions, I can’t really help you.

Can you point us to the exact libraries you are using, can you tell us how you get the data and how you emit the data to the receiver?

Is the list you get from a message sent by another node or was it returned by a simple function call?
Do you need to send the Stuff Map by Map to your GraphQL-thing? Do you need to do it by a function call against a library or do you send a message?

If you can’t answer these questions using words, set up a minimum mix-project which shows your problem in a simplified manner.

3 Likes

It had been better if you had answered instead of editing, but OK, lets do it this way.

As far as I can tell you get a list of maps from somwhere and need to send only a single map per call so somewhere else, so a simple proxy from list to many calls with single maps, something along the following might help:

function_that_retrieves_the_list_of_maps
|> Enum.map(&send_a_single_map_to_the_receiver/1)

PS: The link you gave, seems to be dead…

3 Likes

The correct link is https://github.com/AdamBrodzinski/awesome-stack/tree/phoenix-1.2-new-graphql

1 Like

I think you are facing an issue with your design - have a look at query.ex - get queries use handle_get_response (is_map) in the pipeline while the filter queries use handle_get_many_response (is_list) - i.e. by design get queries return at most one result as a single map while filter queries return zero-to-many results (each in their own map) in a list.

Now look at user_query.ex - it uses get and you can’t just drop filter in its place as that would (in this case) break convert_to_string_map:

iex> convert_to_string_map = fn(data) -> for {key, val} <- data, into: %{}, do: {String.to_atom(key), val} end
iex> data = %{"first_name" => "John", "id" => "651029dc-636d-4e54-a03d-d80097c7567e","last_name" => "Doe", "user_id" => "23232323"}
iex> convert_to_string_map.(data)
%{first_name: "John", id: "651029dc-636d-4e54-a03d-d80097c7567e", last_name: "Doe", user_id: "23232323"} 
# That worked!

iex> data = [%{"first_name" => "John", "id" => "651029dc-636d-4e54-a03d-d80097c7567e","last_name" => "Doe", "user_id" => "23232323"},
%{"first_name" => "John", "id" => "41188ffc-4304-4b01-9c45-7565aa8a4aab","last_name" => "Doe", "user_id" => "23232323"}]
iex> convert_to_string_map.(data)
%{} 
# Oops!

iex> data |> Enum.map(convert_to_string_map)
[%{first_name: "John", id: "651029dc-636d-4e54-a03d-d80097c7567e", last_name: "Doe", user_id: "23232323"},
 %{first_name: "John", id: "41188ffc-4304-4b01-9c45-7565aa8a4aab", last_name: "Doe", user_id: "23232323"}]
# That's better

In essence the filter query needs a pipeline after run that can process a list of maps. Now you could wrap the result of a get query inside a list so that it can be processed by the pipeline that was designed for results from a filter query but I suspect that would create problems with the GraphQL response.

3 Likes

Thanks for that response, have a look at the phoenix-1.2-new-graphql branch of the same repo. That is the one I am using. I also feel that I might be missing something fundamental on how to resolve Lists to Maps in each query in GraphQL helpers to ensure compatibility.

If you look at that branch he is using this block of code to handle the ‘transformations’

` default_resolve fn _, %{source: source, definition: %{name: name}} ->
    if is_boolean(Map.get(source, name)) do
      {:ok, Map.get(source, name)}
    else
      {:ok, Map.get(source, name) || Map.get(source, String.to_existing_atom(name))}
    end
  end`

This is where it’s hitting a snag.

Adam Brodzinski, the author of that example stack was kind enough to direct me to the right place. This is his response:

so either you need to use List.first to get one item or change the query from

mutation do
  field :create_user, type: :user do
    arg :email, non_null(:string)
    arg :password, non_null(:string)
    resolve &GraphQL.Resolvers.User.create/2
  end
end

to a list_of:

mutation do
  field :create_user, type: list_of(:user) do
    arg :email, non_null(:string)
    arg :password, non_null(:string)
    resolve &GraphQL.Resolvers.User.create/2
  end
end

more info: http://absinthe-graphql.org/tutorial/query-arguments

So basically using type: list_of(:user) resolved the issue I was having. :thumbsup:

1 Like

Essentially you had an error in your schema:

web/schema.ex

query do
  @desc "Get all blog posts"
  field :posts, list_of(:post) do      # This needs list_of()
     resolve &Blog.PostResolver.all/2  # because this will return 0-to-many results
  end

  @desc "Get a user of the blog"
  field :user, type: :user do          # This doesn't need list_of()
    arg :id, non_null(:id)
    resolve &Blog.PostResolver.find/2  # because this will return 0-or-1 results
  end
end

The missing list_of resulted in the default_resolve being presented with data that it shouldn’t have to handle.
This demonstrates that one should not immediately assume that the fault lies where it manifests itself - the problem is often further up stream.

3 Likes