Pythonでmongodbを操作する~その3:update編~

この記事は最終更新日から1年以上が経過しています。

当記事の記載範囲

この記事ではPythonでmongodbに接続してから、update(SQLで言ってもupdate)の使い方について記載します。
内容としては以下になります。
1. update_one
2. update_many
3. replace_one
4. find_one_and_update
5. find_one_and_replace

mongodbの起動やpymongoのインストール方法については以下の記事をご覧いただければ幸いです。
https://qiita.com/bc_yuuuuuki/items/2b92598434f6cc320112

準備データ

mongodbの準備データは以下のとおりです。

> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB
test    0.000GB
> show collections
employee
log
salary
> db.salary.find()
{ "_id" : ObjectId("5d4acf84de925ae437e2c123"), "name" : "山田", "salary" : 300000 }
{ "_id" : ObjectId("5d4acf84de925ae437e2c124"), "name" : "佐藤", "salary" : 400000 }
{ "_id" : ObjectId("5d4acf84de925ae437e2c125"), "name" : "田中", "salary" : 500000 }
{ "_id" : ObjectId("5d4b81a4de925ae437e2c126"), "name" : "山田", "salary" : 500000 }

Pythonでupdateを使ってみる

まずはupdate_oneから使ってみます。
これから紹介する内容は基本的には同じ内容になっています。
第一引数のfileterに更新条件を設定し、第二引数のupdateやreplaceに更新する内容を設定します。

update_oneの使い方

MongoUpdateSample.py
from pymongo import MongoClient

class MongoUpdateSample(object):

    def __init__(self, dbName, collectionName):
        self.client = MongoClient()
        self.db = self.client[dbName]
        self.collection = self.db.get_collection(collectionName)

    def find(self, projection=None,filter=None, sort=None):
        return self.collection.find(projection=projection,filter=filter,sort=sort)

    def update_one(self, filter, update):
        return self.collection.update_one(filter,update)

    def update_many(self, filter, update):
        return self.collection.update_many(filter,update)

    def replace_one(self, filter, replacement):
        return self.collection.replace_one(filter, replacement)

    def find_one_and_replace(self, filter, replacement):
        return self.collection.find_one_and_replace(filter, replacement)

mongo = MongoUpdateSample('test', 'salary')
find = mongo.find()
for doc in find:
    print(doc)

update = mongo.update_one({'name':'山田'},{'$set':{'salary':600000}})
print('更新件数:' + str(update.matched_count))
find = mongo.find()
for doc in find:
    print(doc)

やっている内容としては更新前のデータを表示し、nameフィールドが'山田'の給料を600000に更新する。
更新完了後に更新件数と更新後のデータを表示するというものです。

実行結果

--------------------更新前--------------------
{'_id': ObjectId('5d4acf84de925ae437e2c123'), 'name': '山田', 'salary': 300000.0}
{'_id': ObjectId('5d4acf84de925ae437e2c124'), 'name': '佐藤', 'salary': 400000.0}
{'_id': ObjectId('5d4acf84de925ae437e2c125'), 'name': '田中', 'salary': 500000.0}
{'_id': ObjectId('5d4b81a4de925ae437e2c126'), 'name': '山田', 'salary': 500000.0}
更新件数:1
--------------------更新後--------------------
{'_id': ObjectId('5d4acf84de925ae437e2c123'), 'name': '山田', 'salary': 600000}
{'_id': ObjectId('5d4acf84de925ae437e2c124'), 'name': '佐藤', 'salary': 400000.0}
{'_id': ObjectId('5d4acf84de925ae437e2c125'), 'name': '田中', 'salary': 500000.0}
{'_id': ObjectId('5d4b81a4de925ae437e2c126'), 'name': '山田', 'salary': 500000.0}

はい。今回使ったのはupdate_oneなので'name'フィールドが'山田'となっているデータ2件のうち、1件が更新されています。
正直、今のところあまり利用場面が思い浮かんでいません。。
気になるのは'salary'フィールドの値ですね。
今回用意したデータはmongodbのコンソールから登録を行いました。
その際、'salary'フィールドには整数で値を設定しているのですが、pymongoのfindでデータを取得するとfloatになっていることが分かります。
また、pymongoのupdate_oneで更新した整数の値は、findで取得するとint型になっています。
このあたり、キャストなど注意する必要がありそうです。
ちなみに更新後のデータをmongodbのコンソールで確認すると全て整数で表示されています。

> db.salary.find()
{ "_id" : ObjectId("5d4acf84de925ae437e2c123"), "name" : "山田", "salary" : 600000 }
{ "_id" : ObjectId("5d4acf84de925ae437e2c124"), "name" : "佐藤", "salary" : 400000 }
{ "_id" : ObjectId("5d4acf84de925ae437e2c125"), "name" : "田中", "salary" : 500000 }
{ "_id" : ObjectId("5d4b81a4de925ae437e2c126"), "name" : "山田", "salary" : 500000 }

update_manyの使い方

update_manyの使い方はほとんどupdate_oneと同じです。
update_oneを呼び出している部分をupdate_manyに変更し、更新するパラメーターも変更しました。

(抜粋)MongoUpdateSample.py
mongo = MongoUpdateSample('test', 'salary')
find = mongo.find()
print('--------------------更新前--------------------')
for doc in find:
    print(doc)

update = mongo.update_many({'name':'山田'},{'$set':{'salary':7777777}})
print('更新件数:' + str(update.matched_count))
find = mongo.find()
print('--------------------更新後--------------------')
for doc in find:
    print(doc)

実行結果

--------------------更新前--------------------
{'_id': ObjectId('5d4acf84de925ae437e2c123'), 'name': '山田', 'salary': 600000}
{'_id': ObjectId('5d4acf84de925ae437e2c124'), 'name': '佐藤', 'salary': 400000.0}
{'_id': ObjectId('5d4acf84de925ae437e2c125'), 'name': '田中', 'salary': 500000.0}
{'_id': ObjectId('5d4b81a4de925ae437e2c126'), 'name': '山田', 'salary': 500000.0}
更新件数:2
--------------------更新後--------------------
{'_id': ObjectId('5d4acf84de925ae437e2c123'), 'name': '山田', 'salary': 7777777}
{'_id': ObjectId('5d4acf84de925ae437e2c124'), 'name': '佐藤', 'salary': 400000.0}
{'_id': ObjectId('5d4acf84de925ae437e2c125'), 'name': '田中', 'salary': 500000.0}
{'_id': ObjectId('5d4b81a4de925ae437e2c126'), 'name': '山田', 'salary': 7777777}

'name'フィールドが'山田'と一致する全てのデータの'salary'が更新されました。

replace_oneの使い方

これはドキュメント(RDBで言うところのレコード)を丸々入れ替えるという内容になります。
mongodbにより一意に採番される「_id(主キー)」以外のフィールドを新しいフィールドに入れ替えることができます。
SQLには無い構文ですね。早速やってみましょう。

(抜粋)MongoUpdateSample.py
mongo = MongoUpdateSample('test', 'salary')
find = mongo.find()
print('--------------------更新前--------------------')
for doc in find:
    print(doc)

update = mongo.replace_one({'name':'山田'},{'fullname':'山田 太郎','rank':5,'profession':'Engineer','position':'Manager'})
print('更新件数:' + str(update.matched_count))
find = mongo.find()
print('--------------------更新後--------------------')
for doc in find:
    print(doc)

実行結果

--------------------更新前--------------------
{'_id': ObjectId('5d4acf84de925ae437e2c123'), 'name': '山田', 'salary': 7777777}
{'_id': ObjectId('5d4acf84de925ae437e2c124'), 'name': '佐藤', 'salary': 400000.0}
{'_id': ObjectId('5d4acf84de925ae437e2c125'), 'name': '田中', 'salary': 500000.0}
{'_id': ObjectId('5d4b81a4de925ae437e2c126'), 'name': '山田', 'salary': 7777777}
更新件数:1
--------------------更新後--------------------
{'_id': ObjectId('5d4acf84de925ae437e2c123'), 'fullname': '山田\u3000太郎', 'rank': 5, 'profession': 'Engineer', 'position': 'Manager'}
{'_id': ObjectId('5d4acf84de925ae437e2c124'), 'name': '佐藤', 'salary': 400000.0}
{'_id': ObjectId('5d4acf84de925ae437e2c125'), 'name': '田中', 'salary': 500000.0}
{'_id': ObjectId('5d4b81a4de925ae437e2c126'), 'name': '山田', 'salary': 7777777}

全角スペースが文字コードとして表示されたのが解せないですが、'_id'が'5d4acf84de925ae437e2c123'のデータが設定した値に置き換わっていることを確認できました。

find_one_and_updateの使い方

これは名前のとおりfind_oneをしてupdateを行うというものです。
動きとしては関数の戻り値として、更新前のデータが取得できます。

(抜粋)MongoUpdateSample.py
mongo = MongoUpdateSample('test', 'salary')
find = mongo.find()
print('--------------------更新前--------------------')
for doc in find:
    print(doc)

update = mongo.find_one_and_update({'name':'山田'},{'$set':{'salary':800000}})
print(type(update))
print(update)
find = mongo.find()
print('--------------------更新後--------------------')
for doc in find:
    print(doc)

実行結果

--------------------更新前--------------------
{'_id': ObjectId('5d4acf84de925ae437e2c123'), 'name': '山田', 'salary': 300000.0}
{'_id': ObjectId('5d4acf84de925ae437e2c124'), 'name': '佐藤', 'salary': 400000.0}
{'_id': ObjectId('5d4acf84de925ae437e2c125'), 'name': '田中', 'salary': 500000.0}
{'_id': ObjectId('5d4b81a4de925ae437e2c126'), 'name': '山田', 'salary': 500000}
<class 'dict'>
{'_id': ObjectId('5d4acf84de925ae437e2c123'), 'name': '山田', 'salary': 300000.0}
--------------------更新後--------------------
{'_id': ObjectId('5d4acf84de925ae437e2c123'), 'name': '山田', 'salary': 800000}
{'_id': ObjectId('5d4acf84de925ae437e2c124'), 'name': '佐藤', 'salary': 400000.0}
{'_id': ObjectId('5d4acf84de925ae437e2c125'), 'name': '田中', 'salary': 500000.0}
{'_id': ObjectId('5d4b81a4de925ae437e2c126'), 'name': '山田', 'salary': 500000}

動きとしては、find_oneで見つかったデータに対してupdateを行うようなので、更新件数は一件だけとなるようです。

find_one_and_replaceの使い方

これも名前のとおり、find_oneをして、replaceを行うというものです。
find_one_and_updateと同じく、更新前のデータを取得できます。

(抜粋)MongoUpdateSample.py
mongo = MongoUpdateSample('test', 'salary')
find = mongo.find()
print('--------------------更新前--------------------')
for doc in find:
    print(doc)

update = mongo.find_one_and_replace({'name':'山田'},{'fullname':'山田 太郎','rank':5,'profession':'Engineer','position':'Manager'})
print(type(update))
print(update)
find = mongo.find()
print('--------------------更新後--------------------')
for doc in find:
    print(doc)

実行結果

--------------------更新前--------------------
{'_id': ObjectId('5d4acf84de925ae437e2c123'), 'name': '山田', 'salary': 800000}
{'_id': ObjectId('5d4acf84de925ae437e2c124'), 'name': '佐藤', 'salary': 400000.0}
{'_id': ObjectId('5d4acf84de925ae437e2c125'), 'name': '田中', 'salary': 500000.0}
{'_id': ObjectId('5d4b81a4de925ae437e2c126'), 'name': '山田', 'salary': 500000}
<class 'dict'>
{'_id': ObjectId('5d4acf84de925ae437e2c123'), 'name': '山田', 'salary': 800000}
--------------------更新後--------------------
{'_id': ObjectId('5d4acf84de925ae437e2c123'), 'fullname': '山田\u3000太郎', 'rank': 5, 'profession': 'Engineer', 'position': 'Manager'}
{'_id': ObjectId('5d4acf84de925ae437e2c124'), 'name': '佐藤', 'salary': 400000.0}
{'_id': ObjectId('5d4acf84de925ae437e2c125'), 'name': '田中', 'salary': 500000.0}
{'_id': ObjectId('5d4b81a4de925ae437e2c126'), 'name': '山田', 'salary': 500000}

replaceの前の情報を取得することが出来ました。

感想

更新のやり方が普通のSQLにはないreplaceなどのがあり、新鮮でした。
更新のやり方は完全にmongodbのコマンドとパラメーターの渡し方が同じで分かり易かったです。

関連記事

bc_yuuuuuki
ブロックチェーン/AI/Python/Golang/MongoDB/GraphDBなどを学習中です。 このサイトにおける掲載内容はあくまで私自身の見解であり、必ずしも私の所属団体・企業における立場、戦略、意見を代表するものではありません。
ユーザー登録して、Qiitaをもっと便利に使ってみませんか。
    コメント
    この記事にコメントはありません。
    あなたもコメントしてみませんか :)
    すでにアカウントを持っている方は