将一种方法链接到另一种方法如何改变原始方法
How can chaining one method onto another change the original method
解释这个难题的最简单方法是举个例子:
假设我有两个 Mongoid 模型,它们通过 has_many
关系关联:
博客 post
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
现在我创建了一个 Post,它在 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
class 返回,它看起来像一个数组,它包含两个评论。
现在,当我打开一个新的 IRB 控制台,并使用 Mongoid::Relations::Targets::Enumerable
class 实例 [=20] 的 criteria
属性查看用于加载这些评论的标准=],我得到这个输出:
post.comments.criteria #=>
=> #<Mongoid::Criteria
selector: {"post_id"=>BSON::ObjectId('571f315e5a4e491a6be39e02')}
options: {}
class: Comment
embedded: false>
这个例子怎么没有DB请求?这不是缓存问题,因为我打开了一个新的 IRB 控制台。
如何将 criteria
链接到 post.comments
上来改变 .comments
方法的作用?
我查看了 Mongoid 对 Mongoid::Relations::Targets::Enumerable
class (source on Github) 的实现,但找不到有关其工作原理的任何线索。
编辑
澄清问题:
这段代码,不查询数据库:
post.comments.criteria
但是这段代码可以:
foo = post.comments
post.comments.criteria
怎么会?
post.comments
就是您所说的 "Query object"。换句话说,它包含从数据库中获取所需数据的所有必要信息,但不包含数据本身。当您调用 post.comments.criteria
时,它只是显示查询对象为了发出请求而存储的相关参数。对象 ID 在这里可用,因为 post
已经存在于内存中。
如果您使用的是 sql 数据库,同样的原则也适用于 post.comments.to_sql
。
将评论转换为答案:
执行Mongoid::Relations::Targets::Enumerable#inspect时,检查方法对所有条目执行:
Inspection will just inspect the entries for nice array-style printing.
如果不使用查询方法,这是无法完成的。
OP 实际提出的问题与 IRB 控制台更相关。 IRB 控制台以触发 #inspect
的方式处理此响应对象,进而触发查询方法。对于 Mongoid(和 ActiveRecord)类,#inspect 方法执行查询以产生预期结果。
例如,如果在 IRB 控制台中运行,这将触发对数据库的查询:
>> foo = posts.comments
>> post.comments.criteria
foo
的响应将在 IRB 控制台尝试输出对象作为响应时触发查询方法。可以通过(至少)以下两种方式之一在 IRB 控制台中抑制对数据库的查询:
方法一:and nil
本质上,您可以在单个命令后缀 and nil
或 ;0
或类似的东西,以防止第一个命令被 IRB 的响应输出处理。这是因为 IRB 控制台只查看最后一个对象(如 ruby 方法的 return)。
>> foo = posts.comments and nil
>> post.comments.criteria
上面不会查询数据库,因为第一行IRB处理的输出是nil
而不是foo
变量
方法二:conf.echo = false
来源:
此方法可防止 IRB 控制台自动处理响应对象。
>> conf.echo = false
>> foo = posts.comments
>> post.comments.criteria
这不会从控制台中查询数据库。但是,您也不会从最后一行得到响应。您将需要使用 puts
或 pp
(漂亮的打印)来输出对象。相比之下,如果您在使用此方法时 运行 命令 foo.inspect
,您会注意到执行了对数据库的查询以产生所需的结果。
解释这个难题的最简单方法是举个例子:
假设我有两个 Mongoid 模型,它们通过 has_many
关系关联:
博客 post
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
现在我创建了一个 Post,它在 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
class 返回,它看起来像一个数组,它包含两个评论。
现在,当我打开一个新的 IRB 控制台,并使用 Mongoid::Relations::Targets::Enumerable
class 实例 [=20] 的 criteria
属性查看用于加载这些评论的标准=],我得到这个输出:
post.comments.criteria #=>
=> #<Mongoid::Criteria
selector: {"post_id"=>BSON::ObjectId('571f315e5a4e491a6be39e02')}
options: {}
class: Comment
embedded: false>
这个例子怎么没有DB请求?这不是缓存问题,因为我打开了一个新的 IRB 控制台。
如何将 criteria
链接到 post.comments
上来改变 .comments
方法的作用?
我查看了 Mongoid 对 Mongoid::Relations::Targets::Enumerable
class (source on Github) 的实现,但找不到有关其工作原理的任何线索。
编辑
澄清问题:
这段代码,不查询数据库:
post.comments.criteria
但是这段代码可以:
foo = post.comments
post.comments.criteria
怎么会?
post.comments
就是您所说的 "Query object"。换句话说,它包含从数据库中获取所需数据的所有必要信息,但不包含数据本身。当您调用 post.comments.criteria
时,它只是显示查询对象为了发出请求而存储的相关参数。对象 ID 在这里可用,因为 post
已经存在于内存中。
如果您使用的是 sql 数据库,同样的原则也适用于 post.comments.to_sql
。
将评论转换为答案:
执行Mongoid::Relations::Targets::Enumerable#inspect时,检查方法对所有条目执行:
Inspection will just inspect the entries for nice array-style printing.
如果不使用查询方法,这是无法完成的。
OP 实际提出的问题与 IRB 控制台更相关。 IRB 控制台以触发 #inspect
的方式处理此响应对象,进而触发查询方法。对于 Mongoid(和 ActiveRecord)类,#inspect 方法执行查询以产生预期结果。
例如,如果在 IRB 控制台中运行,这将触发对数据库的查询:
>> foo = posts.comments
>> post.comments.criteria
foo
的响应将在 IRB 控制台尝试输出对象作为响应时触发查询方法。可以通过(至少)以下两种方式之一在 IRB 控制台中抑制对数据库的查询:
方法一:and nil
本质上,您可以在单个命令后缀 and nil
或 ;0
或类似的东西,以防止第一个命令被 IRB 的响应输出处理。这是因为 IRB 控制台只查看最后一个对象(如 ruby 方法的 return)。
>> foo = posts.comments and nil
>> post.comments.criteria
上面不会查询数据库,因为第一行IRB处理的输出是nil
而不是foo
变量
方法二:conf.echo = false
来源:
此方法可防止 IRB 控制台自动处理响应对象。
>> conf.echo = false
>> foo = posts.comments
>> post.comments.criteria
这不会从控制台中查询数据库。但是,您也不会从最后一行得到响应。您将需要使用 puts
或 pp
(漂亮的打印)来输出对象。相比之下,如果您在使用此方法时 运行 命令 foo.inspect
,您会注意到执行了对数据库的查询以产生所需的结果。