In SherlockDNA I have a few models that are mostly represented by a JSON-backed column. I don’t want to deserialize the entire JSON payload every time that row is called — there are tens of millions of rows accessed every day on this little side project, and that’s a lot of CPU cycles. I make use of SQL aliases to pull the data that I went.
In one controller action, I have this:
@matches = @leed.leed_matches.select("data->'test_guid' as test_guid, data->'public_display_name' as name, last_synced_at")
Fine, whatever. I have data->'test_guid' as test_guid
a lot though and heaven forbid something changes in the data
attribute I am left scrambling!
What I wanted was this:
@matches = @leed.leed_matches.with_test_guid.with_name.and_select("last_synced_at")
Or something.
Slight issue with ActiveRecord::QueryMethods
This:
Post.select(:id).select(:name)
Only gets name
and id
is left in the dark.
So we need and_select
, or something.
module ActiveRecord
module QueryMethods
def and_select(*fields)
if(!select_values.any?)
fields.unshift(arel_table[Arel.star])
end
select(*fields)
end
end
end
The ActiveModel DSL
module ColumnAliases
extend ActiveSupport::Concern
included do
class_attribute :column_aliases, default: {}
end
class_methods do
def column_alias(name, sql)
self.column_aliases[name] = Arel.sql(sql).as(name.to_s)
scope "with_#{name}".to_sym, -> { and_select(column_aliases[name]) }
end
end
end
Usage
class Leed::Match < ApplicationRecord
include ColumnAliases
column_alias :test_guid, "data->'test_guid'"
column_alias :name, "data->'public_display_name'"
end
Alas,
Leed::Match.with_test_guid.with_name.and_select(:last_synced_at)
Improvement
My colleague and I were talking about how this could be improved. They mentioned we probably really want something like:
Leed::Match.select(:test_guid, :name, :last_synced_at)
Which, we kind of can get, but not for things backed by SQL (at this time):
class Post < ApplicationRecord
alias_attribute :subject, :title
end
Post.where(subject: "bob")
and Post.last.subject
are both respectable.
I made a post on the mailing list: https://discuss.rubyonrails.org/t/support-for-alias-attribute-with-sql-aliasing/81592