我偶然发现了这段代码:
users = User.where([...])
users.group!(:character_id, :age)
user_zip = {
character_id: :character_id, (=> has one character)
age: :age,
amount: Arel.sql('SUM(amount)')
}
user_cleaned = users.pluck(*user_zip.values)
然后,输出是一个包含“转换”(?)用户数据的数组。
这里到底发生了什么?我以前从来没有见过这个。有没有可能用这个做一个单独的记录?
4条答案
按热度按时间7nbnzgx91#
这段代码中有几件值得注意的事情:
*
的数组重构pluck
Arel.sql
拔力定义为
pluck(*column_names)
这意味着它的参数数量是可变的。
如果你用
pluck([:age, :name])
该方法将其视为数组的数组(
[[:age, :name]]
),因此必须"解构"数组firs:pluck(*[:age, :name])
或者首先用单个值调用它:
pluck(:age, :name)
(如果您以数组形式接收值,就像您的情况一样,这可能是不可能的)
See this Thoughtbot post for more details
pluck
做两件事:select
子句因此以下行返回相同的数据
第一种是效率最低的:它选择ALL列并创建模型,以便在调用
map
时丢弃它们第二种方法效率更高,因为它只选择所需的列,但仍然创建模型。
pluck
没有选择 * 所有 * 列和创建模型的开销,因此它通常更快,内存效率更高。在SQL级别上,它可以归结为以下两个方面的区别
select * from users
以及
select age, name from users
因此,IMHO
pluck
对于API或导出到CSV等时特别有用。Rails希望您将不安全的SQL Package 在
Arel.sql
中,这是一种安全措施(我个人觉得这很烦人,也很没用),让您知道您有一个潜在的不安全操作,Arel/ActiveRecord无法为您检查。您不能在单个记录上调用它(并且这也没有多大意义,因为单个记录已经是执行的DB查询的结果,并且那里已经构造了Model)。
如果您想对已从DB加载的单个记录执行类似操作:
用户.属性. values_at('年龄','姓名')
也可以将查询限制为仅返回一条记录的结果
User.where(id: 123).pluck(:age, :name) # only one record since PK
或
User.where(some conditions).limit(1).pluck(:age, :name)
请注意,
pluck
的返回值有些不一致:如果只提取一个属性,则返回一个值数组
["卡尔","迈克"]
但是当你提取多个属性时它会返回一个数组的数组值
x1米20英寸1x
r1zhe5dt2#
让我们从结尾开始分解:
user_zip.values
表示“获取Hashuser_zip
并返回每个键的值的数组。”例如,下面的哈希:会再回来:
再往上一步,
user_cleaned = users.pluck(*user_zip.values)
使用splat operator将上面的数组解构为参数,这些参数被传递到pluck()
,这样您实际上运行的是:至于您提出的将此函数用于单个记录的问题,
pluck
的用途是从多个记录中检索值,如果您只希望检索单个记录,则不使用pluck
(并且它不是一个用于单个记录的方法)但是您可以限制where()
查询,使其返回单个记录,例如.limit(1)
,然后您可以在其上使用pluck
,如下所述:nwo49xxi3#
这段代码是一种非常复杂的方式:
组合散列并从中提取值是聪明的,但完全没有意义--它不会使代码更容易理解或维护。
这里到底发生了什么?
pluck
选择列列表并立即触发数据库查询。查询的结果是数组的数组,而不是模型示例。你会得到这样的结果:
对于非常简单的情况,这是一个方便的低级工具。但它也被广泛地过度使用。
一半的时间实际上一开始就不需要--就像几乎每次有人做
pluck(:id)
一样。select_all
做同样的事情-返回原始查询值,但是它将结果行作为散列返回,这样处理起来就简单多了。可以将此用于单个记录吗?
是的--如果你应用一个
limit(1)
或者where(id: x)
的话。但是这有点愚蠢。创建一个模型示例的开销是可以忽略的。qco9c6ql4#
我将从末尾开始。由于
pluck
是在数据库级别上作为SELECT pluck_columns FROM ...
执行的,因此它不能在单个记录上执行,也不能在记录数组上执行。它必须在“ActiveRecord::Relation”上调用。如果您在开发环境控制台中运行此命令,您将看到它创建了如下查询:
因此,“转化”后的数据是具有相同character_id和年龄的用户群体持有的金额。
如果您需要解释代码中的ruby部分,如
*user_zip.values
,请参考@anothermh的另一个答案