Better way to manage postgresql JSONB data in absinthe-graphql

hi everyone so far so good with graphql and Absinthe. Its getting bit tricky with some data structs
here is the example of schema

     object :uploads do  
           field :links, list_of(:links_types)
     end

the links_types looks like

     object :links_types do
          field  :url,  :string
          field  :format, :string
      end    

so the data structure that is fetched from postgresql looks this
%{id:1, links: %{data: [ %{"url" => "xyz.com", "format" => "format1" }, %{ "url": "abc.com", format => "format2"]}}

i managed to remove data key from links map , so now links: is list of maps as we described in graphql schema still i get null result

sample query

          id,
           links{
             url
            }
       }

result is

        id:1,
        links: [ {url: null}, {url: null}]
  }

I think i am making some mistake here or there is something better way to define schema. thanks in advance

1 Like

Hey there!

Can you elaborate a bit on your data? Normally if you use a jsonb column the keys are strings not atoms. I’m also confused by the single quotes, are you intentionally storing char lists instead of strings?

You’ll likely just need a small inline resolution function on your links field, but in order to suggest one I need to understand the shape of your data better.

1 Like

@benwilson512 thanks for quick reply. I think the problem is my data structure was map with arrow
x = %{"a" => "xyz"}
I thing in the absinthe library we access map as x.a which gave null i think. idn i maybe wrong.

i was thinking to convert arrow map into atoms is there better way to solve this. like some config to access with the arrow maps as well? x["a"]

1 Like

finally solved with own helper
```

Enum.map(list, fn(x)->
   for {key, val} <- x, into: %{}, do: {String.to_existing_atom(key), val}
end)
```

thanks. still wondering is there better solution :slight_smile:

1 Like

First off, some elixir terminology. All elixir maps are “arrow maps”. %{foo: 1} is exactly the same as %{:foo => 1}. If the key is an atom you can write it in the %{foo: 1} form, but it’s just a visual change.

%{:foo => 1} is a map with the atom key :foo.
%{"foo" => 1} is a map with the string key "foo".

What you’re trying to figure out is how to get values out of a map with string keys. The answer does not need to be converting them to atoms.

You haven’t really supplied enough of your code for me to tell you exactly where to put stuff. For example you just said you created a helper, but you didn’t say where you put that code or what values you gave it (what is list?) so it doesn’t help us. Help us to help you, give us the full context.

The basic way to use string keys is as follows:

query do
  field :thing, :thing_with_string_keys do
    {:ok, %{"name" => "ben", "age" => 25}}
  end
end

object :thing_with_string_keys do
  field :name, :string, resolve: key("name")
  field :age, :integer, resolve: key("name")
end

def key(key_name) do
  fn thing, _, _ ->
    {:ok, Map.get(thing, key_name}
  end
end
4 Likes

As a final note, please don’t try to split the conversation. It makes it harder to help you.

1 Like

thanks got it and sorry :slight_smile: cheers finally able to work this out. Thanks again

1 Like

Since you are looking for a general idea, here’s how I tackled a similar problem. I like Ben’s solution with the helper.

My data structure is different, and Ben’s solution is better, but it may help to see nonetheless.

I have a jsonb column with permissions, e.g.

{"add":[], "roles": ["employee", "manager"], "exclude":[]}

…

  object :permissions do
   field :add, list_of(:string) do
     resolve(fn(%{"add" => add},_,_) ->
       {:ok, add}
     end)
   end
   field :exclude, list_of(:string) do
     resolve(fn(%{"exclude" => exclude },_,_) ->
       {:ok, exclude}
     end)
   end
   field :roles, list_of(:string) do
     resolve(fn(%{"roles" => roles},_,_) ->
       {:ok, roles}
     end)
   end
  end
1 Like