如何让 PostgreSQL 函数支持 Rails 模型?

问题描述 投票:0回答:1

我有一个用 Rails 编写的应用程序和一个 PostgreSQL 数据库。我的应用程序有一些复杂的查询,我用它们包装在 PostgreSQL 视图中,以便我可以将 Rails 模型“插入”它们 - 使应用程序级别的事情变得更简单。

到目前为止,上述方法是成功的,因为我可以通过附加到这些视图的模型来使用许多 ActiveRecord 功能。

但是,我面临着一种情况,我必须优化其中一些视图,但我发现的唯一方法是将 PostgreSQL 视图转换为 PostgreSQL 函数,以便我可以传递用作约束的参数(在

 where
条款)。这种方法让我的浏览速度加快了 100 倍!

问题是,为了调用这些函数,我通常会执行类似

Model.find_by_sql('SELECT * FROM my_pg_function(?, ?)')
的操作 - 它返回模型实例的数组。这并不理想,因为我需要方法链接(如
Model.find_by_sql('SELECT * FROM my_pg_function(?, ?)').find(...)
) - 否则我将不得不在我的应用程序中进行大量更改。

关于如何使用原始查询以可链接的方式加载数据有什么想法吗?

ruby-on-rails postgresql activerecord
1个回答
0
投票

你应该能够使用一点 Arel 来实现这个目标。

function = Arel::Nodes::NamedFunction.new('my_pg_function',[1,2])
subquery = Arel::SelectManager.new.project(Arel.star).from(function).as(Model.table_name)

Model.from(subquery)

这将产生以下查询:

SELECT 
  models.*
FROM 
  ( SELECT 
      *
    FROM 
      my_pg_function(1, 2)
   ) models

在这种情况下,这将允许您动态替换函数

[1,2]
的参数。以及将附加条件链接到“表”,因为它将在此过程中返回一个
ActiveRecord::Relation
对象。

所以你可以像这样实现:

class MyModel < ApplicationRecord
  self.table_name = :my_models 

  def self.filter(*args)
    from( 
      Arel::SelectManager.new
        .project(Arel.star)
        .from(Arel::Nodes::NamedFunction.new('my_pg_function',*args))
        .as(table_name)
    ) 
  end
end 

并使用类似

MyModel.filter(1,2).where(x: 12..).order(:x)

请注意,传递给命名函数的参数必须可由 Arel 访问,因此如果您需要传递字符串,则需要将它们放入

Arel::Nodes
我首选的方法是使用
Arel::Nodes::build_quoted
,因为它能够很好地处理不同的数据类型,您可以找到引用的基础知识这里

© www.soinside.com 2019 - 2024. All rights reserved.