Help us understand the problem. What is going on with this article?

MongoDB の null, empty を整理する

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 (存在しない) :x: :o: :x:
item02 null :o: :o: :x:
item03 empty(空文字) :o: :x: :o:
item04 "hoge" :o: :x: :x:

この情報を元に、それぞれを正確に抽出したい。

対象フィールドが存在しないドキュメントを抽出する

これは簡単で $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 の挙動をきちんと把握すれば、あとは条件を組み合わせればできることがわかる。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした