【问题标题】:Differences between `any?` and `exists?` in Ruby on Rails?Ruby on Rails 中 `any?` 和 `exists?` 之间的区别?
【发布时间】:2018-06-29 05:59:18
【问题描述】:

在 Ruby on Rails 中,似乎有两种方法可以检查集合中是否包含任何元素。

ActiveRecord::FinderMethods’ exists?ActiveRecord::Relation’s any?。在通用查询(Foo.first.bars.exists?Foo.first.bars.any?)中运行这些生成等效的 SQL。有什么理由使用其中一个吗?

【问题讨论】:

    标签: ruby-on-rails ruby ruby-on-rails-4 activerecord


    【解决方案1】:

    #any#exists? 是非常不同的野兽,但查询相似。

    主要是,#any? 接受一个块 - 并使用此块检索关系中的记录,调用 #to_a,调用该块,然后使用 Enumerable#any? 命中它。没有块,它相当于!empty?,并计算关系的记录。

    #exists? 总是 查询数据库并且从不依赖预加载的记录,并将 LIMIT 设置为 1。与 #any? 相比,它的性能要高得多。 #exists? 还接受选项参数作为应用条件,如您在文档中所见。

    【讨论】:

    • 为什么exists 从不依赖预加载的记录时性能要高得多,否则 AFAICT 会导致与非块 any 相同的查询?
    【解决方案2】:

    ActiveRecord#any? 的使用比ActiveRecord#exists? 减少。有吗?在传递块的情况下,您可以检查该数组中的某些元素是否符合条件。类似于Enumerable#any?,但不要混淆它们。

    ActiveRecord#any? 在其definition 的逻辑内实现Enumerable#any?,通过将访问的关系转换为数组,以防已将块传递给它并产生并访问块参数以在“手工”方式是“Ruby”any? 方式。

    添加的方便else是为了返回对空的否定?应用于关系。这就是为什么您可以通过两种方式检查模型中是否有记录,例如:

    User.count # 0
    User.any?  # false
    # SELECT  1 AS one FROM "users" LIMIT ?  [["LIMIT", 1]]
    User.exists? # false
    # SELECT  1 AS one FROM "users" LIMIT ?  [["LIMIT", 1]]
    

    您还可以签入“任何?”方式,如果某些记录属性具有特定值:

    Foo.any? { |foo| foo.title == 'foo' } # SELECT "posts".* FROM "posts"
    

    或者通过使用exists来节省“效率”?并改进您的查询和代码行数:

    Foo.exists?(title: 'foo') # SELECT  1 AS one FROM "posts" WHERE "posts"."title" = ? LIMIT ?  [["title", "foo"], ["LIMIT", 1]]
    

    ActiveRecord#exists? 提供了许多实现,并且旨在在 SQL 级别上工作,而不是 any?,无论如何,如果你不传递一个块,它将把你正在使用的关系转换为一个数组。

    【讨论】:

    • 谢谢。我找到了吗?是有问题的,因为它可以给出错误的答案,然后中断后续查询,例如获取 has_many 的第一条记录,因为 rails 似乎记住了 any? 返回的(错误)结果。很遗憾,因为它读起来很好。
    【解决方案3】:

    这里的答案都是基于非常过时的版本。 This commit from 2016 / ActiveRecord 5.1 将 empty? 更改为在未通过任何块时由 any? 调用,在未预加载时调用 exists?。所以在现代的 Rails 中,没有块被传递的唯一区别是一些额外的方法调用和否定,并忽略预加载的结果。

    【讨论】:

      【解决方案4】:

      我遇到了一个实际问题:exists? 强制进行数据库查询,而 any? 没有。

      user = User.new
      user.skills = [Skill.new]
      user.skills.any?
      # => true
      user.skills.exists?
      # => false
      

      考虑拥有工厂和before_create 钩子:

      class User < ActiveRecord::Base
        has_many :skills
      
        before_create :ensure_skills
      
        def ensure_skills
          # Don't want users without skills
          errors.add(:skills, :invalid) if !skills.exists?
        end
      end
      
      
      FactoryBot.define do
        factory :user do
          skills { [association(:skill)] }
        end
      end
      

      create(:user) 会失败,因为在before_create 的时候技能还没有持久化。使用.any? 可以解决这个问题。

      【讨论】:

        猜你喜欢
        • 2014-07-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多