Git Product home page Git Product logo

absinthe_ecto's People

Contributors

0ff avatar avitex avatar benwilson512 avatar bruce avatar jsteiner avatar linjunpop avatar matthewlehner avatar paralax avatar sanderhahn avatar sikanhe avatar tlvenn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

absinthe_ecto's Issues

can not upgrade to Dataloader

hi there ~

i am upgrade absinthe from 1.4 to 1.5 on my project, the absinthe_ecto README says:

image

but when i follow the step, i got error:

image

image

env info:

      elixir 1.10.3
     -----
      {:phoenix, "~> 1.5.7"},
      {:phoenix_pubsub, "~> 2.0"},
      {:phoenix_html, "~> 2.14.3"},
      {:ecto_sql, "~> 3.5.4"},
      {:phoenix_ecto, "~> 4.2.1"},
      {:postgrex, "~> 0.15.8"},
      {:gettext, "~> 0.18.0"},
      {:plug_cowboy, "~> 2.4.1"},
      {:plug, "~> 1.11.0"},
      # GraphQl tool
      {:absinthe, "~> 1.5.5"},
      # {:absinthe_ecto, "~> 0.1.3"},
      {:absinthe_plug, "~> 1.5.4"},
      {:dataloader, "~> 1.0.7"},
     ...

Providing a default order for an association

Let's say all comments should be ordered by inserted_at desc. Will Absinthe.Ecto provide functionality similar to the following?

field :comments, list_of(:comment), resolve: assoc(:comments, order_by: [desc: :inserted_at])

Don't preload if only requesting the "id" field of a belongs_to association

This is a duplicate of absinthe-graphql/dataloader#7. I'm not using dataloader anymore at the moment and figured out it has more of its place here.

Let's say I have a :fruits_basket object that can contains fruits and that can be placed in another basket. Something like:

object :fruits_basket do
  field :id, non_null(:id)
  field :slug, non_null(:string)
  field :fruits, list_of(:delicious_fruit), do: resolve assoc(:fruits)
  field :parent, :fruits_basket, do: resolve assoc(:parent_basket)
end

As of today a request like:

basket(slug: "grandma-basket") {
  id
  fruits {
    name
    color
  }
  parent {
    id # We only ask for parent's id and no other field
  }
}

...would make two queries like:

SELECT (...) FROM fruits_baskets WHERE slug = "grandma-basket" -- Get basket
SELECT (...) FROM fruits_baskets WHERE id = 42 -- Get parent basket

The thing is that second query is not necessary: we already have the parent id in grandma's basket.

I think it is a very common pattern to query only for the id of an association. In this example, the request could be used to show a "Go to parent" link on the frontend.

My real use case is a comment system where comments can reply to each others by using a reply_to field. I would love to see assoc use the reply_to_id field instead of preloading the full reply_to when requesting only the id.

The easiest way to achieve this with actual system is to add a field for the id:

...
field :reply_to_id, :id
field :reply_to :comment, do: resolve assoc(:reply_to)
...

But that doesn't look very neat to me.

README suggestion

Hi,

The README says:

The assoc macro just builds a resolution function which calls ecto_batch/4.

See the ecto_batch/4 function for how to do this from within a regular resolution function.

As a beginner I have a hard time picturing the equivilent ecto_batch code. I know there is documentation on how to use ecto_batch, but I think what would really help a newcomer to this repo is a quick example of how you would implement the specific assoc example in the README if you had to do it with ecto_batch only.

What do you think?

pagination macro in many_to_many ?

hi guys ~

let's say i have a post type with many started_users field witch resolved by assoc macro, it's great , but how to do pagination in this situation ?

object :post do
   field(:id, non_null(:id))
   field(:title, non_null(:string))
   field(:body, non_null(:string))
   field(:author, :author, resolve: assoc(:author))
>  field(:starred_users, list_of(:user), resolve: assoc(:starredUsers))
end

repo isn't on hex

Hi, we broke our build today because the versions changed.
Any reason this repo couldn't be hosted on hex.pm?

Thanks

New query fun addition breaks :through associations

The recent merge of #11 (committed in 29e9921) breaks :through associations.

defmodule Api.Deliverable do
  # ...
  belongs_to :plan, Api.Plan
  has_one :project, through: [:plan, :project]
  # ...

Now when I try to use this in a query:

object :deliverable do
  field :project, :project, resolve: assoc(:project)
end

I get:

** (MatchError) no match of right hand side value: %Ecto.Association.HasThrough{cardinality: :one, field: :project, on_cast: nil, owner: Api.Deliverable, owner_key: :plan_id, relationship: :child, through: [:plan, :project], unique: true}

This is because the %Ecto.Association.HasThrough{} struct doesn't include the queryable key (which the new updates try to match on).

Didn't realize 0.1.0 was pushed to Hex last month so switching to that instead of master is working fine for now. I'll try and get a PR in when I have some time.

Ecto.Schema resolution proposal

Hi, I've been using Absinthe and Absinthe.Relay intensively for a private project and wrote a few helper functions in order to resolve GraphQL queries into Ecto queries.

I've put the code here: https://gist.github.com/redrabbit/be3528a4e4479886acbe648a693e65c0

I don't know if Absinthe.Ecto is going to be implemented in a similar manner. If there is interest, I can write a PR and add some tests to it.

Basically, it uses Ecto.Schema and Absinthe.Resolution to resolve pretty much every kind of GraphQL query I'd came across so far.

There are two distinct functions, build_query/3 and batch_query/5.

By default, build_query/3 automatically resolves :join, :preload and :select clauses. It tries to be as performant as possible and selects only required fields.

Setup

Let's say we have schemas representing a music library:

schema "artists" do
  field :name, :string
  has_many :albums, Album
  has_many :tracks, Track
  timestamps
end

schema "albums" do
  field :title, :string
  field :release_date, Ecto.Date
  belongs_to :artist, Artist
  many_to_many :genres, Genre, join_through: "albums_genres"
  has_many :tracks, Track
  timestamps
end

schema "genres" do
  field :name, :string
  many_to_many :albums, Album, join_through: "albums_genres"
  timestamps
end

schema "tracks" do
  field :title, :string
  field :track_nr, :integer
  field :duration, :integer
  field :popularity, :integer
  belongs_to :artist, Artist
  belongs_to :album, Album
  timestamps
end

And the following matching GraphQL types:

object :artist do
  field :id, :id
  field :name, :string
  field :albums, list_of(:album)
  field :tracks, list_of(:track) do
    arg :top, :integer, default_value: 10
    resolve &resolve_artist_top_tracks/3
  end
end

object :album do
  field :id, :id
  field :title, :string
  field :release_date, :date
  field :artist, :artist
  field :tracks, list_of(:track)
  field :genres, list_of(:genre)
end

object :genre do
  field :id, :id
  field :name, :string
  field :albums, list_of(:album)
end

object :track do
  field :id, :id
  field :title, :string
  field :artist, :artist
  field :album, :album
  field :track_nr, :integer
  field :duration, :integer
  field :popularity, :integer
end

Usage

Following GraphQL query:

{
  artists {
    name
    albums {
      title
      tracks {
        title
      }
      genres {
        name
      }
    }
  }
}

Returns a single Ecto.Query:

#Ecto.Query<from a0 in Artist,
           join: a1 in assoc(a0, :albums),
           join: g in assoc(a1, :genres),
           join: t in assoc(a1, :tracks),
         select: [:id, :name, {:albums, [:id, :title, {:genres, [:id, :name]}, {:tracks, [:id, :title]}]}],
        preload: [albums: {a1, [genres: g, tracks: t]}]>

It handles :belongs_to, :has_one, :has_many and :many_to_many associations and will try to join and preload as much as possible. It also support resolving cyclic graphs.

Writing resolvers is straight forward:

 def resolve_artists(_args, info) do
  query = build_query(Artist, info, &order_by(&1, [asc: :name]))
  {:ok, Repo.all(query)}
end

def resolve_artist_by_id(%{id: id}, info) do
  id = String.to_integer(id)
  query = build_query(Artist, info, &where(&1, [id: ^id]))
  case Repo.one(query) do
    nil ->
      {:error, "Cannot find artist with id #{id}."}
    artist ->
      {:ok, artist}
  end
end

In order to support batching and avoid N+1 queries, batch_query/5 provides similar functionalities and resolve the given associations automatically. For example:

  object :artist do
    field :id, :id
    field :name, :string
    field :albums, list_of(:album)
    field :tracks, list_of(:track) do
      arg :top, :integer, default_value: 10
      resolve fn artist, args, info ->
        batch_query(Track, artist, info, &Repo.all/1, fn query ->
            query
            |> order_by([desc: :popularity])
            |> limit(^min(Map.get(args, :top, 10), 100))
        end)
      end
    end
  end

In the above object, the :albums field is resolved automatically within the query using a :join. The :tracks field will require a 2nd query (using where: [artist_id: id] or where: a1.artist_id in ^ids).

Resulting in executing two SQL queries for the following GraphQL:

{
  artists {
    name
    tracks(top:5) {
      title,
      duration
    }
    albums {
      title
      tracks {
        title
      }
      genres {
        name
      }
    }
  }
}
2016-11-26 02:57:28.093 [debug] QUERY OK source="artists" db=2.1ms
SELECT a0."id", a0."name", a1."id", a1."title", t2."id", t2."title", g3."id", g3."name" FROM "artists" AS a0 INNER JOIN "albums" AS a1 ON a1."artist_id" = a0."id" INNER JOIN "tracks" AS t2 ON t2."album_id" = a1."id" INNER JOIN "albums_genres" AS a4 ON a4."album_id" = a1."id" INNER JOIN "genres" AS g3 ON a4."genre_id" = g3."id" ORDER BY a0."name" []
2016-11-26 02:57:28.095 [debug] QUERY OK source="tracks" db=1.6ms
SELECT t0."artist_id", t0."id", t0."title", t0."duration" FROM "tracks" AS t0 WHERE (t0."artist_id" = $1) ORDER BY t0."popularity" DESC LIMIT $2 [1, 5]

Customization

You can customize your queries using the optional transform parameter both functions provide.

For example, to fetch albums sorted by :release_date and album tracks sorted by :track_nr, one can write two new resolver functions:

def resolve_artist_albums(artist, _args, info) do
  batch_query(Album, artist, info, &order_by(&1, [desc: :release_date]))
end

def resolve_album_tracks(album, _args, info) do
  batch_query(Track, album, info, &order_by(&1, [asc: :track_nr]))
end

And update the schema types like this:

object :artist do
  field :albums, list_of(:album), do: resolve &resolve_artist_albums/3
end

object :album do
  field :tracks, list_of(:track), do: resolve &resolve_album_tracks/3  
end

many_to_many associations

Are there any plans to implement support for many_to_many associations? I would guess implementing this is a little bit harder?

Run tests on CI

Now that there's a test suite, it would be helpful to add some sort of CI to the project to ensure the tests are passing.

Assoc helper, batch id and query fun

With the introduction of the query fun support to tailor the batch query, I am wondering if it shouldnt be used to compose the batch id.

Suppose we have 2 fields which use the same ecto field but with 2 different queries. My understanding is that because they share the same batch id, only one of those 2 queries will run and those 2 fields will end up with the same value.

@benwilson512 what do you think ?

Fetch Single Record

Lets say an event has many attendees (events_users), and we have a unique index so that a user can't attend the same event twice.

Is there a way using absinthe_ecto to have a field on event called e.g. current_user_attendance which checks if the current user attended the event, but instead of returning a list with a single 'attendance', it returns the field directly?

  object :event do
    field :current_user_attendance, list_of(:event_user) do
      resolve assoc(:events_users, fn query, args, %{current_user: current_user} ->
        query
        |> where([eu], eu.user_id == ^current_user.id)
      end)
    end
  end

The above works fine, except it'll return the aforementioned list, when we know be definition there will be either one or zero results.

I appreciate I could write a custom resolver, but it would be very elegant to do it in absinthe_ecto, as when you're fetching many events it very cleanly takes care of n+1 issues.

Please, support this package

Using this package was so awesome to avoid N+1, auto-preload associations, and do not mess with complex Dataloader. Why you deprecated it?

Select only some schema fields

Hi,

Some of my tables have fields that serialize very large values in Postgres hstore or jsonb columns. Will batching with absinthe_ecto always include those columns in queries, and if so, how can I control that? There are many situations where I wouldn't want those columns included in a query unless their respective GraphQL field is included in the query, especially for has_many associations where there could be a lot.

All association calls missing from MSSQL call

SQL call ends up being just the one for the root query type when doing the following:

query{
  profiles{
    firstName
    profileImages{
      url
    }
    logins{
      cellNumber
    }
  }
}


[debug] QUERY OK source="Profile" db=152.8ms decode=0.2ms
SELECT P0."sm_id", P0."first_name" FROM "Profile" AS P0 []
[info] Sent 200 in 156ms

The graphiql query is then of course missing the lists

...
     {
        "profileImages": null,
        "logins": null,
        "firstName": "Thomas"
      },
      {
        "profileImages": null,
        "logins": null,
        "firstName": "Jonathan"
      },
...

graphql types are set up just like examples
and ecto models as well
Could there be an incompatibility with the mssql-ecto driver ?

dependency absinthe version

Failed to use "absinthe" because
  mix.exs specifies ~> 1.3.1

mix.exs specifies ~> 1.3.1 to mix.exs specifies ~> 1.3.0 run

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.