我正在编写自定义搜索功能,我必须通过关联进行过滤。 我有2个活动记录支持的模型,卡片和颜色带有has_many_and_belongs_to,颜色有属性color_name
由于我的数据库已增长到大约10k卡,我的搜索功能异常缓慢,因为我有一个带有查询的select语句,所以基本上我不得不进行数千次查询。
我需要将数组#select方法转换为活动记录查询,这将产生相同的结果,并且我无法提出解决方案。当前(相关代码)如下:
colors = [['Black'], ['Blue', 'Black']] #this is a parameter retrieved from a form submission
if color
cards = color.flat_map do |col|
col.inject( Card.includes(:colors) ) do |memo, color|
temp = cards.joins(:colors).where(colors: {color_name: color})
memo + temp.select{|card| card.colors.pluck(:color_name).sort == col.sort}
end
end
end
我试图模仿的功能是只搜索颜色与传入数组完全匹配的卡片(比较两个数组)。因为卡可以是单红,红蓝或红蓝绿等,我需要只能搜索红蓝卡或只能搜索单红卡
我最初是沿着这条路线开始的,但是我在将数组与活动记录查询进行比较时遇到了麻烦
color_objects = Color.where(color_name: col)
Card.includes(:colors).where('colors = ?', color_objects)
返回错误
ActiveRecord :: StatementInvalid:PG :: SyntaxError:ERROR:语法错误 在或附近" SELECT"第1行:... id"在哪里"卡"。" id" IN(2,3,4)和 (colors = SELECT" co ...
它看起来像它失败了,因为它不想比较数组,只比较表元素。这个功能甚至可能吗?
一种解决方案可能是将habtm转换为具有多个关系,并创建包含每个颜色排列的键的连接表,以便直接访问这些颜色
答案 0 :(得分:1)
我需要只能搜索绿黑卡片,而不会出现单绿色或绿黑红色的卡片。
我删除了我的普遍答案,因为我没有意识到你正在寻找完全匹配。
我玩了一点,我没有使用聚合函数就看不到任何解决方案。 对于Postgres,它将是array_agg。
您需要生成一个SQL查询,如:
SELECT *, array_to_string(array_agg(colors.color_name), ',')) as color_names FROM cards
JOINS cards_colors, colors
ON (cards.id = cards_colors.card_id AND colors.id = cards_colors.color_id)
GROUP BY cards.id HAVING color_names = 'green, black'
我从未使用过那些聚合器,所以array_to_string可能是一个错误的格式化程序,无论如何你必须注意按字母顺序聚合颜色。只要你没有太多卡片它就足够慢,但它会扫描表格中的每张卡片。
我想在此查询中使用索引,您应该对数据结构进行非规范化,在卡片记录上使用array of color_names,索引该数组字段并对其进行搜索。您还可以保持标准化结构并定义一个自动association callback,每次将颜色分配给卡片时,它会将颜色名称放入卡片的color_names数组中。
答案 1 :(得分:0)
试试这个
colors = Color.where(color_name: col).pluck(:id)
Card.includes(:colors).where('colors.id'=> colors)