diff --git a/spec/avram/json_criteria_spec.cr b/spec/avram/json_criteria_spec.cr index 5e8b09cb4..b8fca7cbf 100644 --- a/spec/avram/json_criteria_spec.cr +++ b/spec/avram/json_criteria_spec.cr @@ -38,6 +38,34 @@ describe JSON::Any::Lucky::Criteria do preferences.not.has_all_keys(["theme", "style"]).to_sql.should eq ["SELECT #{QueryMe::COLUMN_SQL} FROM users WHERE NOT(users.preferences ?& $1)", ["theme", "style"]] end end + + describe "includes" do + it "@>" do + json = JSON::Any.new({"theme" => JSON::Any.new("dark")}) + critera = preferences.includes(json) + critera.to_sql.should eq ["SELECT #{QueryMe::COLUMN_SQL} FROM users WHERE users.preferences @> $1", "{\"theme\":\"dark\"}"] + end + + it "negates with NOT()" do + json = JSON::Any.new({"theme" => JSON::Any.new("dark")}) + critera = preferences.not.includes(json) + critera.to_sql.should eq ["SELECT #{QueryMe::COLUMN_SQL} FROM users WHERE NOT(users.preferences @> $1)", "{\"theme\":\"dark\"}"] + end + end + + describe "in" do + it "<@" do + json = JSON::Any.new({"theme" => JSON::Any.new("dark"), "style" => JSON::Any.new("cyberpunk"), "version" => JSON::Any.new(2i64)}) + critera = preferences.in(json) + critera.to_sql.should eq ["SELECT #{QueryMe::COLUMN_SQL} FROM users WHERE users.preferences <@ $1", "{\"theme\":\"dark\",\"style\":\"cyberpunk\",\"version\":2}"] + end + + it "negates with NOT()" do + json = JSON::Any.new({"theme" => JSON::Any.new("dark"), "style" => JSON::Any.new("cyberpunk"), "version" => JSON::Any.new(2i64)}) + critera = preferences.not.in(json) + critera.to_sql.should eq ["SELECT #{QueryMe::COLUMN_SQL} FROM users WHERE NOT(users.preferences <@ $1)", "{\"theme\":\"dark\",\"style\":\"cyberpunk\",\"version\":2}"] + end + end end private def preferences diff --git a/src/avram/charms/json_extensions.cr b/src/avram/charms/json_extensions.cr index d260ecbb3..3f676b1f9 100644 --- a/src/avram/charms/json_extensions.cr +++ b/src/avram/charms/json_extensions.cr @@ -90,6 +90,15 @@ struct JSON::Any def has_all_keys(keys : Array(String)) : T add_clause(Avram::Where::JSONHasAllKeys.new(column, keys)) end + + # performs `WHERE jsonb @> other_json` + def includes(other_json : JSON::Any) + add_clause(Avram::Where::JsonbIncludes.new(column, other_json.to_json)) + end + + def in(other_json : JSON::Any) + add_clause(Avram::Where::JsonbIn.new(column, other_json.to_json)) + end end end end diff --git a/src/avram/where.cr b/src/avram/where.cr index 2c121d7c6..9a32f78dd 100644 --- a/src/avram/where.cr +++ b/src/avram/where.cr @@ -317,6 +317,54 @@ module Avram::Where end end + class JsonbIncludes < ValueHoldingSqlClause + def operator : String + "@>" + end + + def negated : JsonbExcludes + JsonbExcludes.new(column, value) + end + end + + class JsonbExcludes < ValueHoldingSqlClause + def operator : String + "@>" + end + + def negated : JsonbIncludes + JsonbIncludes.new(column, value) + end + + def prepare(placeholder_supplier : Proc(String)) : String + "NOT(#{column} #{operator} #{placeholder_supplier.call})" + end + end + + class JsonbIn < ValueHoldingSqlClause + def operator : String + "<@" + end + + def negated : JsonbNotIn + JsonbNotIn.new(column, value) + end + end + + class JsonbNotIn < ValueHoldingSqlClause + def operator : String + "<@" + end + + def negated : JsonbIn + JsonbIn.new(column, value) + end + + def prepare(placeholder_supplier : Proc(String)) : String + "NOT(#{column} #{operator} #{placeholder_supplier.call})" + end + end + class Raw < Condition @clause : String