Rails在同一个字段上查询多个条件

时间:2014-03-26 21:09:30

标签: sql ruby-on-rails ruby postgresql activerecord

我有两个模型,RecipesSkills。在这种情况下,技能是一种烹饪技术,如烘焙,油炸等。因此每个食谱都有一定的相关技能。

我想搜索所有这些食谱:

  1. 查找使用任何特定技能的所有食谱(例如BakingFrying或两者兼备)

    编辑:这不应该返回需要搜索查询中没有技能的食谱 - 例如如果我搜索技能[1,2]我不想要一个使用技能[1,2,4]或任何其他超集的食谱。

  2. 如果您在搜索中添加新技能,请仅返回其他食谱(例如,如果您将Boiling添加到上一个BakingFrying的查询中,你现在可以做新的食谱吗?)

  3. 我目前使用普通的旧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方式编写这些查询?

5 个答案:

答案 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_idskills_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