如何将一种方法链接到另一种方法可以改变原始方法

时间:2016-04-27 13:18:24

标签: ruby-on-rails ruby mongoid

解释这个难题的最简单方法是举个例子:

假设我有两个 Mongoid 模型,它们通过has_many关系相关: 博客文章

class Post
   include Mongoid::Document
   field :body, type: String

   has_many :comments
end

及其评论

class Comment
   include Mongoid::Document
   field :text, type: String

   belongs_to :post
end

现在我创建一个在IRB中有两个注释的帖子,我试图通过关系加载它们。我启用了一些数据库日志记录,因此我可以看到查询的时间:

post.comments #=>
 2016-04-27 13:51:52.144 [DEBUG MONGODB | localhost:27017 | test.find | STARTED | {"find"=>"comments", "filter"=>{"post_id"=>BSON::ObjectId('571f315e5a4e491a6be39e02')}}]
 2016-04-27 13:51:52.150 [DEBUG MONGODB | localhost:27017 | test.find | SUCCEEDED | 0.000492643s]
 => [#<Comment _id: 571f315e5a4e491a6be39e03, text: 'great post' >, #<Comment _id: 571f315e5a4e491a6be39e12, text: 'this!' >]

因此注释从数据库加载并作为Mongoid::Relations::Targets::Enumerable类返回,它看起来像一个数组,它包含两个注释。

现在,当我打开一个全新的IRB控制台,并使用criteria类实例Mongoid::Relations::Targets::Enumerable的{​​{1}}属性查看用于加载这些注释的条件时,我得到了这个输出:

post.comments

为什么在这个例子中没有提出数据库请求?当我打开一个新的IRB控制台时,这不是一个缓存问题。

如何将post.comments.criteria #=> => #<Mongoid::Criteria selector: {"post_id"=>BSON::ObjectId('571f315e5a4e491a6be39e02')} options: {} class: Comment embedded: false> 链接到criteria更改post.comments方法的功能? 我查看了Mongoid对.comments课程(source on Github)的实施情况,但无法找到有关其工作原理的任何线索。

修改

澄清问题:

此代码不会查询数据库:

Mongoid::Relations::Targets::Enumerable

但是这段代码确实:

post.comments.criteria

怎么回事?

2 个答案:

答案 0 :(得分:1)

post.comments就是你所谓的“查询对象”。换句话说,它包含从数据库中获取所需数据的所有必要信息,但不包含数据本身。当您调用post.comments.criteria时,它只是显示查询对象存储的相关参数,以便发出请求。此处提供了对象ID,因为内存中已存在post

如果您使用的是SQL数据库,则同样的原则适用于post.comments.to_sql

答案 1 :(得分:1)

将评论转换为答案:

执行Mongoid::Relations::Targets::Enumerable#inspect时,将对所有条目执行inspect方法:

  

检查只会检查条目以获得良好的阵列式打印。

如果不使用查询方法,则无法完成此操作。

OP实际拥有的问题与IRB控制台更相关。 IRB控制台以触发#inspect的方式处理此响应对象,进而触发查询方法。对于Mongoid(和ActiveRecord)类,#inspect方法执行查询以产生预期结果。

例如,如果在IRB控制台中运行,这将触发对数据库的查询:

>> foo = posts.comments
>> post.comments.criteria

当IRB控制台尝试将对象作为响应输出时,foo的响应将触发查询方法。可以在(至少)以下两种方式之一的IRB控制台中抑制对db的查询:

方法1:and nil

基本上,您可以使用and nil;0或类似内容为单个命令添加后缀,以防止第一个命令被IRB的响应输出处理。这是因为IRB控制台只查看最后一个对象(就像ruby方法的返回一样)。

>> foo = posts.comments and nil
>> post.comments.criteria

以上不会查询数据库,因为IRB在第一行处理的输出是nil而不是foo变量。

方法2:conf.echo = false

来源:https://stackoverflow.com/a/13284331/1483788

此方法可防止IRB控制台自动处理响应对象。

>> conf.echo = false    
>> foo = posts.comments
>> post.comments.criteria

这不会从控制台中查询数据库。但是,您也不会从最后一行得到回复。您需要使用putspp(漂亮打印)来输出对象。相比之下,如果在使用此方法时运行命令foo.inspect,您将注意到执行db的查询以产生所需的结果。