https://qiita.com/risou/items/d1d0b0173039d68ed81c で $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" }
それぞれのドキュメントの memo
は以下のようになる。
name | memo |
---|---|
item01 | (存在しない) |
item02 | null |
item03 | empty(空文字) |
item04 | "hoge" |
memo
が null のドキュメントを取得する
普通に条件に memo: null
を追加すると以下のようになる。
> db.items.find({memo: null})
{ "_id" : ObjectId("5de50feab2e406088d483c44"), "name" : "item01", "color" : "red", "size" : "M" }
{ "_id" : ObjectId("5de51001b2e406088d483c45"), "name" : "item02", "color" : "blue", "size" : "S", "memo" : null }
null を指定すると 対象フィールドが存在しないドキュメント・対象フィールドの値が null のドキュメント の両方がとれる。
memo
が empty のドキュメントを取得する
そのまま指定すると意図通りの挙動になる。
> db.items.find({memo: ""})
{ "_id" : ObjectId("5de51012b2e406088d483c46"), "name" : "item03", "color" : "yellow", "size" : "L", "memo" : "" }
ここまでの情報を整理すると以下のようになる。
name | memo | $exists: true |
null | "" |
---|---|---|---|---|
item01 | (存在しない) | |||
item02 | null | |||
item03 | empty(空文字) | |||
item04 | "hoge" |
この情報を元に、それぞれを正確に抽出したい。
対象フィールドが存在しないドキュメントを抽出する
これは簡単で $exists: false
を設定してあげれば良い。
> db.items.find({memo: {$exists: false}})
{ "_id" : ObjectId("5de50feab2e406088d483c44"), "name" : "item01", "color" : "red", "size" : "M" }
対象フィールドが null のドキュメントを抽出する
先に書いたように memo: null
では対応できないので $and
を使って条件を絞る必要がある。
> db.items.find({$and: [{memo: {$exists: true}}, {memo: null}]})
{ "_id" : ObjectId("5de51001b2e406088d483c45"), "name" : "item02", "color" : "blue", "size" : "S", "memo" : null }
対象フィールドが空のドキュメントを抽出する
これは先に書いたように memo: ''
でいける。
> db.items.find({memo: ''})
{ "_id" : ObjectId("5de51012b2e406088d483c46"), "name" : "item03", "color" : "yellow", "size" : "L", "memo" : "" }
対象フィールドが null でも空でもないドキュメントを抽出する
Ruby の present?
のような条件だ。
これも $and
を使って実現できる。
> db.items.find({$and: [{memo: {$exists: true}}, {memo: {$ne: null}}, {memo: {$ne: ''}}]})
{ "_id" : ObjectId("5de51029b2e406088d483c47"), "name" : "item04", "color" : "green", "size" : "M", "memo" : "hoge" }
対象フィールドが存在しないか値が null か空のドキュメントを抽出する
こちらは Ruby の blank?
のような条件。
先の条件の逆なので難しくない。
> db.items.find({$or: [{memo: null}, {memo: ''}]})
{ "_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" : "" }
上で整理した $exists
と null の挙動をきちんと把握すれば、あとは条件を組み合わせればできることがわかる。