整理 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
广告
将在 10 秒后关闭
bannerAds