pymongoを使った様々な検索条件（AND／OR／部分一致／範囲検索）

#当記事の記載範囲
この記事ではPythonでmongodbに接続後、findを使った様々な検索方法を記載しています。
1. AND
2. OR
3. 部分一致
4. 前方一致
5. 後方一致
6. 範囲検索（BETWEEN）

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("5d4acf84de925ae437e2c124"), "name" : "佐藤", "salary" : 450000, "depId" : "A0001" }
{ "_id" : ObjectId("5d4acf84de925ae437e2c125"), "name" : "田中", "salary" : 500000, "depId" : "A0001" }
{ "_id" : ObjectId("5d4f814c950945628d663d95"), "name" : "加藤", "salary" : 400000, "depId" : "B0001" }
{ "_id" : ObjectId("5d4f814c950945628d663d96"), "name" : "松井", "salary" : 500000, "depId" : "C0001" }
{ "_id" : ObjectId("5d5179f76427f268db46299f"), "name" : "山田", "salary" : 500000, "depId" : "A0002" }
{ "_id" : ObjectId("5d5216ab61aa9ee6159ad4d8"), "name" : "山田", "salary" : 300000, "depId" : "C0002" }
```

#様々な検索方法
検索方法がmongodbのコマンドと結構違っているところがあったので、mongodbのコマンドと比較しながら記載します。
pymongoを使ったコードは以下の内容を使用しています。

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

class MongoFindSample(object):

    def __init__(self, dbName, collectionName):
        self.client = MongoClient()
        self.db = self.client[dbName]
        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)

    def count_documents(self, filter=None):
        return self.collection.count_documents(filter)


mongo = MongoFindSample('test', 'salary')
```


##ANDの使い方
collection"salary"からnameフィールドが"山田"　かつ　depIdフィールドが"C0002"のデータを取得します。
まずは、mongodbのコマンド

```
> db.salary.find({$and:[{'name':'山田'},{'depId':'C0002'}]})
{ "_id" : ObjectId("5d5216ab61aa9ee6159ad4d8"), "name" : "山田", "salary" : 300000, "depId" : "C0002" }
```
次はpymongoを使った書き方

``` MongoFindSample.py
result1 = mongo.find(filter={'$and':[{'name':'山田'},{'depId':'C0002'}]})
for doc in result1:
    print(doc)
```
実行結果

```
{'_id': ObjectId('5d5216ab61aa9ee6159ad4d8'), 'name': '山田', 'salary': 300000, 'depId': 'C0002'}
```
ANDについては$andをシングルクォーテーションで囲むか囲まないかという違いしかありませんでした。

##ORの使い方
collection"salary"からnameフィールドが"山田"　または　depIdフィールドが"B0001"のデータを取得します。
まずは、mongodbのコマンド

```
> db.salary.find({$or:[{'name':'山田'},{'depId':'B0001'}]})
{ "_id" : ObjectId("5d4f814c950945628d663d95"), "name" : "加藤", "salary" : 400000, "depId" : "B0001" }
{ "_id" : ObjectId("5d5179f76427f268db46299f"), "name" : "山田", "salary" : 500000, "depId" : "A0002" }
{ "_id" : ObjectId("5d5216ab61aa9ee6159ad4d8"), "name" : "山田", "salary" : 300000, "depId" : "C0002" }
```

pymongoを使った書き方

``` MongoFindSample.py
result1 = mongo.find(filter={'$or':[{'name':'山田'},{'depId':'B0001'}]})
for doc in result1:
    print(doc)
```
実行結果

```
{'_id': ObjectId('5d4f814c950945628d663d95'), 'name': '加藤', 'salary': 400000, 'depId': 'B0001'}
{'_id': ObjectId('5d5179f76427f268db46299f'), 'name': '山田', 'salary': 500000, 'depId': 'A0002'}
{'_id': ObjectId('5d5216ab61aa9ee6159ad4d8'), 'name': '山田', 'salary': 300000, 'depId': 'C0002'}
```
ORはANDと同じくシングルクォーテーションで囲むか囲まないかという違いだけでした。

##部分一致の使い方
nameに"田"を含むデータを取得してみます。

```
> db.salary.find({"name":/田/}))
{ "_id" : ObjectId("5d4acf84de925ae437e2c125"), "name" : "田中", "salary" : 500000, "depId" : "A0001" }
{ "_id" : ObjectId("5d5179f76427f268db46299f"), "name" : "山田", "salary" : 500000, "depId" : "A0002" }
{ "_id" : ObjectId("5d5216ab61aa9ee6159ad4d8"), "name" : "山田", "salary" : 300000, "depId" : "C0002" }
```
次はpymongoの使い方

``` MongoFindSample.py
result1 = mongo.find(filter={'name':{'$regex':'田'}})
for doc in result1:
    print(doc)
```
実行結果

```
{'_id': ObjectId('5d4acf84de925ae437e2c125'), 'name': '田中', 'salary': 500000, 'depId': 'A0001'}
{'_id': ObjectId('5d5179f76427f268db46299f'), 'name': '山田', 'salary': 500000, 'depId': 'A0002'}
{'_id': ObjectId('5d5216ab61aa9ee6159ad4d8'), 'name': '山田', 'salary': 300000, 'depId': 'C0002'}
```
これは書き方がかなり違いますね。
pymongoの方の書き方は"name"に対して正規表現を使いますというような書き方になっています。
また、正規表現のパターンの前後のスラッシュもpymongoでは不要なようです。

##前方一致の使い方
"depId"がAで始まるデータを取得してみます。
まずはmongodbのコマンド

```
> db.salary.find({"depId":/^A/})
{ "_id" : ObjectId("5d4acf84de925ae437e2c124"), "name" : "佐藤", "salary" : 450000, "depId" : "A0001" }
{ "_id" : ObjectId("5d4acf84de925ae437e2c125"), "name" : "田中", "salary" : 500000, "depId" : "A0001" }
{ "_id" : ObjectId("5d5179f76427f268db46299f"), "name" : "山田", "salary" : 500000, "depId" : "A0002" }
```
次はpymongoの使い方

``` MongoFindSample.py
result1 = mongo.find(filter={'depId':{'$regex':'^A'}})
for doc in result1:
    print(doc)
```
実行結果

```
{'_id': ObjectId('5d4acf84de925ae437e2c124'), 'name': '佐藤', 'salary': 450000, 'depId': 'A0001'}
{'_id': ObjectId('5d4acf84de925ae437e2c125'), 'name': '田中', 'salary': 500000, 'depId': 'A0001'}
{'_id': ObjectId('5d5179f76427f268db46299f'), 'name': '山田', 'salary': 500000, 'depId': 'A0002'}
```
前方一致の場合は^が最初に必要になります。

##後方一致の使い方
"depId"が"2"で終わるデータを取得してみます。
まずは、mongodbのコマンド

```
> db.salary.find({"depId":/2$/})
{ "_id" : ObjectId("5d5179f76427f268db46299f"), "name" : "山田", "salary" : 500000, "depId" : "A0002" }
{ "_id" : ObjectId("5d5216ab61aa9ee6159ad4d8"), "name" : "山田", "salary" : 300000, "depId" : "C0002" }
```
次はpymongoの使い方

``` MongoFindSample.py
result = mongo.find(filter={'depId':{'$regex':'2$'}})
for doc in result:
    print(doc)
```
実行結果

```
{'_id': ObjectId('5d5179f76427f268db46299f'), 'name': '山田', 'salary': 500000, 'depId': 'A0002'}
{'_id': ObjectId('5d5216ab61aa9ee6159ad4d8'), 'name': '山田', 'salary': 300000, 'depId': 'C0002'}
```
後方一致の場合は末尾に$マークが必要です。

##範囲検索（BETWEEN）
mongodbにはBETWEENが無いみたいなので、AND条件を組み合わせて行う必要があります。
salaryフィールドが300000以上、400000以下のデータを取得してみます。

```
> db.salary.find({$and:[{'salary':{$gte:300000}},{'salary':{$lte:400000}}]})
{ "_id" : ObjectId("5d4f814c950945628d663d95"), "name" : "加藤", "salary" : 400000, "depId" : "B0001" }
{ "_id" : ObjectId("5d5216ab61aa9ee6159ad4d8"), "name" : "山田", "salary" : 300000, "depId" : "C0002" }
```
次はpymongoの使い方

``` MongoFindSample.py
result1 = mongo.find(filter={'$and':[{'salary':{'$gte':300000}},{'salary':{'$lte':400000}}]})
for doc in result1:
    print(doc)
```
実行結果

```
{'_id': ObjectId('5d4f814c950945628d663d95'), 'name': '加藤', 'salary': 400000, 'depId': 'B0001'}
{'_id': ObjectId('5d5216ab61aa9ee6159ad4d8'), 'name': '山田', 'salary': 300000, 'depId': 'C0002'}
```

長いな。。
次の書き方でも同じ結果を得られました。

```
> db.salary.find({'salary':{$gte:300000,$lte:400000}})
{ "_id" : ObjectId("5d4f814c950945628d663d95"), "name" : "加藤", "salary" : 400000, "depId" : "B0001" }
{ "_id" : ObjectId("5d5216ab61aa9ee6159ad4d8"), "name" : "山田", "salary" : 300000, "depId" : "C0002" }
```
``` MongoFindSample.py
result1 = mongo.find(filter={'salary':{'$gte':300000,'$lte':400000}})
```
内部の動きまでは分かりませんが、こちらの方が書き方がシンプルで分かり易いですね。

#感想
部分一致以外はmongodbのコマンドと同じものをpymongoで使えました。
ANDのは複数の書き方が出来るみたいなので、違いを細かく調べる必要がありそうです。

#関連記事
- [Pythonでmongodbを操作する～その１～](https://qiita.com/bc_yuuuuuki/items/2b92598434f6cc320112)
- [Pythonでmongodbを操作する～その２：find編～](https://qiita.com/bc_yuuuuuki/items/23fa7ba63399e64cf321)
- [Pythonでmongodbを操作する～その３：update編～](https://qiita.com/bc_yuuuuuki/items/f80e62784aa4f6ec5834)
- [Pythonでmongodbを操作する～その４：insert編～](https://qiita.com/bc_yuuuuuki/items/07aab6cd5615e79bdf58)
- [Pythonでmongodbを操作する～その５：delete編～](https://qiita.com/bc_yuuuuuki/items/42fb4e328304e59b0eec)