最近遇到一些 rails 查询的难题,大部分都是关于 ActiveRecord_Relation 对象的。。 有几个新手问题我现在没有搞明白 我们经常用 where 查询,查询后的结果是 ActiveRecord_Relation,这相当与一个集合的概念吗?为什么不可以进行遍历,获取其中的值,每个数据的值
没看懂你说的。。。
简单解释下 Relation 的价值,另外你可能还会看到一个叫做 CollectionProxy 的类型(user.posts 这种类似调用会遇到),他是 Relation 的子类。
Relation
CollectionProxy
user.posts
Relation 实现了 Enumerable,所以你做遍历之类的集合操作是 OK 的,另外,其实在你真正需要使用数据前,还没有进行查询呢,这种手法叫做 lazy_evolution,就是延迟计算。
Enumerable
lazy_evolution
想象一条查询 Post.where(author: me).page(2).per_page(10) 我的意图是获取我发表的帖子中位于第二页的,如果没有 Relation 每次调用都会返回结果集,那么就要意味着进行 SQL 查询,这里就有问题,首先明确一点,IO 的代价是极大的,SQL 查询是一个网络 IO 操作,通常是性能的瓶颈所在,其次,这个中间结果我们并不需要。那么,我们就需要一种手段,来避免这种中间过程的产生,这就是 Relation 的价值。
Post.where(author: me).page(2).per_page(10)
此外,ORM 有一种经典问题要避免,即 N+1,想象 user.posts.map { |post| post.title },从代码上看,我需要的是每一条 post,于是就产生了 N+1 问题(查询 user 为一次查询,之后发出等同于 posts 数量的查询来获取每条 post 的数据),Relation 来持有 eager_load 的信息,保证绝大情况下避免 N+1 的情况(当然需要你来手动指出)。
user.posts.map { |post| post.title }
eager_load
另外这里引申一点,Ruby 标准库的集合操作都是不支持延迟计算的,于是会导致大量中间变量的创建,这其实隐含了性能瓶颈。
感谢大神的回复,今天写了遍历,把循环都写在内存里面,所以感觉 rails 的原理这方面的东西,还是深入的了解,尤其是 ORM,这块地的延迟加载
你要有一定编程经验的话,可以读读 CollectionProxy 的源码,大概浏览一下就知道他想干啥了,实现比较复杂,但是意图很明显的
@jasl 请问下如何进行一些复杂的分组查询?
http://guides.rubyonrails.org/active_record_querying.html#group
哇哦,一直在用Relation,但是对其一知半解,今天总算理解它的意图。哈哈。3q
ActiveRecord::Relation用到了很多函数式编程的思想。
ActiveRecord::Relation
有需要,才会真的去做查询、装载成对象,这样可以避免没意义的执行。 也就是是函数式中的 lazy。lazy 可以带来很多好处,比如模块化,不需要对中间值进行计算,无限长的数组
每一次方法的调用,都返回一个新的ActiveRecord::Relation对象,而不是做修改。 所谓的 pure function。另外用到了 chain 这个 pattern,jQuery 采用了同样的设计方法。
想进一步了解的话,可以看下这篇文章 http://patshaughnessy.net/2014/9/17/20000-leagues-under-activerecord