diff --git a/spec/avram/array_column_spec.cr b/spec/avram/array_column_spec.cr index c5a80d42c..694ce0f10 100644 --- a/spec/avram/array_column_spec.cr +++ b/spec/avram/array_column_spec.cr @@ -46,4 +46,35 @@ describe "Array Columns" do bucket = SaveBucket.update!(bucket, enums: [Bucket::Size::Small]) bucket.enums.should eq([Bucket::Size::Small]) end + + describe "the #have_any method" do + it "returns the records with have at least one element of the provided ones" do + BucketFactory.new.numbers([1, 2]).create + BucketFactory.new.numbers([1, 3]).create + + bucket = BucketQuery.new.numbers.have_any([1, 2, 3]).select_count + bucket.should eq 2 + + bucket = BucketQuery.new.numbers.have_any([1, 3]).select_count + bucket.should eq 2 + + bucket = BucketQuery.new.numbers.have_any([3, 4]).select_count + bucket.should eq 1 + + bucket = BucketQuery.new.numbers.have_any([4]).select_count + bucket.should eq 0 + end + + it "returns nothing with an empty array" do + BucketFactory.new.numbers([1, 2]).create + bucket = BucketQuery.new.numbers.have_any([] of Int64).select_count + bucket.should eq 0 + end + + it "negates with not" do + BucketFactory.new.numbers([1, 2]).create + bucket = BucketQuery.new.numbers.not.have_any([3]).select_count + bucket.should eq 1 + end + end end diff --git a/spec/avram/where_spec.cr b/spec/avram/where_spec.cr index a02740c4a..98956d166 100644 --- a/spec/avram/where_spec.cr +++ b/spec/avram/where_spec.cr @@ -28,5 +28,7 @@ describe Avram::Where do should_negate(Avram::Where::In, Avram::Where::NotIn) should_negate(Avram::Where::NotIn, Avram::Where::In) should_negate(Avram::Where::Null, Avram::Where::NotNull) + should_negate(Avram::Where::Any, Avram::Where::NotAny) + should_negate(Avram::Where::NotAny, Avram::Where::Any) end end diff --git a/src/avram/criteria.cr b/src/avram/criteria.cr index 0155b8353..98fd6b069 100644 --- a/src/avram/criteria.cr +++ b/src/avram/criteria.cr @@ -154,6 +154,11 @@ class Avram::Criteria(T, V) add_clause(Avram::Where::In.new(column, values)) end + def have_any(values) : T + values = values.map { |value| V.adapter.to_db!(value) } + add_clause(Avram::Where::Any.new(column, values)) + end + # :nodoc: def private_distinct_on : T rows.tap &.query.distinct_on(column) diff --git a/src/avram/where.cr b/src/avram/where.cr index dafcbb9f1..c64b92090 100644 --- a/src/avram/where.cr +++ b/src/avram/where.cr @@ -217,6 +217,34 @@ module Avram::Where end end + class Any < ValueHoldingSqlClause + def operator : String + "&&" + end + + def negated : NotAny + NotAny.new(column, value) + end + + def prepare(placeholder_supplier : Proc(String)) : String + "#{column} #{operator} (#{placeholder_supplier.call})" + end + end + + class NotAny < ValueHoldingSqlClause + def operator : String + "&&" + end + + def negated : Any + Any.new(column, value) + end + + def prepare(placeholder_supplier : Proc(String)) : String + "NOT(#{column} #{operator} (#{placeholder_supplier.call}))" + end + end + class Includes < ValueHoldingSqlClause def operator : String "= ANY"