Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Presenting a collection with an entity fetches each table twice (2 SQL statements) #71

Open
monfresh opened this issue May 5, 2014 · 1 comment
Labels

Comments

@monfresh
Copy link

monfresh commented May 5, 2014

I have a Location model that has_one :address. The data is stored in Postgres. Here's the Grape API endpoint definition for fetching all locations:

resource "locations" do
  # GET /locations
  desc 'Returns all locations, 30 per page by default'
  params do
    optional :page, type: Integer, default: 1
    optional :per_page, type: Integer, default: 30
  end
  get do
    locations = Location.includes(:address).
                        page(params[:page]).per(params[:per_page])
    present locations, with: Entities::Location
  end
end

My (simplified) Location Entities:

module Entities
  class Location < Grape::Entity
    expose :address, using: Address::Entity
    expose :name
  end
end

The Address::Entity is defined within the Address model:

include Grape::Entity::DSL
entity do
  expose :id
  expose :street
  expose :city
  expose :state
  expose :zip
end

When I visit http://localhost:8080/api/locations, I get the following output in the log:

Location Load (0.6ms)  SELECT "locations".* FROM "locations" ORDER BY "locations"."id" ASC LIMIT 1 OFFSET 0
Address Load (0.5ms)  SELECT "addresses".* FROM "addresses" WHERE "addresses"."location_id" IN (2)
Location Load (6.0ms)  SELECT "locations".* FROM "locations" LIMIT 30 OFFSET 0
Address Load (0.8ms)  SELECT "addresses".* FROM "addresses" WHERE "addresses"."location_id" IN (2, 17, 41, 138, 321, 54, 61, 325, 1146, 531, 139, 326, 965, 72, 238, 140, 228, 330, 1631, 271, 241, 331, 335, 6, 1460, 336, 75, 90, 101, 818)

Note that there are two calls to each table: one to fetch the first record in the results, and then another to fetch all the results (up to the per_page amount).

If I change the endpoint definition to just return locations without any grape-entity representation, and instead, use my own as_json method in the Locations model, like this:

def as_json(options={})
  {
    name: name,
    address: address
  }
end

then, I only end up with one call to each table, as opposed to the two that grape-entity made:

Location Load (6.7ms)  SELECT "locations".* FROM "locations" LIMIT 30 OFFSET 0
Address Load (4.4ms)  SELECT "addresses".* FROM "addresses" WHERE "addresses"."location_id" IN (2, 17, 41, 138, 321, 54, 61, 325, 1146, 531, 139, 326, 965, 72, 238, 140, 228, 330, 1631, 271, 241, 331, 335, 6, 1460, 336, 75, 90, 101, 818)

This is reproducible every time I switch back and forth between the two methods of representing the JSON. It also happens if the JSON doesn't include data from other tables, and even when you're just calling Location.all (not that you would do that). It always makes 2 calls to the Locations table.

In the actual app, I need to include 8 other tables, so having an extra call to each one adds up!

Has anyone noticed this before? Is there a way to write a spec for this?

@dblock dblock added the bug? label May 7, 2014
@dspaeth-faber
Copy link
Contributor

@monfresh & @dblock You'r using a scope as parameter for present. That's the issue. It arises within grape. To get the Class of you'r Collection Instance Grape is looking at the first element. With rails this is an issue. You can walk around this by explicitly setting the Entity class or you explicitly use .load

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants