DataMapperでinに空の配列が渡された時になんとか空のコレクションを返す
dm-core-1.2.0の話です。
DataMapperはinの条件に配列を渡すと次のように良い感じで検索してくれる。
class User include DataMapper::Resource property :id, Serial property :name, String end User.all(id: [1, 2, 3]) # SELECT "id", "name" # FROM "users" # WHERE "id" IN (1, 2, 3) # ORDER BY "id" User.all(id: []) # => [] # 不正なクエリのためDataMapperがSQLを発行せず[]を返す User.all(id: []).query.conditions.valid? # => false
しかし、調子に乗ると、、、
しかし、調子に乗ってDataMapper::Collection.union(|)を利用すると、次の用にクエリが壊れてしまう。
User.all(id: [1, 2]) # SELECT "id", "name" # FROM "users" # WHERE "id" IN (1, 2) # ORDER BY "id" User.all(id: []).all(name: 'hoge') # => [] User.all(id: []).all(name: 'hoge').query.conditions.valid? # => false (User.all(id: [1, 2]) | User.all(id: []).all(name: 'hoge')) # SELECT "id", "name" # FROM "users" # WHERE ("id" IN (1, 2) OR "name" = 'hoge') # ORDER BY "id" # orの条件がおかしい。 # 期待するSQLは次のはずなのに、、 # SELECT "id", "name" # FROM "users" # WHERE "id" IN (1, 2) # ORDER BY "id (User.all(id: [1, 2]) & User.all(id: []).all(name: 'hoge')) # => [] (User.all(id: [1, 2]) & User.all(id: []).all(name: 'hoge')).query.conditions.valid? # => false # & だとこの不具合は発生しない。。。
ちょっとやんちゃすると壊れてしまうのは困る。
回避策
この回避方法はやっつけ仕事である。 空の配列が渡された時にまずマッチしないであろう値['NOT_EXISTS']に書き換えて回避する。
module DataMapper class Query module Conditions class InclusionComparison < AbstractComparison # inの検索のときに空の配列を渡された場合、現実的にマッチしない条件に置き換える def initialize(subject, value) @subject = subject @loaded_value = typecast(value) if @loaded_value.is_a?(Enumerable) and @loaded_value.empty? @loaded_value = typecast(['NOT_EXISTS']) end @dumped_value = dump end end end end end (User.all(id: [1, 2]) | User.all(id: []).all(name: 'hoge')) # SELECT "id", "name" # FROM "users" # WHERE ("id" IN (1, 2) OR ("id" IN ('NOT_EXISTS') AND "name" = 'hoge')) # ORDER BY "id"
とりあえず、意図した結果が取得できるSQLになった。
まとめ
だれか、もっと良い解決方法教えてください、、、