Pythonでmongodbを操作する～その２：find編～

#当記事の記載範囲
この記事ではPythonでmongodbに接続してから、find（SQLで言うところのSELECT）の使い方について記載します。
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でmongodbのfindを使ってみる
早速、mongodbにfindOneとfindを実行してみます。

``` MongoFindSample.py
from pymongo import MongoClient
from pymongo import DESCENDING
from pymongo import ASCENDING

class MongoFindSample(object):

    def __init__(self, dbName, collectionName):
        self.client = MongoClient()
        self.db = self.client[dbName] #DB名を設定
        self.collection = self.db.get_collection(collectionName)

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

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

mongo = MongoFindSample('test', 'salary')
findOne = mongo.find_one()
print('-----------------find_One-----------------')
print(type(findOne))
print(findOne)

find = mongo.find()
print('-------------------find-------------------')
print(type(find))
for doc in find:
    print(doc)
```

実行結果

```
-----------------find_One-----------------
<class 'dict'>
{'_id': ObjectId('5d4acf84de925ae437e2c123'), 'name': '山田', 'salary': 300000.0}
-------------------find-------------------
<class 'pymongo.cursor.Cursor'>
{'_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}
```

mongoDBだと以下のコマンドと同じ結果が得られました。

```
> db.salary.findOne()
{
        "_id" : ObjectId("5d4acf84de925ae437e2c123"),
        "name" : "山田",
        "salary" : 300000
}
> 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 }
```

findOneは実行結果が**dict**形式でfindだと**pymongo.cursor.Cursor**クラス形式で取得出来ることが分かりました。
この**Cursor**形式はでは色々なデータの取り出し方法があります。

**インデックスを指定した取得方法**

``` （抜粋）MongoFindSample.py
find = mongo.find()
print('-------------------find-------------------')
print(type(find))
for i in range(find.count()):
    print(find[i])
```

実行結果

```
-------------------find-------------------
<class 'pymongo.cursor.Cursor'>
c:\Users\crypr\Documents\study\python\MongoFindSample.py:30: DeprecationWarning: count is deprecated. Use Collection.count_documents instead.
  for i in range(find.count()):
{'_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}
```

どうもCursorクラスのcount()は非推奨みたいです。
Collection.count_documentsを使うようにとのことです。
取得したデータを１件ずつ処理する場合はこのやり方は向いてなさそうですね。
インデックスを指定して処理が出来るということだけ押さえておけばいいかなと思います。

**Cursor.next()を使用した取得方法**

``` （抜粋）MongoFindSample.py
try:
    doc = find.next()
    while doc != None:
        print(doc)
        doc = find.next()
except StopIteration:
    pass
```
実行結果

```
-------------------find-------------------
{'_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}
```

JavaのIteratorのhasNextみたいな関数を見つけることが出来ず、こんな書き方になりました。
もっと良い方法ありそう。。

##条件を指定して取得する
SQLで言うところのWHEREの条件を指定して取得します。

``` （抜粋）MongoFindSample.py
find = mongo.find(filter={'name':'山田'})
for doc in find:
    print(doc)
```
実行結果

```
{'_id': ObjectId('5d4acf84de925ae437e2c123'), 'name': '山田', 'salary': 300000.0}
{'_id': ObjectId('5d4b81a4de925ae437e2c126'), 'name': '山田', 'salary': 500000.0}
```

**複数の条件を指定する**
名前が"山田"で給料が"400,000"以上のデータを取得してみます。

``` （抜粋）MongoFindSample.py
find = mongo.find(filter={'name':'山田','salary':{'$gte':400000}})
for doc in find:
    print(doc)
```
実行結果

```
{'_id': ObjectId('5d4b81a4de925ae437e2c126'), 'name': '山田', 'salary': 500000.0}
```

##並べ替えをする
SQLで言うところのORDER BYをしてみます。
試しに、給料の降順、名前の昇順でデータを取得します。

``` （抜粋）MongoFindSample.py
find = mongo.find(sort=[('salary',DESCENDING),('name',ASCENDING)])
for doc in find:
    print(doc)
```
実行結果

```
{'_id': ObjectId('5d4b81a4de925ae437e2c126'), 'name': '山田', 'salary': 500000.0}
{'_id': ObjectId('5d4acf84de925ae437e2c125'), 'name': '田中', 'salary': 500000.0}
{'_id': ObjectId('5d4acf84de925ae437e2c124'), 'name': '佐藤', 'salary': 400000.0}
{'_id': ObjectId('5d4acf84de925ae437e2c123'), 'name': '山田', 'salary': 300000.0}
```

**coursolを使って並べ替えをする**
データ取得後にcursor側で並べ変えをすることも出来るようです。

``` （抜粋）MongoFindSample.py
find = mongo.find()
for doc in find.sort([('salary',DESCENDING),('name',ASCENDING)]):
    print(doc)
```
実行結果

```
{'_id': ObjectId('5d4b81a4de925ae437e2c126'), 'name': '山田', 'salary': 500000.0}
{'_id': ObjectId('5d4acf84de925ae437e2c125'), 'name': '田中', 'salary': 500000.0}
{'_id': ObjectId('5d4acf84de925ae437e2c124'), 'name': '佐藤', 'salary': 400000.0}
{'_id': ObjectId('5d4acf84de925ae437e2c123'), 'name': '山田', 'salary': 300000.0}
```

##フィールド名を指定して取得する
最後に指定したフィールドのみを取得します。
名前と給料のみを取得します。

``` （抜粋）MongoFindSample.py
find = mongo.find(projection={'_id':0, 'name':1, 'salary':1},sort=[('salary',DESCENDING),('name',ASCENDING)])
for doc in find:
    print(doc)
```


実行結果

```
{'name': '山田', 'salary': 500000.0}
{'name': '田中', 'salary': 500000.0}
{'name': '佐藤', 'salary': 400000.0}
{'name': '山田', 'salary': 300000.0}
```


#感想
Pythonで一通りfindを使用してデータ取得が出来ました。
かなり簡単に出来たなという印象です。