【问题标题】:Rails query which aggregates (with an array or jsonb hash) the count of ordered products by the color of each productRails 查询通过每个产品的颜色聚合(使用数组或 jsonb 哈希)订购产品的数量
【发布时间】:2020-12-08 15:22:08
【问题描述】:

在我的 Rails (v6.0) 应用程序中,我有一个 Product 模型列出了 name 和颜色(实际上是 flavors['color'],因为作为颜色的产品变体存储在名为 @ 的 jsonb 字段中每个产品的 987654324@),以及一个关联的 Order 模型,该模型具有并属于许多 (HABTM) 产品(实际上它是一个 HMT 关系,其中包含一个列出每个订单产品数量的连接表,但是现在让我们忽略这个)。

我可以用这个查询来计算按产品颜色分组的订购产品的数量

Order.joins(:products).group(:name,"products.flavors ->> 'color'").order(:name,'products_flavors_color').count

哪个输出

=> {["jacket", "black"]=>59, ["jacket", "orange"]=>34, ["jacket", "white"]=>9, ["jacket", "red"]=>1, ["sockets", "black"]=>76, ["sockets", "green"]=>6, ["gloves", "black"]=>94, ["gloves", "green"]=>9, ["shirt", "black"]=>120, ["shirt", "orange"]=>62, ["shirt", "white"]=>19, ["shirt", "red"]=>3, ["pants", "black"]=>129, ["pants", "orange"]=>63, ["pants", "white"]=>18, ["pants", "red"]=>3, ["hat", "black"]=>86, ["hat", "orange"]=>45, ["hat", "white"]=>13, ["hat", "red"]=>1}

我希望能够按每个产品的颜色聚合(使用数组或 jsonb 哈希)订购产品的数量,以获得这样的对象:

{"jacket"=>{"black":59,"orange":34,"white":9,"red":1},"sockets"=>{"black":76,"green":6},"gloves"=>{"black":94,"green":9},"shirt"=>{"black":10,"orange":62,"white":19,"red":3},"pants"=>{"black":129,"orange":63,"white":18,"red":3},"hat"=>{"black":86,"orange":45,"white":13,"red":1}}

与其在 Rails 中操作对象(除非它可以通过非常有效的方法完成),我更愿意由 RDBMS (PostgreSQL 12.4) 使用聚合函数(如 array_agg()jsonb_agg())完成。

我尝试过类似的方法

Order.joins(:products).select(:name,"ARRAY_AGG(products.flavors ->> 'color')").group(:name).count(:name)

但它失去了聚合功能。因为它看起来更像一个 PostgreSQL 问题,所以也接受纯 SQL 提示,但我希望能够使用不可知的 Active Record 查询方法来实现查询(聚合函数除外,但要避免 find_by_sql 或类似 arel )。

【问题讨论】:

    标签: sql ruby-on-rails ruby postgresql aggregate-functions


    【解决方案1】:

    您将无法从 SQL 查询中本地获取所需的结果对象,因为您不能像这样嵌套分组;但是,您可以将生成的Hash 操作到您想要的Hash 结构中。

    举例

    result = Order.joins(:products)
                  .group(:name,"products.flavors ->> 'color'")
                  .order(:name,'products_flavors_color')
                  .count
    #=> {["jacket", "black"]=>59, ["jacket", "orange"]=>34, ["jacket", "white"]=>9, ["jacket", "red"]=>1, ["sockets", "black"]=>76, ["sockets", "green"]=>6, ["gloves", "black"]=>94, ["gloves", "green"]=>9, ["shirt", "black"]=>120, ["shirt", "orange"]=>62, ["shirt", "white"]=>19, ["shirt", "red"]=>3, ["pants", "black"]=>129, ["pants", "orange"]=>63, ["pants", "white"]=>18, ["pants", "red"]=>3, ["hat", "black"]=>86, ["hat", "orange"]=>45, ["hat", "white"]=>13, ["hat", "red"]=>1}
    
    desired_result = result.each_with_object(Hash.new {|h,k| h[k] = {}}) do |((article,color),count),obj|
                        obj[article][color] = count
                     end
    #=> {"jacket"=>{"black"=>59, "orange"=>34, "white"=>9, "red"=>1}, 
    # "sockets"=>{"black"=>76, "green"=>6}, 
    # "gloves"=>{"black"=>94, "green"=>9}, 
    # "shirt"=>{"black"=>120, "orange"=>62, "white"=>19, "red"=>3}, 
    # "pants"=>{"black"=>129, "orange"=>63, "white"=>18, "red"=>3}, 
    # "hat"=>{"black"=>86, "orange"=>45, "white"=>13, "red"=>1}}
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-02
      • 1970-01-01
      相关资源
      最近更新 更多