---
# System prepended metadata

title: 'M220P: MongoDB for Python Developers'
tags: [python, mongodb]

---

# M220P: MongoDB for Python Developers

#### MongoDB Atlas Cluste

MFlix uses MongoDB to persist all of its data.

> 先在Atlas新建帳號後，新增免費的資料庫。
> 加入可讀寫的帳號
> 加入可進入的IP
> 連結方式選擇 shell 

Go to your cluster Overview -> Connect -> Connect Your Application. Select the option corresponding to your local MongoDB version and copy the mongo connection command.

先到資料的目錄裡
> #navigate to mflix-python directory
> $cd mflix-python

上傳資料到atlas的資料庫
>#import data into Atlas
```
$ mongorestore --drop --gzip --uri "mongodb+srv://renny:1234@mflix-vc4fl.gcp.mongodb.net/test?retryWrites=true&w=majority" data
```

Rename this file to .ini with the following command:

> $mv dotini_unix .ini  # on Unix
> $ren dotini_win .ini # on Windows

you need to edit it, you can open it from there:

> $vi .ini       # on Unix
> $notepad .ini  # on Windows

To start MFlix, run the following command:
> $pipenv install -r requirements.txt
> $python run.py

To open and run the notebooks in this course, navigate to the notebooks directory and launch the Jupyter notebook server:

> $cd mflix-python/notebooks
> $jupyter notebook

----
> Good tool :+1: 
> [Jupyter套件管理-搭配Pipenv](https://blog.thecodingday.com/2019/02/jupyter%E5%A5%97%E4%BB%B6%E7%AE%A1%E7%90%86-%E6%90%AD%E9%85%8Dpipenv/)
> 


### First write
> http://api.mongodb.com/python/current/tutorial.html

```python=
import pymongo
uri = "mongodb+srv://renny:1234@mflix-vc4fl.gcp.mongodb.net/test?retryWrites=true&w=majority"
client = pymongo.MongoClient(uri)
```
```python=
client.stats
```
> Database(MongoClient(host=['mflix-shard-00-02-vc4fl.gcp.mongodb.net:27017', 'mflix-shard-00-00-vc4fl.gcp.mongodb.net:27017', 'mflix-shard-00-01-vc4fl.gcp.mongodb.net:27017'], document_class=dict, tz_aware=False, connect=True, authsource='admin', replicaset='mflix-shard-0', ssl=True, retrywrites=True, w='majority'), 'stats')

```python=
client.list_database_names()
```
> ['mflix', 'admin', 'local']

```python=
mflix = client.mflix
mflix.list_collection_names()
```
> ['theaters', 'comments', 'users', 'sessions', 'movies']

```python=
movies = mflix.movies
movies.count_documents({})
```
> 45993

```python=
client = pymongo.MongoClient(uri, connectTimeoutMS=200, retryWrites=True)
client.stats
```
> Database(MongoClient(host=['mflix-shard-00-01-vc4fl.gcp.mongodb.net:27017', 'mflix-shard-00-02-vc4fl.gcp.mongodb.net:27017', 'mflix-shard-00-00-vc4fl.gcp.mongodb.net:27017'], document_class=dict, tz_aware=False, connect=True, authsource='admin', replicaset='mflix-shard-0', ssl=True, retrywrites=True, w='majority', connecttimeoutms=200), 'stats')


### first read
```python=
import pymongo
uri = "mongodb+srv://renny:1234@mflix-vc4fl.gcp.mongodb.net/test?retryWrites=true&w=majority"
client = pymongo.MongoClient(uri)
mflix = client.mflix
movies = mflix.movies
```
```
They are used to query documents in a collection.
This is true, both find() and find_one() are used for this purpose.
They accept a query predicate.
This is true, both methods allow a query predicate to target specific documents.
They accept a field projection.
This is true, both methods allow a field projection to suppress certain fields.
Incorrect :
They return a cursor.
The method find() will always return a cursor.
However, the find_one() method will never return a cursor. It will either return a document (in a Python dictionary) if one was found, or None if no document was not found.
```


在pymongo中使用find是得到Cursor，若想要像MongoDB shell中find操作

> db.test.find()
{ "_id" : ObjectId("5838531e0f3577fc9178b834"), "name" : "zhangsan" }

在pymongo中需要使用find_one方法而不是find方法:

> $print db.test.find_one()
{u'_id': ObjectId('5838531e0f3577fc9178b834'), u'name': u'zhangsan'}

> $ print db.test.find()
<pymongo.cursor.Cursor at 0x7f4ac789e450>
```python=
result = []
for x in db.test.find():
        result.append(x)
        print(result)
        
#[{u'_id': ObjectId('5838531e0f3577fc9178b834'), u'name': u'zhangsan'},...
```
其它方式
```python=
array = list(posts.find())#posts是我的collection
type(array)
#list
#list to datafram
import pandas as pd
df = pd.DataFrame(array)
```

from bson.json_util import dumps
dumps
```python=
print 'DATA:', repr(data)
print 'repr(data)             :', len(repr(data))
print 'dumps(data)            :', len(json.dumps(data))
print 'dumps(data, indent=2)  :', len(json.dumps(data, indent=4))
print 'dumps(data, separators):', len(json.dumps(data, separators=(',',':')))
```

```python=
cursor = movies.find( { "cast": "Salma Hayek" } )
from bson.json_util import dumps
print(dumps(cursor, indent=2))

cursor = movies.find( { "cast": "Salma Hayek" }, { "title": 1, "_id": 0 } )
print(dumps(cursor, indent=2))
```
#### Limiting
> And the find() method is always gonna return a cursor to us. But before assigning that cursor to a variable, we've transformed it with the limit() method, to make sure no more than 2 documents are returned by this cursor.
```python=
import pymongo
from bson.json_util import dumps
uri = "mongodb+srv://renny:1234@mflix-vc4fl.gcp.mongodb.net/test?retryWrites=true&w=majority"
client = pymongo.MongoClient(uri)
mflix = client.mflix
movies = mflix.movies

limited_cursor = movies.find(
    { "directors": "Sam Raimi" },
    { "_id": 0, "title": 1, "cast": 1 } 
).limit(2)

print(dumps(limited_cursor, indent=2))

pipeline = [
    { "$match": { "directors": "Sam Raimi" } },
    { "$project": { "_id": 0, "title": 1, "cast": 1 } },
    { "$limit": 2 }
]

limited_aggregation = movies.aggregate( pipeline )

print(dumps(limited_aggregation, indent=2))
```

#### Sorthing
> This is an example of the sort() (point) cursor method. sort() takes two parameters, the key we're sorting on and the sorting order. In this example we're sorting on year (point), in increasing (point) order.
> 
> ASCENDING and DESCENDING are values from the pymongo library to specify sort direction, but they're really just the integers 1 and -1.


```python=
from pymongo import DESCENDING, ASCENDING

sorted_cursor = movies.find(
    { "directors": "Sam Raimi" },
    { "_id": 0, "year": 1, "title": 1, "cast": 1 } 
).sort("year", DESCENDING)

print(dumps(sorted_cursor, indent=2))

# aggregate
pipeline = [
    { "$match": { "directors": "Sam Raimi" } },
    { "$project": { "_id": 0, "year": 1, "title": 1, "cast": 1 } },
    { "$sort": { "year": ASCENDING } }
]

sorted_aggregation = movies.aggregate( pipeline )

print(dumps(sorted_aggregation, indent=2))

#When sorting on two or more keys, the sort() method takes a single argument, an array of tuples. And each tuple has a key and a sort order.

sorted_cursor = movies.find(
    { "cast": "Tom Hanks" },
    { "_id": 0, "year": 1, "title": 1, "cast": 1 }
).sort([("year", ASCENDING), ("title", DESCENDING)])

print(dumps(sorted_cursor, indent=2))

#aggregate
pipeline = [
    { "$match": { "cast": "Tom Hanks" } },
    { "$project": { "_id": 0, "year": 1, "title": 1, "cast": 1 } },
    { "$sort": { "year": ASCENDING, "title": ASCENDING } }
]

sorted_aggregation = movies.aggregate( pipeline )

print(dumps(sorted_aggregation, indent=2))
```

#### Skipping
> The skip() method allows us to skip documents in a collection, so only documents we did not skip appear in the cursor. Because we only have 15 documents, skipping 14 of them should only leave us with 1.
```python=
pipeline = [
    { "$match": { "directors": "Sam Raimi" } },
    { "$project": { "_id": 0, "title": 1, "cast": 1 } },
    { "$count": "num_movies" }
]

sorted_aggregation = movies.aggregate( pipeline )

print(dumps(sorted_aggregation, indent=2))

'''
[
  {
    "num_movies": 15
  }
]
'''
```
```python=
skipped_sorted_cursor = movies.find(
    { "directors": "Sam Raimi" },
    { "_id": 0, "title": 1, "year": 1, "cast": 1 } 
).sort("year", 1).skip(14)

print(dumps(skipped_sorted_cursor, indent=2))

pipeline = [
    { "$match": { "directors": "Sam Raimi" } },
    { "$project": { "_id": 0, "year": 1, "title": 1, "cast": 1 } },
    { "$sort": { "year": ASCENDING } },
    { "$skip": 10 }
]

sorted_skipped_aggregation = movies.aggregate( pipeline )

print(dumps(sorted_skipped_aggregation, indent=2))
```
#### Summary
> .limit() == $limit
> .sort() == $sort
> .skip() == $skip

> So just to recap, in this lesson we covered some cursor methods and their aggregation equivalents. Remember that there won't always be a 1 to 1 mapping, because the aggregation framework can do a lot more than cursors can.
> 
> But these three methods exist as both aggregation stages and cursor methods.


#### Basic Aggregation
![](https://i.imgur.com/RXyt6w8.png)


Using Compass' Aggregation Pipeline Builder feature

![](https://i.imgur.com/R6lkzyY.png)

> $project	選擇集合中要的欄位，並可進行修改。
> $match	篩選操作，可以減少不需要的資料。
> $group	可以欄位進行分組。
> $unwind	拆開，可以將陣列欄位拆開成多個document。
> $sort	可針對欄位進行排序 。
> $limit	可針對回傳結果進行數量限制。
> $skip	略過前n筆資料，在開始回傳 。


#### Write Concerns
> https://medium.com/@sj82516/mongodb-isolation-%E8%88%87-transaction-132ab29731c2

> w: 0
> 
> This will not ask for an acknowledgement from any of the nodes in the set.
> 
> w: 1
> 
> This will only ask for an acknowledgement from one of the nodes in the set.
> 
> w: majority
> 
> This will ask for an acknowledgement from a majority of nodes in the set.


#### Basic Updates
Using faker create fake data
> https://github.com/joke2k/faker

###### tags: `mongodb` `python`