9
  render :json => {
    "playlist" => playlist_description,
    "songs" => @playlist.songs.as_json(:include => {:playlist_songs => {:only => [:id, :position]}})
  }

The above code results in 1+N queries to the database, one to load playlist_songs for each song. The playlist is preloaded in @playlist.

This is so slow, how can I optimize?

Johan
  • 1,897
  • 5
  • 23
  • 29

3 Answers3

22

My guess: You're not eager-loading the playlist_songs at the moment. You're currently waiting until the as_json call - after which all the songs have been loaded - and then the code has to iterate over every song and fetch the playlist_songs then.

My guess (this is totally untested and may include bugs)

@playlist.songs.all(:include => :playlist_songs).as_json(:include => {:playlist_songs => {:only => [:id, :position]}})

AFAICT, this should first eager load all the songs and the playlist_songs... and then render as json.

Taryn East
  • 27,486
  • 9
  • 86
  • 108
4

I would highly recommend integrating with a JSON builder such as rabl. It will make your life 10x easier moving forward, and is extremely nice to separate the "view" of the JSON representation. I made the switch a couple months ago and haven't looked back.

Within your controller:

@playlist = Playlist.where(:id => params[:id]).includes(:playlist_songs)

Then the rabl template could be something like this:

object @playlist
attribute :description
child :playlist_songs do
  attributes :id, :position
end
ejlevin1
  • 735
  • 5
  • 15
1
render :json => {
  "playlist" => playlist_description,
  "songs" => @playlist.songs.all.as_json(:include => {:playlist_songs => {:only => [:id, :position]}})
}

^ guess

Reactormonk
  • 21,472
  • 14
  • 74
  • 123