【问题标题】:Rails enumerables: why would find_all{...}.count return a different value than count{...}?Rails 可枚举:为什么 find_all{...}.count 返回与 count{...} 不同的值?
【发布时间】:2014-02-15 23:27:54
【问题描述】:

背景

我正在使用

> ruby -v
ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-darwin12.0]

> rails -v
Rails 4.0.2

一般来说,

> [1,2,3,4,5].find_all{|x| x == 4}.count

> [1,2,3,4,5].count{|x| x == 4}

给出相同的值:

=> 1

...一切都很好。

问题

但在我的应用程序中,出了点问题。当我输入断点(使用 pry)时,我注意到我遇到了不一致:

(这里不用太担心特定的数据结构)

> Meme.find(24).punches.count{|punch| punch.new_to_user?(User.find(14))}
=> 6

鉴于:

> Meme.find(24).punches.find_all{|punch| punch.new_to_user?(User.find(14))}.count
=> 0

为什么要这样做?

6 != 0,阿米特?从http://ruby-doc.org/core-2.1.0/Enumerable.html 文档看来,ruby 2.1.0 应该同样对待这两种情况。

当我查看这些命令执行的内容时,很明显 .count{} 并没有真正评估其块内的代码:

> Meme.find(24).punches.count{|punch| punch.new_to_user?(User.find(14))}
 CACHE (0.0ms)  SELECT "memes".* FROM "memes" WHERE "memes"."id" = $1 LIMIT 1  [["id", 24]]
 CACHE (0.0ms)  SELECT COUNT(*) FROM "punches" WHERE "punches"."meme_id" = $1  [["meme_id", 24]]
=> 6

与 find_all 的(我认为的)正确行为相反:

> Meme.find(24).punches.find_all{|punch| punch.new_to_user?(User.find(14))}.count
 CACHE (0.0ms)  SELECT "memes".* FROM "memes" WHERE "memes"."id" = $1 LIMIT 1  [["id", 24]]
 CACHE (0.0ms)  SELECT "punches".* FROM "punches" WHERE "punches"."meme_id" = $1  [["meme_id", 24]]
 CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 14]]
 CACHE (0.2ms)  SELECT "votes".* FROM "votes" INNER JOIN "vote_decisions" ON "votes"."vote_decision_id" = "vote_decisions"."id" WHERE "vote_decisions"."user_id" = $1 AND "votes"."punch_id" = 531  [["user_id", 14]]
 CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 14]]
 CACHE (0.0ms)  SELECT "votes".* FROM "votes" INNER JOIN "vote_decisions" ON "votes"."vote_decision_id" = "vote_decisions"."id" WHERE "vote_decisions"."user_id" = $1 AND "votes"."punch_id" = 532  [["user_id", 14]]
 CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 14]]
 CACHE (0.0ms)  SELECT "votes".* FROM "votes" INNER JOIN "vote_decisions" ON "votes"."vote_decision_id" = "vote_decisions"."id" WHERE "vote_decisions"."user_id" = $1 AND "votes"."punch_id" = 533  [["user_id", 14]]
 CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 14]]
 CACHE (0.0ms)  SELECT "votes".* FROM "votes" INNER JOIN "vote_decisions" ON "votes"."vote_decision_id" = "vote_decisions"."id" WHERE "vote_decisions"."user_id" = $1 AND "votes"."punch_id" = 534  [["user_id", 14]]
 CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 14]]
 CACHE (0.0ms)  SELECT "votes".* FROM "votes" INNER JOIN "vote_decisions" ON "votes"."vote_decision_id" = "vote_decisions"."id" WHERE "vote_decisions"."user_id" = $1 AND "votes"."punch_id" = 535  [["user_id", 14]]
 CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 14]]
 CACHE (0.0ms)  SELECT "votes".* FROM "votes" INNER JOIN "vote_decisions" ON "votes"."vote_decision_id" = "vote_decisions"."id" WHERE "vote_decisions"."user_id" = $1 AND "votes"."punch_id" = 536  [["user_id", 14]]
=> 0

可能的答案

  • 我使用的 ruby​​ 或 rails 版本是否不支持 count{block} 的这种使用?我一直在使用 ruby​​ 2.1.0 doc http://ruby-doc.org/core-2.1.0/Enumerable.html 作为参考。

  • 我的应用正在使用或 pry 正在使用的版本是否与我预期的 2.1.0/4.0.2 不同? FWIW,在我的 Gemfile 中有

    source 'https://rubygems.org'
    ruby "2.1.0"
    gem 'rails', '4.0.2'
    
  • 缓存?我完全不明白。

谢谢!

编辑:

澄清一下,new_to_user?与其他 ActiveRecords 做一些工作。这就是为什么我说 find_all 行为似乎是正确的。 count{} 似乎正在运行一个简单的 SQL COUNT 命令,这对我的目的来说是错误的(但对于 ruby​​ 版本可能是正确的,原因我不明白)

【问题讨论】:

    标签: ruby-on-rails ruby ruby-2.1


    【解决方案1】:

    Meme.find(24).punches 不返回数组。它返回一个ActiveRecord::Relation,其行为通常类似于一个数组,但它有一些不同的属性。

    当您在关系上调用 #count 时,将执行 ActiveRecord association #count 方法,而不是 Enumerable #count。这意味着Meme.find(24).punches.count 应该对模因进行 SQL 计数并返回模因的出拳次数,而不管块(在这种情况下被忽略)。

    如果要达到同样的效果,首先需要将关联转换为Array

    Meme.find(24).punches.to_a.count{|punch| punch.new_to_user?(User.find(14))}
    

    【讨论】:

    • 谢谢!我是否错过了一种更有效或更有效的方式来做到这一点?
    【解决方案2】:

    来自 enumerable 的find_all 与来自 ActiveRecord 的find_all 方法不同。

    【讨论】:

      猜你喜欢
      • 2014-12-08
      • 2014-07-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-22
      • 2022-11-03
      • 2013-02-25
      • 1970-01-01
      相关资源
      最近更新 更多