Skip to content

Commit

Permalink
Insert and Update many to many relations
Browse files Browse the repository at this point in the history
  • Loading branch information
Gabriel Naiman committed Sep 23, 2016
1 parent b9e7745 commit c90070b
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 10 deletions.
40 changes: 38 additions & 2 deletions lib/rasti/db/collection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,21 @@ def initialize(db, schema=nil)
end

def insert(attributes)
dataset.insert attributes
db.transaction do
collection_attributes, relations_primary_keys = split_related_attributes attributes
primary_key = dataset.insert collection_attributes
save_relations primary_key, relations_primary_keys
primary_key
end
end

def update(primary_key, attributes)
dataset.where(self.class.primary_key => primary_key).update(attributes)
db.transaction do
collection_attributes, relations_primary_keys = split_related_attributes attributes
updated_count = dataset.where(self.class.primary_key => primary_key).update(collection_attributes) unless collection_attributes.empty?
save_relations primary_key, relations_primary_keys
updated_count
end
end

def delete(primary_key)
Expand Down Expand Up @@ -105,6 +115,32 @@ def dataset
db[schema.nil? ? self.class.collection_name : "#{schema}__#{self.class.collection_name}".to_sym]
end

def split_related_attributes(attributes)
relation_names = self.class.relations.values.select(&:many_to_many?).map(&:name)

collection_attributes = attributes.reject { |n,v| relation_names.include? n }
relations_ids = attributes.select { |n,v| relation_names.include? n }

[collection_attributes, relations_ids]
end

def save_relations(primary_key, relations_primary_keys)
relations_primary_keys.each do |relation_name, primary_keys|
relation = self.class.relations[relation_name]
relation_collection_name = relation.qualified_relation_collection_name(schema)

values = primary_keys.map do |rel_primary_key|
{
relation.source_foreign_key => primary_key,
relation.target_foreign_key => rel_primary_key
}
end

db[relation_collection_name].where(relation.source_foreign_key => primary_key).delete
db[relation_collection_name].multi_insert values
end
end

end
end
end
18 changes: 17 additions & 1 deletion lib/rasti/db/relations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@ def target_collection_class
@target_collection_class ||= @options[:collection].is_a?(Class) ? @options[:collection] : Consty.get(@options[:collection] || camelize(pluralize(name)), self.class)
end

def one_to_many?
is_a? OneToMany
end

def many_to_one?
is_a? ManyToOne
end

def many_to_many?
is_a? ManyToMany
end

private

attr_reader :options
Expand Down Expand Up @@ -113,12 +125,16 @@ def relation_collection_name
@relation_collection_name ||= @options[:relation_collection_name] || [source_collection_class.collection_name, target_collection_class.collection_name].sort.join('_').to_sym
end

def qualified_relation_collection_name(schema=nil)
schema.nil? ? relation_collection_name : Sequel.qualify(schema, relation_collection_name)
end

def graph_to(rows, db, schema=nil, relations=[])
pks = rows.map { |row| row[source_collection_class.primary_key] }

target_collection = target_collection_class.new db, schema

relation_name = schema.nil? ? relation_collection_name : Sequel.qualify(schema, relation_collection_name)
relation_name = qualified_relation_collection_name schema

join_rows = target_collection.query do |q, ds|
ds.join(relation_name, target_foreign_key => target_collection_class.primary_key)
Expand Down
68 changes: 61 additions & 7 deletions spec/collection_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,20 @@
db[:users][id: id][:name].must_equal 'User 1'
end

it 'Insert many to many'
it 'Insert many to many' do
user_id = db[:users].insert name: 'User 1'

1.upto(2) do |i|
db[:posts].insert user_id: user_id, title: "Post #{i}", body: '...'
db[:categories].insert name: "Category #{i}"
end

post_id = posts.insert user_id: user_id, title: 'Post title', body: '...', categories: [1,2]
category_id = categories.insert name: 'Category', posts: [1,2]

db[:categories_posts].where(post_id: post_id).map(:category_id).must_equal [1,2]
db[:categories_posts].where(category_id: category_id).map(:post_id).must_equal [1,2]
end

it 'Insert batch'

Expand All @@ -49,7 +62,30 @@
db[:users][id: id][:name].must_equal 'updated'
end

it 'Update many to many'
it 'Update many to many' do
user_id = db[:users].insert name: 'User 1'

1.upto(3) do |i|
db[:posts].insert user_id: user_id, title: "Post #{i}", body: '...'
db[:categories].insert name: "Category #{i}"
end

db[:categories_posts].insert post_id: 1, category_id: 1
db[:categories_posts].insert post_id: 1, category_id: 2
db[:categories_posts].insert post_id: 2, category_id: 2

db[:categories_posts].where(post_id: 1).map(:category_id).must_equal [1,2]

posts.update 1, categories: [2,3]

db[:categories_posts].where(post_id: 1).map(:category_id).must_equal [2,3]

db[:categories_posts].where(category_id: 2).map(:post_id).must_equal [1,2]

categories.update 2, posts: [2,3]

db[:categories_posts].where(category_id: 2).map(:post_id).must_equal [2,3]
end

it 'Update batch'

Expand All @@ -65,8 +101,6 @@

it 'Delete batch'

it 'Delete cascade'

end

describe 'Queries' do
Expand Down Expand Up @@ -181,20 +215,40 @@
end
end

Sequel.mock fetch: stubs
Sequel.mock fetch: stubs, autoid: 1
end

let(:stub_users) { Users.new stub_db, :custom_schema }
let(:stub_posts) { Posts.new stub_db, :custom_schema }

it 'Insert' do
stub_users.insert name: 'User 1'
stub_db.sqls.must_equal ["INSERT INTO custom_schema.users (name) VALUES ('User 1')"]
stub_db.sqls.must_equal [
'BEGIN',
"INSERT INTO custom_schema.users (name) VALUES ('User 1')",
'COMMIT'
]
end

it 'Insert with many to many relation' do
stub_posts.insert user_id: 1, title: 'Post 1', body: '...', categories: [2,3]
stub_db.sqls.must_equal [
'BEGIN',
"INSERT INTO custom_schema.posts (user_id, title, body) VALUES (1, 'Post 1', '...')",
'DELETE FROM custom_schema.categories_posts WHERE (post_id = 1)',
'INSERT INTO custom_schema.categories_posts (post_id, category_id) VALUES (1, 2)',
'INSERT INTO custom_schema.categories_posts (post_id, category_id) VALUES (1, 3)',
'COMMIT'
]
end

it 'Update' do
stub_users.update 1, name: 'Updated name'
stub_db.sqls.must_equal ["UPDATE custom_schema.users SET name = 'Updated name' WHERE (id = 1)"]
stub_db.sqls.must_equal [
'BEGIN',
"UPDATE custom_schema.users SET name = 'Updated name' WHERE (id = 1)",
'COMMIT'
]
end

it 'Delete' do
Expand Down
24 changes: 24 additions & 0 deletions spec/relations_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@

end

it 'Type' do
relation = Rasti::DB::Relations::OneToMany.new :posts, Users

relation.one_to_many?.must_equal true
relation.many_to_one?.must_equal false
relation.many_to_many?.must_equal false
end

it 'Graph' do
user_id = db[:users].insert name: 'User 1'
1.upto(2) { |i| db[:posts].insert user_id: user_id, title: "Post #{i}", body: '...' }
Expand Down Expand Up @@ -56,6 +64,14 @@

end

it 'Type' do
relation = Rasti::DB::Relations::ManyToOne.new :user, Posts

relation.one_to_many?.must_equal false
relation.many_to_one?.must_equal true
relation.many_to_many?.must_equal false
end

it 'Graph' do
user_id = db[:users].insert name: 'User 1'
db[:posts].insert user_id: user_id, title: 'Post 1', body: '...'
Expand Down Expand Up @@ -95,6 +111,14 @@

end

it 'Type' do
relation = Rasti::DB::Relations::ManyToMany.new :categories, Posts

relation.one_to_many?.must_equal false
relation.many_to_one?.must_equal false
relation.many_to_many?.must_equal true
end

it 'Graph' do
user_id = db[:users].insert name: 'User 1'

Expand Down

0 comments on commit c90070b

Please sign in to comment.