postgresql Ruby on Rails -选择数组中所有id的位置

anhgbhbe  于 2022-11-04  发布在  PostgreSQL
关注(0)|答案(5)|浏览(129)

我试图找到一种最简洁的方法,根据关联和搜索数组来选择记录。
我有一个Recipes,其中有许多Ingredients(通过连接表)我有一个Ingredient.idsarray搜索表单字段
要查找包含搜索数组中任何id的任何配方,我可以使用
例如1。
filtered_meals = Recipe.includes(:ingredients).where("ingredients.id" => ids)
但是,我只想匹配所有成分都在搜索数组中找到的食谱。
例如2。

search_array = [1, 2, 3, 4, 5]
Recipe1 = [1, 4, 5, 6]
Recipe2 = [1, 3, 4]

# results => Recipe2

我知道我可以使用each循环,类似于这样;
例如3。

filtered_meals = []

 Recipes.each do |meal|
   meal_array = meal.ingredients.ids
   variable =  meal_array-search_array
     if variable.empty?
       filtered_meals.push(meal)
     end
   end
 end

 return filtered_meals

这里的问题是分页。在第一个示例中,我可以使用.limit().offset()来控制显示多少个结果,但在第三个示例中,我需要添加一个额外的计数器,将其与结果一起提交,然后在页面更改时重新发送计数器,并在each.do循环中使用.drop(counter)
这似乎太罗嗦了,有没有更好的方法来做到这一点?

lp0sw83n

lp0sw83n1#

假设你使用的是has_many through & recipe_id,配料_id组合是唯一的。

recipe_ids = RecipeIngredient.select(:recipe_id)
                             .where(ingredient_id: ids)
                             .group(:recipe_id)
                             .having("COUNT(*) >= ?", ids.length)
filtered_meals = Recipe.find recipe_ids
ih99xse1

ih99xse12#

不如

filtered_meals = Recipe.joins(:ingredients)
                       .group(:recipe_id)
                       .order("ingredients.id ASC")
                       .having("array_agg(ingredients.id) = ?", ids)

您需要确保ids参数是按升序列出的,这样数组中元素的顺序也会匹配。

z6psavjg

z6psavjg3#

Ruby on Rails Guide 2.3.3 - Subset Conditions
SELECT * FROM recipes WHERE (recipes.ingredients IN (1,2,3,4,5))
应导致:
SELECT * FROM recipes WHERE (recipes.ingredients IN (1,2,3,4,5))
得双曲余切值.

31moq8wy

31moq8wy4#

数组&运算符在这里是否适用?
类似于:

search_array = [1, 2, 3, 4, 5]
recipe_1 = [1, 4, 5, 6]
recipe_2 = [1, 3, 4]

def contains_all_ingredients?(search_array, recipe)
  (search_array & recipe).sort == recipe.sort
end

contains_all_ingredients(search_array, recipe_1) #=> false
contains_all_ingredients(search_array, recipe_2) #=> true

这个方法比较数组,只返回两者都存在的元素,所以如果比较的结果等于配方数组,则所有元素都存在(显然,您可以进行一些重构,使该方法位于配方模型中)。
然后,您可以执行以下操作:

Recipes.all.select { |recipe| contains_all_ingredients?(search_array, recipe) }

我不确定它是否通过你的示例三,但可能会对你有所帮助?让我知道它是否开始好,同时我会有更多的思考/如果它有用:)

qjp7pelc

qjp7pelc5#

我有一个类似的需求,并使用下面的模式解决了它。这是我的食谱模型中的方法。

def self.user_has_all_ingredients(ingredient_ids)
    # casts ingredient_ids to postgres array syntax
    ingredient_ids = '{' + ingredient_ids.join(', ') + '}'

    return Recipe.joins(:ingredients)
                  .group(:id)
                  .having('array_agg(ingredients.id) <@ ?', ingredient_ids)
  end

这将返回所有所需成分都包含在成分数组中的每个配方。
Postgres '〈@'操作符是一个神奇的解决方案。array_agg函数创建一个包含每个配方的配料id的数组,然后向左指的bird操作符询问该数组中的所有唯一id是否都包含在右边的数组中。
使用array_agg函数需要将search_array转换为Postgres语法。
我的“食谱”模型有许多“配料”和“部分”。
我很想知道是否有人有更好的优化,或者知道如何避免我需要做的Postgres语法转换。

相关问题