我有两个模型,Recipes
和Skills
。在这种情况下,技能是一种烹饪技术,如烘焙,油炸等。因此每个食谱都有一定的相关技能。
我想搜索所有这些食谱:
查找使用任何特定技能的所有食谱(例如Baking
或Frying
或两者兼备)
编辑:这不应该返回需要搜索查询中没有技能的食谱 - 例如如果我搜索技能[1,2]我不想要一个使用技能[1,2,4]或任何其他超集的食谱。
如果您在搜索中添加新技能,请仅返回其他食谱(例如,如果您将Boiling
添加到上一个Baking
或Frying
的查询中,你现在可以做新的食谱吗?)
我目前使用普通的旧Ruby方法在Rails中使用它:
class Recipe < ActiveRecord::Base
has_many :practices
has_many :skills, through: :practices
def self.find_recipes_that_require_any_of_these_skills(*known_skill_ids)
self.select do |recipe|
recipe.skill_ids.all? do |skill_id|
known_skill_ids.include?(skill_id)
end
end
end
# calls the above method twice, once with the new skill and once without
# and subtracts one result from the other
def self.find_newly_unlocked_recipes(*prior_skill_ids, new_skill_id)
self.find_recipes_that_require_any_of_these_skills(*(prior_skill_ids + [new_skill_id])) - self.find_recipes_that_require_any_of_these_skills(*prior_skill_ids)
end
end
在Rails控制台中:Recipe.find_recipes_that_require_any_of_these_skills(1,4)
返回技能1,技能4或技能1&amp;的所有Recipe对象。 4.
但这是低效的,因为它为我的数据库中的每个配方生成一个SQL查询。
如何以ActiveRecord / SQL方式编写这些查询?
答案 0 :(得分:2)
def self.find_recipes_that_require_any_of_these_skills(*known_skill_ids)
self.includes(:skills).where(skills: { id: known_skill_ids })
end
答案 1 :(得分:0)
以下方法总共只使用三个sql查询来创建收件人集合
array_of_skills=["cooking","frying",...]
skills=Skill.where('name in (?)',array_of_skills).map(&:id)
到目前为止,您已经拥有了,所以您可能需要这样:
practices=Practice.where('skill_id in (?)',skills).map(&:recipe_id)
recipes=Recipe.where('id in (?)', practices)
也许有更好的方法,但我认为不会有更少的sql
答案 2 :(得分:0)
由于您使用的是has_many :skills, through: :practices
- 您的惯例表应包含recipe_id
和skills_id
列。
previous_recipe_ids = Recipe.joins(:practices).where('practices.skill_id in (?)', prior_skills_ids).map(&:id)
Recipe.joins(:practices).where('practices.skill_id = (?) and recipes.recipe_id not in (?)', new_skill_id, previous_recipe_ids)
答案 3 :(得分:0)
您的 find_recipes_that_require_any_of_these_skills 方法似乎并不正确。它返回包含所有known_skill的配方,而不是任何。
所以,ActiveRecord / SQL方式:
已知技能:
class Skill < ActiveRecord::Base
#known skills
scope :known_skills, -> { where(id: known_skill_ids) }
#not known skills
scope :not_known_skills, -> { where("skills.id NOT IN (?)", known_skill_ids) }
具有任何已知技能的食谱:
Recipe.joins(:skills).merge(Skill.known_skills)
Newly_unlocked_recipes:
Recipe.joins(:skills).merge(Skill.not_known_skills).where("skills.id = ?", new_skill_id)
答案 4 :(得分:0)
对DB的两个查询:
def self.find_recipes_that_require_any_of_these_skills(*known_skill_ids)
Recipe.joins(:skills)
.merge(Skill.where(id: known_skill_ids))
.where("recipes.id NOT IN (?)", Recipe.joins(:skills).merge(Skill.where("skills.id NOT IN (?)", known_skill_ids)).uniq.pluck(:id)).uniq
end