【问题标题】:How can I query a Ruby array, like I do with Rails ActiveRecord?如何像使用 Rails ActiveRecord 一样查询 Ruby 数组?
【发布时间】:2015-08-08 19:04:39
【问题描述】:

我有一张桌子,我们称之为 Widget。

我做了一些复杂的处理来获得各种类型的小部件。它们最终形成两个不同的变量。

为了简单起见,假设我们有...

widgetsA = Widget.where("blah blah blah")
widgetsB = Widget.where("blah blah blah blah")

我们仍然可以在widgetsAwidgetsB 上执行像.where 这样的ActiveRecord 函数。

现在,在检索到 A 和 B 的集合后,我需要合并它们,然后对它们执行额外的 ActiveRecord 函数。

我想做这样的事情......

widgetsAll = widgetsA | widgetsB
widgetsAll = widgetsAll.order("RANDOM()")
widgetsAll = widgetsAll.where(answers_count: 0).limit(10) + widgetsAll.where("answers_count > 0").limit(10)

这将获取在 A 和 B 中找到的所有小部件(联合),将它们随机化,然后选择 10 个有答案和 10 个没有答案。

问题是,我不能使用 .order 并且 widgetsAll 不再是 ActiveRecord 对象,但由于 widgetsAll = widgetsA | widgetsB 行,它是一个数组。我该怎么做

A) 将两个 ActiveRecord 集合并集/相交,形成一个 ActiveRecord 集合

B) 如何对数组进行排序和执行“where”样式查询。

两者都可以解决问题。我认为 B 的性能要好一些,所以我认为这将是更好的答案。

有什么想法吗?

最后,假设 Widget 表包含 idnamedescription 列。最后,我们想要所有内容的 ActiveRecord 或 Array(可能是首选)。

编辑:(尝试通过 SQL UNION 合并...但不工作)

w1 = Widget.where("id = 1 OR id = 2")
w2 = Widget.where("id = 2 OR id = 3")
w3 = Widget.from("(#{w1.to_sql} UNION #{w2.to_sql})")

PG::SyntaxError: ERROR:  subquery in FROM must have an alias
LINE 1: SELECT "widgets".* FROM (SELECT "widgets".* FROM "widge...

【问题讨论】:

    标签: ruby-on-rails ruby activerecord


    【解决方案1】:

    我看到两个选项:

    1) 在 SQL 中进行联合:代替返回数组的widgetsA | widgetsB,您可以在数据库中进行联合,这样结果仍然是一个关系对象:

    Widget.from("(#{widgetA.to_sql} UNION #{widgetB.to_sql}) AS widgets")
    

    2) 使用普通数组方法。你的例子:

    widgetsAll = widgetsAll.order("RANDOM()")
    widgetsAll = widgetsAll.where(answers_count: 0).limit(10) + widgetsAll.where("answers_count > 0").limit(10)
    

    会翻译成这样的:

    widgetsAll = widgetsAll.shuffle
    widgetsAll = widgetsAll.select { |answer| widget.answer_count == 0 }.take(10) + 
                 widgetsAll.select { |answer| widget.answers_count > 0).take(10)
    

    阅读更多关于Ruby arrays的信息。

    【讨论】:

    • 谢谢,这看起来像我想要的。从性能的角度来看,您认为什么更好?如果我做 1,是否会因为涉及额外的查询而成本更高?用数组操作做 2 会更好吗?或者两者都可能是相同的,因为 SQL 在已经确定的 ActiveRecords 上只是一个 UNION?
    • 永远不要做 2。对于 1,我会使用 OR 而不是 UNION
    • 第一个版本会更快,因为它在数据库中进行联合,因此它仅在运行至少两个的示例的查询中运行。我添加秒版本只是因为您要求它。我不推荐第二个版本,数据库版本很可能总是更快。
    • @apneadiving:OR 与 UNION 不同。在这种情况下,当你组合两个关系对象时,你需要使用 UNION。
    • 任何一个选项都可以@chrisP - 这里的一些人建议不要使用通常比执行 SQL 查询快几个数量级的选项(例如使用数组方法)。即使是那些担心执行两个查询而不是一个查询的人也会担心通常需要几分之一毫秒的查询。做任何对你最有意义的事情,它们都是很好的解决方案。
    【解决方案2】:

    使用any_of gem 你可以做到:

    widgetsAll = Widget.where.any_of(widgetsA, widgetsB)
    

    【讨论】:

      【解决方案3】:

      一种方法如下:

      widgetsAllActual = Widget.where(id: widgetsAll)
      

      这将创建一个新的 Widget::ActiveRecord_Relation 集合,其中包含 widgetsAwidgetsB 中的所有元素,并允许进行进一步的活动记录范围。

      参考:https://stackoverflow.com/a/24448317/429758

      【讨论】:

      • 我编辑了这个以删除相对较慢的.map(&:id) - ActiveRecord 只是为您做正确的事情。
      • @smathy 你知道这是否会很昂贵(因为涉及到一个额外的查询),而不是操作已经创建的 Array 对象?还是不太可能因为 id 无论如何都会被编入索引?
      • @prakash 你知道这是否会很昂贵(因为涉及到一个额外的查询),而不是操作已经创建的 Array 对象?还是不太可能因为 id 无论如何都会被编入索引?
      • 这不是一个很好的解决方案:它会进行多次查询,并且不允许您进行排序和使用分页
      • 这是错误的@apneadiving - 你最终会在widgetsAllActual 中得到一个AR_Relation,所以你显然仍然可以执行order 或分页或任何你要执行的操作在 AR 范围内。
      猜你喜欢
      • 1970-01-01
      • 2012-05-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-04
      • 1970-01-01
      • 2011-06-02
      相关资源
      最近更新 更多