spec/lib/zermelo/associations/multiple_spec.rb in zermelo-1.2.1 vs spec/lib/zermelo/associations/multiple_spec.rb in zermelo-1.3.0

- old
+ new

@@ -18,27 +18,27 @@ child = child_class.new(:id => '3', :important => true) expect(child.save).to be_truthy children = parent.children.all - expect(children).to be_an(Array) + expect(children).to be_a(Set) expect(children).to be_empty parent.children << child children = parent.children.all - expect(children).to be_an(Array) + expect(children).to be_a(Set) expect(children.size).to eq(1) end it "loads a child from a parent's has_many relationship" do create_child(parent, :id => '3') children = parent.children.all - expect(children).to be_an(Array) + expect(children).to be_a(Set) expect(children.size).to eq(1) child = children.first expect(child).to be_a(child_class) expect(child.id).to eq('3') end @@ -59,32 +59,32 @@ expect(parent.children.count).to eq(2) child = child_class.find_by_id('3') parent.children.remove(child) expect(parent.children.count).to eq(1) - expect(parent.children.ids).to eq(['4']) + expect(parent.children.ids).to eq(Set.new(['4'])) end it "deletes a record from the set by id" do create_child(parent, :id => '3') create_child(parent, :id => '4') expect(parent.children.count).to eq(2) parent.children.remove_ids('3') expect(parent.children.count).to eq(1) - expect(parent.children.ids).to eq(['4']) + expect(parent.children.ids).to eq(Set.new(['4'])) end it "clears all records from the set" do create_child(parent, :id => '3') create_child(parent, :id => '4') expect(parent.children.count).to eq(2) child = child_class.find_by_id('3') parent.children.clear expect(parent.children.count).to eq(0) - expect(parent.children.ids).to eq([]) + expect(parent.children.ids).to eq(Set.new) end it "does not add a child if the before_add callback raises an exception" # do # create_child(nil, :id => '6', :important => true) # child = child_class.find_by_id('6') @@ -123,29 +123,29 @@ end it "by indexed attribute values" do important_kids = parent.children.intersect(:important => true).all expect(important_kids).not_to be_nil - expect(important_kids).to be_an(Array) + expect(important_kids).to be_a(Set) expect(important_kids.size).to eq(2) - expect(important_kids.map(&:id)).to match_array(['3', '4']) + expect(important_kids.map(&:id)).to eq(['3', '4']) end it "by intersecting ids" do important_kids = parent.children.intersect(:important => true, :id => ['4', '5']).all expect(important_kids).not_to be_nil - expect(important_kids).to be_an(Array) + expect(important_kids).to be_a(Set) expect(important_kids.size).to eq(1) expect(important_kids.map(&:id)).to match_array(['4']) end it "applies chained intersect and union filters to a has_many association" do create_child(parent, :id => '3', :important => true) create_child(parent, :id => '4', :important => false) result = parent.children.intersect(:important => true).union(:id => '4').all - expect(result).to be_an(Array) + expect(result).to be_a(Set) expect(result.size).to eq(2) expect(result.map(&:id)).to eq(['3', '4']) end it "checks whether a record id exists through a has_many filter" do @@ -170,20 +170,41 @@ assoc_ids = parent_class.intersect(:id => [ '8', '9', '10']). associated_ids_for(:children) expect(assoc_ids).to eq('8' => Set.new(['3', '4', '5']), '9' => Set.new(['6']), - '10' => Set.new()) + '10' => Set.new) assoc_parent_ids = child_class.intersect(:id => ['3', '4', '5', '6']). associated_ids_for(:parent) expect(assoc_parent_ids).to eq('3' => '8', '4' => '8', '5' => '8', '6' => '9') end + it 'returns associations for multiple parent ids' do + create_parent(:id => '9') + parent_9 = parent_class.find_by_id('9') + + create_child(parent_9, :id => '6', :important => false) + + create_parent(:id => '10') + + assocs = parent_class.intersect(:id => [ '8', '9', '10']). + associations_for(:children) + expect(assocs).to be_a(Hash) + expect(assocs.keys).to match_array(['8', '9', '10']) + expect(assocs.values.all? {|r| r.is_a?(Zermelo::Associations::Multiple)}).to be true + + expect(assocs['8'].count).to eq(3) + expect(assocs['8'].ids).to eq(Set.new(['3', '4', '5'])) + expect(assocs['9'].count).to eq(1) + expect(assocs['9'].ids).to eq(Set.new(['6'])) + expect(assocs['10'].count).to eq(0) + expect(assocs['10'].ids).to eq(Set.new) + end end end context 'redis', :redis => true, :has_many => true do @@ -308,10 +329,110 @@ expect(redis.keys).to match_array(["#{ck}::attrs:ids", "#{ck}::indices:by_important:boolean:true", "#{ck}:6:attrs"]) end + it 'queries using association objects' do + create_parent(:id => '8') + parent_8 = parent_class.find_by_id('8') + create_child(parent_8, :id => '5') + create_child(parent_8, :id => '6') + + create_parent(:id => '9') + parent_9 = parent_class.find_by_id('9') + create_child(parent_9, :id => '7') + + create_parent(:id => '10') + + assocs = parent_class.intersect(:id => ['8', '10']). + associations_for(:children).values + + children = child_class.intersect(:id => assocs) + expect(children.count).to eq(2) + expect(children.ids).to eq(Set.new(['5', '6'])) + end + + it 'queries using multiple association objects' do + create_parent(:id => '8') + parent_8 = parent_class.find_by_id('8') + create_child(parent_8, :id => '5') + create_child(parent_8, :id => '6') + + create_parent(:id => '9') + parent_9 = parent_class.find_by_id('9') + create_child(parent_9, :id => '7') + + create_parent(:id => '10') + parent_10 = parent_class.find_by_id('10') + create_child(parent_10, :id => '4') + + children = child_class.intersect(:id => [parent_8.children, parent_9.children]) + expect(children.count).to eq(3) + expect(children.ids).to eq(Set.new(['5', '6', '7'])) + end + + it 'queries using a single filter object' do + create_parent(:id => '8') + parent_8 = parent_class.find_by_id('8') + create_child(parent_8, :id => '5') + create_child(parent_8, :id => '6') + + create_parent(:id => '9') + parent_9 = parent_class.find_by_id('9') + create_child(parent_9, :id => '7') + + create_parent(:id => '10') + + par = parent_class.intersect(:id => ['8', '10']) + + parent_ids = parent_class.intersect(:id => par).ids + expect(parent_ids).to eq(Set.new(['8', '10'])) + end + + it 'queries using multiple filter objects' do + create_parent(:id => '8') + parent_8 = parent_class.find_by_id('8') + create_child(parent_8, :id => '5') + create_child(parent_8, :id => '6') + + create_parent(:id => '9') + parent_9 = parent_class.find_by_id('9') + create_child(parent_9, :id => '7') + + create_parent(:id => '10') + + par_1 = parent_class.intersect(:id => ['8']) + par_2 = parent_class.intersect(:id => ['10']) + + parent_ids = parent_class.intersect(:id => [par_1, par_2]).ids + expect(parent_ids).to eq(Set.new(['8', '10'])) + end + + it 'queries using a combination of bare value, association and filter object' do + create_parent(:id => '8') + parent_8 = parent_class.find_by_id('8') + create_child(parent_8, :id => '5') + create_child(parent_8, :id => '6') + + create_parent(:id => '9') + parent_9 = parent_class.find_by_id('9') + create_child(parent_9, :id => '7') + + create_parent(:id => '10') + parent_10 = parent_class.find_by_id('10') + create_child(parent_10, :id => '4') + + assocs = parent_class.intersect(:id => ['8']). + associations_for(:children).values + + children = child_class.intersect(:id => assocs + [ + parent_9.children.intersect(:id => '7'), '4' + ]) + expect(children.count).to eq(4) + expect(children.ids).to eq(Set.new(['4', '5', '6', '7'])) + end + end context 'influxdb', :influxdb => true, :has_many => true do let(:influxdb) { Zermelo.influxdb } @@ -347,10 +468,13 @@ attrs[:important] = attrs[:important].to_s unless attrs[:important].nil? Zermelo.influxdb.write_point("#{ck}/#{attrs[:id]}", attrs) par.children.add(child_class.find_by_id!(attrs[:id])) end + # FIXME not implemented yet for InfluxDB, see SetStep + it 'queries associated filters transparently' + end end @@ -370,11 +494,11 @@ secondary.primaries << primary secondaries = primary.secondaries.all - expect(secondaries).to be_an(Array) + expect(secondaries).to be_a(Set) expect(secondaries.size).to eq(1) other_secondary = secondaries.first expect(other_secondary).to be_a(secondary_class) expect(other_secondary.id).to eq(secondary.id) end @@ -410,13 +534,13 @@ secondary.primaries.remove(primary) expect(secondary.primaries.count).to eq(1) expect(primary.secondaries.count).to eq(0) expect(primary_2.secondaries.count).to eq(1) - expect(secondary.primaries.ids).to eq(['9']) - expect(primary.secondaries.ids).to eq([]) - expect(primary_2.secondaries.ids).to eq(['2']) + expect(secondary.primaries.ids).to eq(Set.new(['9'])) + expect(primary.secondaries.ids).to eq(Set.new) + expect(primary_2.secondaries.ids).to eq(Set.new(['2'])) end it "deletes a record from the set by id" do create_primary(:id => '9', :active => false) primary = primary_class.find_by_id('8') @@ -432,13 +556,13 @@ secondary.primaries.remove_ids('8') expect(secondary.primaries.count).to eq(1) expect(primary.secondaries.count).to eq(0) expect(primary_2.secondaries.count).to eq(1) - expect(secondary.primaries.ids).to eq(['9']) - expect(primary.secondaries.ids).to eq([]) - expect(primary_2.secondaries.ids).to eq(['2']) + expect(secondary.primaries.ids).to eq(Set.new(['9'])) + expect(primary.secondaries.ids).to eq(Set.new) + expect(primary_2.secondaries.ids).to eq(Set.new(['2'])) end it "clears all records from the set" do create_primary(:id => '9', :active => false) primary = primary_class.find_by_id('8') @@ -454,13 +578,13 @@ secondary.primaries.clear expect(secondary.primaries.count).to eq(0) expect(primary.secondaries.count).to eq(0) expect(primary_2.secondaries.count).to eq(0) - expect(secondary.primaries.ids).to eq([]) - expect(primary.secondaries.ids).to eq([]) - expect(primary_2.secondaries.ids).to eq([]) + expect(secondary.primaries.ids).to eq(Set.new) + expect(primary.secondaries.ids).to eq(Set.new) + expect(primary_2.secondaries.ids).to eq(Set.new) end context 'filters' do it "filters has_and_belongs_to_many records by indexed attribute values" do @@ -476,11 +600,11 @@ primary_2.secondaries << secondary primary_3.secondaries << secondary primaries = secondary.primaries.intersect(:active => true).all expect(primaries).not_to be_nil - expect(primaries).to be_an(Array) + expect(primaries).to be_a(Set) expect(primaries.size).to eq(2) expect(primaries.map(&:id)).to match_array(['8', '10']) end it "checks whether a record id exists through a has_and_belongs_to_many filter" do @@ -745,11 +869,11 @@ create_child(parent, :id => '4', :emotion => 'indifferent', :timestamp => time) child = child_class.find_by_id('4') children = parent.children.all - expect(children).to be_an(Array) + expect(children).to be_a(Zermelo::OrderedSet) expect(children.size).to eq(1) child = children.first expect(child).to be_a(child_class) expect(child.timestamp).to be_within(1).of(time) # ignore fractional differences end @@ -864,13 +988,13 @@ create_child(parent_10, :id => '5', :timestamp => time, :emotion => 'not_ok') assoc_ids = parent_class.intersect(:id => ['8', '9', '10']). associated_ids_for(:children) - expect(assoc_ids).to eq('8' => ['3', '4'], - '9' => [], - '10' => ['5']) + expect(assoc_ids).to eq('8' => Zermelo::OrderedSet.new(['3', '4']), + '9' => Zermelo::OrderedSet.new, + '10' => Zermelo::OrderedSet.new(['5'])) end it "deletes a record from the set" do create_child(parent, :id => '3', :timestamp => time - 20, :emotion => 'ok') @@ -879,11 +1003,11 @@ expect(parent.children.count).to eq(2) child = child_class.find_by_id('3') parent.children.remove(child) expect(parent.children.count).to eq(1) - expect(parent.children.ids).to eq(['4']) + expect(parent.children.ids).to eq(Set.new(['4'])) end it "deletes a record from the set by id" do create_child(parent, :id => '3', :timestamp => time - 20, :emotion => 'ok') @@ -891,11 +1015,11 @@ :emotion => 'ok') expect(parent.children.count).to eq(2) parent.children.remove_ids('3') expect(parent.children.count).to eq(1) - expect(parent.children.ids).to eq(['4']) + expect(parent.children.ids).to eq(Zermelo::OrderedSet.new(['4'])) end it "clears all records from the set" do create_child(parent, :id => '3', :timestamp => time - 20, :emotion => 'ok') @@ -904,11 +1028,11 @@ expect(parent.children.count).to eq(2) child = child_class.find_by_id('3') parent.children.clear expect(parent.children.count).to eq(0) - expect(parent.children.ids).to eq([]) + expect(parent.children.ids).to eq(Zermelo::OrderedSet.new) end context 'filters' do before do create_child(parent, :id => '4', :timestamp => time - 20, @@ -920,56 +1044,56 @@ end it "by indexed attribute values" do upset_children = parent.children.intersect(:emotion => 'upset').all expect(upset_children).not_to be_nil - expect(upset_children).to be_an(Array) + expect(upset_children).to be_a(Zermelo::OrderedSet) expect(upset_children.size).to eq(2) expect(upset_children.map(&:id)).to eq(['4', '6']) end it "by indexed attribute values with a regex search" do upset_children = parent.children.intersect(:emotion => /^ups/).all expect(upset_children).not_to be_nil - expect(upset_children).to be_an(Array) + expect(upset_children).to be_a(Zermelo::OrderedSet) expect(upset_children.size).to eq(2) expect(upset_children.map(&:id)).to eq(['4', '6']) end it "a subset of a sorted set by index" do range = Zermelo::Filters::IndexRange.new(0, 1) children = parent.children.intersect(:timestamp => range).all expect(children).not_to be_nil - expect(children).to be_an(Array) + expect(children).to be_a(Zermelo::OrderedSet) expect(children.size).to eq(2) expect(children.map(&:id)).to eq(['4', '5']) end it "a reversed subset of a sorted set by index" do range = Zermelo::Filters::IndexRange.new(1, 2) children = parent.children.intersect(:timestamp => range).sort(:id, :desc => true).all expect(children).not_to be_nil - expect(children).to be_an(Array) + expect(children).to be_a(Zermelo::OrderedSet) expect(children.size).to eq(2) expect(children.map(&:id)).to eq(['6', '5']) end it "a subset of a sorted set by score" do range = Zermelo::Filters::IndexRange.new(time - 25, time - 5, :by_score => true) children = parent.children.intersect(:timestamp => range).all expect(children).not_to be_nil - expect(children).to be_an(Array) + expect(children).to be_a(Zermelo::OrderedSet) expect(children.size).to eq(2) expect(children.map(&:id)).to eq(['4', '5']) end it "a reversed subset of a sorted set by score" do range = Zermelo::Filters::IndexRange.new(time - 25, time - 5, :by_score => true) children = parent.children.intersect(:timestamp => range). sort(:timestamp, :desc => true).all expect(children).not_to be_nil - expect(children).to be_an(Array) + expect(children).to be_a(Zermelo::OrderedSet) expect(children.size).to eq(2) expect(children.map(&:id)).to eq(['5', '4']) end it "checks whether a record exists" do @@ -992,54 +1116,54 @@ it "ANDs multiple union arguments, not ORs them" do children = parent.children.intersect(:id => ['4']). union(:emotion => 'upset', :id => ['4', '6']).all expect(children).not_to be_nil - expect(children).to be_an(Array) + expect(children).to be_a(Zermelo::OrderedSet) expect(children.size).to eq(2) expect(children.map(&:id)).to eq(['4', '6']) end it "ANDs multiple diff arguments, not ORs them" do children = parent.children.diff(:emotion => 'upset', :id => ['4', '5']).all expect(children).not_to be_nil - expect(children).to be_an(Array) + expect(children).to be_a(Zermelo::OrderedSet) expect(children.size).to eq(2) expect(children.map(&:id)).to eq(['5', '6']) end it "the exclusion of a sorted set by index" do range = Zermelo::Filters::IndexRange.new(0, 1) children = parent.children.diff(:timestamp => range).all expect(children).not_to be_nil - expect(children).to be_an(Array) + expect(children).to be_a(Zermelo::OrderedSet) expect(children.size).to eq(1) expect(children.map(&:id)).to eq(['6']) end it "a reversed exclusion of a sorted set by index" do range = Zermelo::Filters::IndexRange.new(2, 2) children = parent.children.diff(:timestamp => range).sort(:id, :desc => true).all expect(children).not_to be_nil - expect(children).to be_an(Array) + expect(children).to be_a(Zermelo::OrderedSet) expect(children.size).to eq(2) expect(children.map(&:id)).to eq(['5', '4']) end it "the exclusion of a sorted set by score" do range = Zermelo::Filters::IndexRange.new(time - 25, time - 5, :by_score => true) children = parent.children.diff(:timestamp => range).all expect(children).not_to be_nil - expect(children).to be_an(Array) + expect(children).to be_a(Zermelo::OrderedSet) expect(children.size).to eq(1) expect(children.map(&:id)).to eq(['6']) end it "a reversed exclusion of a sorted set by score" do range = Zermelo::Filters::IndexRange.new(time - 5, time, :by_score => true) children = parent.children.diff(:timestamp => range).sort(:timestamp, :desc => true).all expect(children).not_to be_nil - expect(children).to be_an(Array) + expect(children).to be_a(Zermelo::OrderedSet) expect(children.size).to eq(2) expect(children.map(&:id)).to eq(['5', '4']) end end \ No newline at end of file