Rails使用别名查询联接关联表



我有一个型号Edge,它通过不同的外键两次属于另一型号Node

def Edge < ActiveRecord::Base
    belongs_to :first, class_name: 'Node'
    belongs_to :second, class_name: 'Node'
end

我想使用ActiveRecord:执行这个查询

SELECT * FROM edges INNER JOIN nodes as first ON first.id = edges.first_id WHERE first.value = 5

我找到了使用.joins()方法加入协会的方法:

Edge.joins(:first)

但这会使用表名而不是关联名生成查询,所以在.where()方法中,我必须显式使用表名,这打破了关联抽象。

Edge.joins(:first).where(nodes: {value: 5})

我还可以在.joins()方法中显式地使用SQL查询来定义模型别名:

Edge.joins('INNER JOIN nodes as first ON nodes.id = edges.first_id')

但这打破了更多的抽象。

我认为应该有一种方法可以在联接时自动定义表别名。或者可能是一种自己编写函数的方法。类似于:

def Edge < ActiveRecord::Base
    ...
    def self.joins_alias
        # Generate something like 
        # joins("INNER JOIN #{relation.table} as #{relation.alias} ON #{relation.alias}.#{relation.primary_key} = #{table}.#{relation.foreign_key}")
    end
end

但我找不到任何关于访问特定关系信息的信息,比如它的名称、外键等。那么我该怎么做呢?

而且,我觉得奇怪的是,即使Rails已经是第四个主要版本了,如此明显的功能仍然如此复杂。也许我错过了什么?

对于Rails 4.2.1,我认为在使用ActiveRecord中的joins时无法提供别名。

如果你想通过第一个节点查询边缘,你可以像你说的那样:

Edge.joins(:first).where(nodes: {value: 1})
SELECT "edges".* FROM "edges" INNER JOIN "nodes" ON "nodes"."id" = "edges"."first_id" WHERE "nodes"."value" = 1

但是,如果必须同时使用两个节点进行查询,则仍然可以使用joins,如下所示:

Edge.joins(:first, :second).where(nodes: {value: 1}, seconds_edges: {value: 2})
SELECT "edges".* FROM "edges" INNER JOIN "nodes" ON "nodes"."id" = "edges"."first_id" INNER JOIN "nodes" "seconds_edges" ON "seconds_edges"."id" = "edges"."second_id" WHERE "nodes"."value" = 1 AND "seconds_edges"."value" = 2

当然,您可以为关系使用表别名(可能是rails 5之后的别名),有些别名如下:

def Edge < ActiveRecord::Base
    ...
    def self.joins_alias
        # Generate something like 
       join_name = table.table_alias || table.name
       table_to_join = ... # table name to join
       alias_to_join = ... # table alias to join
       joins("INNER JOIN #{table_to_join} as #{alias_to_join} ON #{alias_to_join}.external_id = #{join_name}.id")
    end
end

最新更新