(注意:我进行了多次更新,您可能对UPDATE 2或UPDATE 3中提供的代码最感兴趣。)
我想你可以在你的问题模型中放置以下内容:
def diff_tags(other_q)
other_q.tags - tags
end
def add_tags(other_q)
tags << diff_tags(other_q)
end
然后执行以下操作:
q1 = Question.find(1)
q2 = Question.find(2)
q1.add_tags(q2)
导致(在我的情况下为 Postgres):
SELECT "tags".* FROM "tags" INNER JOIN "questions_tags" ON "tags"."id" = "questions_tags"."tag_id" WHERE "questions_tags"."question_id" = ? [["question_id", 2]]
SELECT "tags".* FROM "tags" INNER JOIN "questions_tags" ON "tags"."id" = "questions_tags"."tag_id" WHERE "questions_tags"."question_id" = ? [["question_id", 1]]
begin transaction
INSERT INTO "questions_tags" ("question_id", "tag_id") VALUES (1, <missing tag id 1>)
INSERT INTO "questions_tags" ("question_id", "tag_id") VALUES (1, <missing tag id 2>)
... and all other missing tags ...
commit transaction
您可以进一步处理以下查询:
1) 在前 2 个查询中仅选择标签 ID,而不是实例化整个标签对象
2)在单个 SQL 语句中插入多个值INSERT INTO "questions_tags" ("question_id", "tag_id") VALUES ( <question_id>, <id1> ), ( <question_id>, <id2> )
,但您可能需要为此使用原始 sql。
更新:这是优化版本:
def diff_tags_ids(other_q)
(other_q.tags.select(:id) - tags.select(:id)).map(&:id)
end
def add_tags_ids(tag_ids)
query_head = 'INSERT INTO "questions_tags" ("question_id", "tag_id") VALUES '
query_values = []
tag_ids.each do |tag_id|
query_values << "(#{self.id},#{tag_id})"
end
query = query_head + query_values.join(", ")
ActiveRecord::Base.connection.execute(query)
end
def add_tags_from(other_q)
add_tags_ids( diff_tags_ids(other_q) )
end
现在以下
q1 = Question.find(1)
q2 = Question.find(2)
q1.add_tags_from(q2)
仅导致 3 个查询:
SELECT id FROM "tags" INNER JOIN "questions_tags" ON "tags"."id" = "questions_tags"."tag_id" WHERE "questions_tags"."question_id" = ? [["question_id", 3]]
SELECT id FROM "tags" INNER JOIN "questions_tags" ON "tags"."id" = "questions_tags"."tag_id" WHERE "questions_tags"."question_id" = ? [["question_id", 1]]
INSERT INTO "questions_tags" ("question_id", "tag_id") VALUES (1,5), (1,6) # or whatever values are missing in question 1 compared to question 2
更新 2:刚刚意识到您不需要从第二个问题中读取标签,您已经将它们放在 tag_list 中。好吧,那就更简单了:
def diff_tags_ids(tag_list)
(tag_list - tags.select(:id)).map(&:id)
end
def add_tags_ids(tag_ids)
query_head = 'INSERT INTO "questions_tags" ("question_id", "tag_id") VALUES '
query_values = []
tag_ids.each do |tag_id|
query_values << "(#{self.id},#{tag_id})"
end
query = query_head + query_values.join(", ")
ActiveRecord::Base.connection.execute(query)
end
def update_tags(tag_list)
add_tags_ids( diff_tags_ids(tag_list) )
end
这个我没有在实际的应用程序上尝试过,如果有一些小错别字,请见谅。
更新 3:如果您的 tag_list 中有标签名称,而不是标签对象name
,那么这里是更新(假设您的标签模型中有属性:
def diff_tags_names(tag_list)
tag_list - tags.select(:name).map(&:name)
end
def find_tags_ids_by_names(tag_list)
Tag.where( :name => tag_list ).select(:id).map(&:id)
# That leads to SELECT "tags"."id" FROM "tags" WHERE "tags"."name" IN ('tag1', 'tag2', ...)
end
def add_tags_ids(tag_ids)
query_head = 'INSERT INTO "questions_tags" ("question_id", "tag_id") VALUES '
query_values = []
tag_ids.each do |tag_id|
query_values << "(#{self.id},#{tag_id})"
end
query = query_head + query_values.join(", ")
ActiveRecord::Base.connection.execute(query)
end
def update_tags(tag_list)
tags_ids_to_add = find_tags_ids_by_names( diff_tags_names(tag_list) )
add_tags_ids( tags_ids_to_add )
end
仍然只有两个查询...