How to pluck aliased column from joined tables in rom-sql?

513 Views Asked by At

I cannot find how to pluck aliased column using rom-sql.

Given relations users and avatars (user has many avatars).

# works
users.join(:avatars).select { |r| r.avatars[:id] }.pluck(:id)

# does not work
users.join(:avatars).select { |r| r.avatars[:id].as(:avatar_id) }.pluck(:avatar_id)
# NoMethodError: undefined method `[]' for nil:NilClass
# from /usr/local/bundle/gems/rom-core-4.2.1/lib/rom/schema.rb:195:in `[]'

# more examples
users.join(:avatars).select{|r| r.avatars[:id].as('avatar_id')}                                           
#=> #<Persistence::Relations::Users name=ROM::Relation::Name(users) dataset=#<Sequel::Postgres::Dataset: "SELECT \"avatars\".\"id\" AS \"avatar_id\" FROM \"users\" INNER JOIN \"avatars\" ON (\"users\".\"id\" = \"avatars\".\"user_id\") ORDER BY \"users\".\"id\"">>

users.join(:avatars).select{|r| r.avatars[:id].as('avatar_id')}.to_a
#> Dry::Struct::Error: [Entities::User.new] :avatar_id is missing in Hash input

users.join(:avatars).select{|r| r.avatars[:id].as('avatar_id')}.with(auto_struct: false).to_a
#=> [{:avatar_id=>88}, {:avatar_id=>89}]
1

There are 1 best solutions below

0
On

It's impossible to suggest any solution if the version of the used libraries is not specified. For rom-sql 2.4.0 it just works:

#!/usr/bin/env ruby

# frozen_string_literal: true

require 'bundler/inline'

gemfile(false) do
  source 'https://rubygems.org'

  gem 'sqlite3'
  gem 'rom-sql', '2.4.0'
  gem 'rspec'
end

require 'rspec'
require 'rspec/autorun'

require 'dry-types'
require 'rom-sql'
require 'sqlite3'

RSpec.describe do
  let(:uri) { 'sqlite::memory' }
  let(:conn) { Sequel.connect(uri) }
  let(:conf) { ROM::Configuration.new(:sql, conn) }
  let(:container) { ROM.container(conf) }

  let(:avatars) { container.relations[:avatars] }
  let(:users) { container.relations[:users] }

  before do
    conn.create_table(:avatars) do
      primary_key :id
      Integer :user_id
      String :url
    end

    conn.create_table(:users) { primary_key :id }

    conf.relation(:avatars) do
      schema(infer: true) do
        associations do
          belongs_to :user
        end
      end
    end

    conf.relation(:users) do
      schema(infer: true) do
        associations do
          has_many :avatars
        end
      end
    end
  end

  subject(:relation) do
    users.join(:avatars).select { relations[:avatars][:url].as(:avatar_url) }
  end

  it do
    users.insert
    avatars.insert(user_id: 1, url: 'avatars/1')
    avatars.insert(user_id: 1, url: 'avatars/2')

    expect { relation.pluck(:avatar_url) }.not_to raise_error
    expect(relation.pluck(:avatar_url)).to eq ['avatars/1', 'avatars/2']
  end
end