DataMapperでinner joinする
dm-core-1.2.0の話です。
DataMapperでjoinする話が全然検索に引っかからないので。メモ的に
class User include DataMapper::Resource property :id, Serial property :name, String has 1, :profile, 'UserProfile' end class UserProfile include DataMapper::Resource property :id, Serial property :address, String belongs_to :user end # 関連として定義したUser.profileを条件にわたす # ユーザープロフィールが存在するもののみ取得 User.all(User.profile.id.not => nil) # SELECT "users"."id", "users"."name" # FROM "users" INNER JOIN "user_profiles" ON "users"."id" = "user_profiles"."user_id" # WHERE NOT("user_profiles"."id" IS NULL) # GROUP BY "users"."id", "users"."name" # ORDER BY "users"."id" # ユーザープロフィールで住所が東京から始まるものだけ取得 User.all(User.profile.address.like => '東京%') # SELECT "users"."id", "users"."name" # FROM "users" INNER JOIN "user_profiles" ON "users"."id" = "user_profiles"."user_id" # WHERE "user_profiles"."address" LIKE '東京%' # GROUP BY "users"."id", "users"."name" # ORDER BY "users"."id" # User.profileとUser.profile.addressはDataMapper::Query::Pathオブジェクト pp User.profile.address # #<DataMapper::Query::Path:0x007ff21d040b48 # @model=UserProfile, # @property=nil, # @relationships=nil, # @repository_name=:default>
異なるテーブルをjoinする場合は問題ないが、自分自身をjoinする場合、条件の混同が発生してしまう。
class Category include DataMapper::Resource property :id, Serial property :name, String property :parent_id, Integer belongs_to :parent_category, 'Category', parent_key: [:id], child_key: [:parent_id] end Category.all(Category.parent_category.name => "parent", :name => "child") # SELECT "categories"."id", "categories"."name", "categories"."parent_id" # FROM "categories" INNER JOIN "categories" "categories_1" ON "categories"."parent_id" = "categories_1"."id" # WHERE ("categories"."name" = 'parent' AND "categories"."name" = 'child') # GROUP BY "categories"."id", "categories"."name", "categories"."parent_id" # ORDER BY "categories"."id" # joinしたテーブルの条件とjoinされるテーブルの条件が混同されている # WHERE ("categories"."name" = 'parent' AND "categories"."name" = 'child')
調べてみたけど、解決方法を見つけられなかった。次のようにconditionsを利用して混同を避けたりしてる。
# exists を使ってjoinの代わりとする。 Category.all( name: "child", conditions: [ "exists (select id from categories as C where C.id = categories.id and name = ?)", 'parent' ]) # SELECT "id", "name", "parent_id" # FROM "categories" # WHERE ("name" = 'child' AND (exists (select id from categories as C where C.id = categories.id and name = 'parent'))) # ORDER BY "id"
DBがレガシーで次のように:fieldを指定している場合、その定義を再利用すると良いかもしれない。
class Category property :name, String, field: 'CAT_NAME' end Category.properties[:name].field # => 'CAT_NAME' <<SQL select #{Category.properties[:id].field} from #{Category.storage_name} as C where C.#{Category.properties[:id].field} = categories.#{Category.id} and #{Category.properties[:name].field} = ? SQL # select id # from categories as C # where C.id = categories.id # and CAT_NAME = ?
レガシーは辛い。