整理 MongoDB/Mongoid 中的 exists 功能
MongoDB 的 $exists 的行为是什么?
假设有以下数据。
> db.items.find()
{ "_id" : ObjectId("5de50feab2e406088d483c44"), "name" : "item01", "color" : "red", "size" : "M" }
{ "_id" : ObjectId("5de51001b2e406088d483c45"), "name" : "item02", "color" : "blue", "size" : "S", "memo" : null }
{ "_id" : ObjectId("5de51012b2e406088d483c46"), "name" : "item03", "color" : "yellow", "size" : "L", "memo" : "" }
{ "_id" : ObjectId("5de51029b2e406088d483c47"), "name" : "item04", "color" : "green", "size" : "M", "memo" : "hoge" }
使用$exists来获取仅包含备忘录数据的情况如下。
> db.items.find({memo: {$exists: true}})
{ "_id" : ObjectId("5de51001b2e406088d483c45"), "name" : "item02", "color" : "blue", "size" : "S", "memo" : null }
{ "_id" : ObjectId("5de51012b2e406088d483c46"), "name" : "item03", "color" : "yellow", "size" : "L", "memo" : "" }
{ "_id" : ObjectId("5de51029b2e406088d483c47"), "name" : "item04", "color" : "green", "size" : "M", "memo" : "hoge" }
$exists 只评估目标字段是否存在。
因此,无论 memo 字段的值是 null 还是空,$exists 都被视为 true。
到目前为止,我们在讨论 MongoDB 的 $exists,并没有什么难的。
Mongoid 的 exists 行为
Mongoid 是用于在 Ruby 上操作 MongoDB 的对象文档映射(ODM)工具。
当然,Mongoid 也存在与 MongoDB 的 $exists 相对应的功能。
首先,我们定义一个类如下。
class Item
include Mongoid::Document
field :name
field :color
field :size
field :memo
end
只需执行以下代码即可获取所有记录。
(在这里,使用了Rails控制台)
irb(main):001:0> Item.each { |i| p "#{i._id}, #{i.name}, #{i.color}, #{i.size}, #{i.memo}" }; nil
MONGODB | [1] localhost:28001 #1 | sandbox.find | STARTED | {"find"=>"items", "filter"=>{}, "lsid"=>{"id"=><BSON::Binary:0x70346640265920 type=uuid data=0x672fb96b60a9434f...>}}
MONGODB | [1] localhost:28001 | sandbox.find | SUCCEEDED | 0.002s
"5de50feab2e406088d483c44, item01, red, M, "
"5de51001b2e406088d483c45, item02, blue, S, "
"5de51012b2e406088d483c46, item03, yellow, L, "
"5de51029b2e406088d483c47, item04, green, M, hoge"
=> nil
使用exists关键字,即可得到以下结果。
irb(main):002:0> Item.exists(memo: true).each { |i| p "#{i._id}, #{i.name}, #{i.color}, #{i.size}, #{i.memo}" }; nil
MONGODB | [2] localhost:28001 #1 | sandbox.find | STARTED | {"find"=>"items", "filter"=>{"memo"=>{"$exists"=>true}}, "lsid"=>{"id"=><BSON::Binary:0x70346640265920 type=uuid data=0x672fb96b60a9434f...>}}
MONGODB | [2] localhost:28001 | sandbox.find | SUCCEEDED | 0.002s
"5de51001b2e406088d483c45, item02, blue, S, "
"5de51012b2e406088d483c46, item03, yellow, L, "
"5de51029b2e406088d483c47, item04, green, M, hoge"
=> nil
可以用 Item.where(:memo.exists => true) 来代替 Item.exists(memo: true)。
到目前为止,与MongoDB的$exists具有相同的行为。
如果在Mongoid中设置了默认值,其行为是怎样的?
在模型中,可以为memo字段设置默认值。
class Item
include Mongoid::Document
field :name
field :color
field :size
field :memo, default: 'empty'
end
在Rails控制台上进行重新加载以加载模型的更改。
irb(main):003:0> reload!
Reloading...
=> true
如果全部获得,会变成以下这样。
irb(main):004:0> Item.each { |i| p "#{i._id}, #{i.name}, #{i.color}, #{i.size}, #{i.memo}" }; nil
MONGODB | [3] localhost:28001 #1 | sandbox.find | STARTED | {"find"=>"items", "filter"=>{}, "lsid"=>{"id"=><BSON::Binary:0x70346640265920 type=uuid data=0x672fb96b60a9434f...>}}
MONGODB | [3] localhost:28001 | sandbox.find | SUCCEEDED | 0.002s
"5de50feab2e406088d483c44, item01, red, M, empty"
"5de51001b2e406088d483c45, item02, blue, S, "
"5de51012b2e406088d483c46, item03, yellow, L, "
"5de51029b2e406088d483c47, item04, green, M, hoge"
=> nil
可以看到,item01的备忘录已经设置了默认值。
在这种情况下,如果像之前那样使用exists进行筛选,结果如下。
irb(main):005:0> Item.exists(memo: true).each { |i| p "#{i._id}, #{i.name}, #{i.color}, #{i.size}, #{i.memo}" }; nil
MONGODB | [4] localhost:28001 #1 | sandbox.find | STARTED | {"find"=>"items", "filter"=>{"memo"=>{"$exists"=>true}}, "lsid"=>{"id"=><BSON::Binary:0x70346640265920 type=uuid data=0x672fb96b60a9434f...>}}
MONGODB | [4] localhost:28001 | sandbox.find | SUCCEEDED | 0.002s
"5de51001b2e406088d483c45, item02, blue, S, "
"5de51012b2e406088d483c46, item03, yellow, L, "
"5de51029b2e406088d483c47, item04, green, M, hoge"
=> nil
物品01没有被输出。
换句话说,存在与否并不影响默认值的结果。
默认值只是用来在应用程序中设置当字段不存在时要处理的值。
借助这一点,应用程序可以区分该值是默认值还是已设置的值。
添加一个项目。
> db.items.insert({name: 'item05', color: 'orange', size: 'S', memo: 'empty'})
WriteResult({ "nInserted" : 1 })
使用Rails控制台进行全记录获取会如下所示。
irb(main):006:0> Item.each { |i| p "#{i._id}, #{i.name}, #{i.color}, #{i.size}, #{i.memo}" }; nil
MONGODB | [5] localhost:28001 #1 | sandbox.find | STARTED | {"find"=>"items", "filter"=>{}, "lsid"=>{"id"=><BSON::Binary:0x70346640265920 type=uuid data=0x672fb96b60a9434f...>}}
MONGODB | [5] localhost:28001 | sandbox.find | SUCCEEDED | 0.002s
"5de50feab2e406088d483c44, item01, red, M, empty"
"5de51001b2e406088d483c45, item02, blue, S, "
"5de51012b2e406088d483c46, item03, yellow, L, "
"5de51029b2e406088d483c47, item04, green, M, hoge"
"5de51b39b2e406088d483c4a, item05, orange, S, empty"
=> nil
从这个结果来看,我们无法分辨item01的memo和item05的memo是否相同。
irb(main):007:0> Item.find_by(name: 'item01').memo == Item.find_by(name: 'item05').memo
MONGODB | [6] localhost:28001 #1 | sandbox.find | STARTED | {"find"=>"items", "filter"=>{"name"=>"item01"}, "lsid"=>{"id"=><BSON::Binary:0x70346640265920 type=uuid data=0x672fb96b60a9434f...>}}
MONGODB | [6] localhost:28001 | sandbox.find | SUCCEEDED | 0.002s
MONGODB | [7] localhost:28001 #1 | sandbox.find | STARTED | {"find"=>"items", "filter"=>{"name"=>"item05"}, "lsid"=>{"id"=><BSON::Binary:0x70346640265920 type=uuid data=0x672fb96b60a9434f...>}}
MONGODB | [7] localhost:28001 | sandbox.find | SUCCEEDED | 0.001s
=> true
如果使用“存在”这个词,就可以区分出来。
irb(main):008:0> Item.exists(memo: true).each { |i| p "#{i._id}, #{i.name}, #{i.color}, #{i.size}, #{i.memo}" }; nil
MONGODB | [8] localhost:28001 #1 | sandbox.find | STARTED | {"find"=>"items", "filter"=>{"memo"=>{"$exists"=>true}}, "lsid"=>{"id"=><BSON::Binary:0x70346640265920 type=uuid data=0x672fb96b60a9434f...>}}
MONGODB | [8] localhost:28001 | sandbox.find | SUCCEEDED | 0.002s
"5de51001b2e406088d483c45, item02, blue, S, "
"5de51012b2e406088d483c46, item03, yellow, L, "
"5de51029b2e406088d483c47, item04, green, M, hoge"
"5de51b39b2e406088d483c4a, item05, orange, S, empty"
=> nil
如果想要逆转局势,请参考以下方法。
irb(main):009:0> Item.exists(memo: false).each { |i| p "#{i._id}, #{i.name}, #{i.color}, #{i.size}, #{i.memo}" }; nil
MONGODB | [9] localhost:28001 #1 | sandbox.find | STARTED | {"find"=>"items", "filter"=>{"memo"=>{"$exists"=>false}}, "lsid"=>{"id"=><BSON::Binary:0x70346640265920 type=uuid data=0x672fb96b60a9434f...>}}
MONGODB | [9] localhost:28001 | sandbox.find | SUCCEEDED | 0.003s
"5de50feab2e406088d483c44, item01, red, M, empty"
=> nil
或者,可以直接where(memo:’empty’)来仅获取item05。
irb(main):010:0> Item.where(memo: 'empty').each { |i| p "#{i._id}, #{i.name}, #{i.color}, #{i.size}, #{i.memo}" }; nil
MONGODB | [10] localhost:28001 #1 | sandbox.find | STARTED | {"find"=>"items", "filter"=>{"memo"=>"empty"}, "lsid"=>{"id"=><BSON::Binary:0x70346640265920 type=uuid data=0x672fb96b60a9434f...>}}
MONGODB | [10] localhost:28001 | sandbox.find | SUCCEEDED | 0.002s
"5de51b39b2e406088d483c4a, item05, orange, S, empty"
=> nil