解释这个难题的最简单方法是举个例子:
假设我有两个 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
怎么回事?
答案 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
这不会从控制台中查询数据库。但是,您也不会从最后一行得到回复。您需要使用puts
或pp
(漂亮打印)来输出对象。相比之下,如果在使用此方法时运行命令foo.inspect
,您将注意到执行db的查询以产生所需的结果。