0

I'm developing an API that deals with localities. I have a table that has been build as a self-referential and contains a parent_id column. I have so far 144 records in that table.

Our client app needs to receive nested localities. Since I'm using Rails, I was trying at first to use the power of serializers to do it for me:

class LocalitySerializer < ActiveModel::Serializer
  attributes :id, :name, :postcode, :kind, :latitude, :longitude, :parent_id

  has_many :childrens
end

While it does works, it does not seems to be efficient. First, the log shows massive SQL queries to the database. Of course I've tried to eager load data using something like Locality.includes(:childrens).all but it did not works and has no effect (maybe I did something wrong?)

I ended up writing a recursive function that takes the full list of localities (Locality.all) and check each element's parent_id to nest them. So I removed the association in LocalitySerializer:

class LocalitySerializer < ActiveModel::Serializer
  attributes :id, :name, :postcode, :kind, :latitude, :longitude, :parent_id
end

and build it separately within it's own serializer:

class DictionarySerializer < ActiveModel::Serializer
  def attributes
    hash = super
    hash[:localities] = localities
    hash
  end

  private

  def localities
    Rails.cache.fetch('localities.json', expires_in: 7.days) do
      localities = ActiveModel::ArraySerializer.new(Locality.all, each_serializer: LocalitySerializer).as_json
      build_hierarchy(localities, [], nil)
    end
  end

  def build_hierarchy(source, target_array, n)
    source.select { |h| h[:parent_id] == n }.each do |h|
      h.except!(:parent_id)
      target_array << h.clone.merge(childrens: build_hierarchy(source, [], h[:id]))
    end

    target_array
  end
end

While this is a bit faster, it is still very slow IMHO. Note that I set a low-level cache here to make it faster on subsequent request. I haven't actually made any benchmark (just base my word as end user perception), but I'll be happy to do it if it could help anyone to figure out a faster way to generate it.

lkartono
  • 2,323
  • 4
  • 29
  • 47
  • `has_many :children` - *children* is the proper plural of child - not `childrens`. While this is not the root of your problem using the wrong inflection can mess things up down the road. – max Mar 22 '17 at 13:35
  • @max Thanks for the heads up and sorry for this typo (I'm french). child => children ;) – lkartono Mar 22 '17 at 14:04

0 Answers0