Skip to content

Commit

Permalink
Add cross_product shortcut
Browse files Browse the repository at this point in the history
  * Sugar over join with empty on-list
  * Checks that there are no duplicate attributes between the relations
  • Loading branch information
felixyz committed Mar 5, 2024
1 parent 8e99975 commit d9dc75f
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 0 deletions.
4 changes: 4 additions & 0 deletions lib/bmg/algebra/shortcuts.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ def left_join(right, on = [], *args)
self.left_join(right.rename(renaming), on.keys, *args)
end

def cross_product(right)
return join(right)
end

def matching(right, on = [])
return super unless on.is_a?(Hash)
renaming = Hash[on.map{|k,v| [v,k] }]
Expand Down
6 changes: 6 additions & 0 deletions lib/bmg/type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,12 @@ def unknown_attributes!(attrs)
end

def join_compatible!(right, on)
if on.empty? # Cross product
duplicates = self.attrlist & right.attrlist
return if duplicates.empty?
raise TypeError, "Cross product incompatible - duplicate attribute(s): #{duplicates.join(', ')}"
end

extra = on - self.attrlist
raise TypeError, "Unknow attributes #{extra.join(', ')}" unless extra.empty?
if right.knows_attrlist?
Expand Down
42 changes: 42 additions & 0 deletions spec/unit/algebra/shortcuts/test_cross_product.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
require 'spec_helper'
module Bmg
module Algebra
describe Shortcuts, "cross_product" do

let(:left) {
Relation.new([
{ a: "foo", b: 1 },
{ a: "bar", b: 2 }
], Type::ANY.with_attrlist([:a, :b]))
}

let(:right) {
Relation.new([
{ c: "baz", d: 3 },
{ c: "zap", d: 4 }
], Type::ANY.with_attrlist([:z, :c]))
}

subject {
left.cross_product(right)
}

it 'compiles as expected' do
expect(subject).to be_a(Operator::Join)
expect(left_operand(subject)).to be(left)
expect(right_operand(subject)).to be(right)
expect(subject.send(:on)).to eql([])
end

it 'works as expected' do
expect(subject.to_set).to eql([
{ a: "bar", b: 2, c: "zap", d: 4 },
{ a: "foo", b: 1, c: "baz", d: 3 },
{ a: "foo", b: 1, c: "zap", d: 4 },
{ a: "bar", b: 2, c: "baz", d: 3 },
].to_set)
end

end
end
end
7 changes: 7 additions & 0 deletions spec/unit/type/test_type_check.rb
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,13 @@ module Bmg
type.join(right_type, [:id])
}.to raise_error(Bmg::TypeError, /id/)
end

it 'detects duplicate attributes when on is empty' do
right_type = Type::ANY.with_attrlist([:name])
expect{
type.join(right_type, [])
}.to raise_error(Bmg::TypeError, /Cross product incompatible.*name/)
end
end

describe "matching typecheck" do
Expand Down

0 comments on commit d9dc75f

Please sign in to comment.