web-dev-qa-db-ja.com

Railsエイリアスを使用した結合関連付けテーブルのクエリ

異なる外部キーを介して他のモデルEdgeに2回属するモデル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はすでに4番目のメジャーバージョンになっていますが、このような明らかな機能が非常に複雑であるのは奇妙に思えます。何かが足りないのではないでしょうか。

19

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
8
RPinel