Reputation: 11469
I'm trying to create a json array (string actually) based on my db structure. I have the following relationship:
Country > State > City
The way I'm doing it now is very innefficient (N+1):
data = "[" + Country.all.map{ |country|
{
name: country.name,
states: country.states_data
}.to_json
}.join(",") + "]"
Then on the Country model:
def states_data
ret_states = []
states.all.each do |state|
ret_states.push name: state.name, cities: state.cities_data
end
ret_states
end
Then on the State model:
def cities_data
ret_cities = []
cities.all.each do |city|
ret_cities.push name: city.name, population: city.population
end
ret_cities
end
How can I do this more efficiently?
Upvotes: 0
Views: 145
Reputation: 6574
u can provide the model to be included when converting to json.
country.to_json(:include => {:states => {:include => :cities}})
check http://apidock.com/rails/ActiveRecord/Serialization/to_json for
Upvotes: 1
Reputation: 12643
Eager load the states and cities. Just be careful because this could take up a lot of memory for large datasets. See documentation here http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations. Whenever possible I like using joins in addition to includes to fetch all data at once.
#to_json will also serialize Arrays for you, so you don't need to manually add bits of JSON.
Your code from above could be altered like so:
data = Country.joins(:states => :cities).includes(:states => :cities).all.map{ |country|
{
name: country.name,
states: country.states_data
}
}.to_json
But you could also remove the need for the _data methods.
data = Country.joins(:states => :cities).includes(:states => :cities).to_json(
:only => :name,
:include => {
:states => {
:only => :name,
:include => {
:cities => {
:only => [:name, :population]
}
}
}
}
)
That is pretty ugly, so you may want to look into overriding #as_json for each of your models. There is a lot of information about that available on the web.
Upvotes: 1