Python連接MongoDB操作
本教程的目的是介紹如何使用Python連接MongoDB數據庫,並通過PyMongo操作MongoDB數據庫。
1.安裝PyMongo
注意:請勿安裝「bson」軟件包。 PyMongo配有自己的bson包; 執行「
pip install bson
」或「easy_install bson
」則會安裝與PyMongo不兼容的第三方軟件包。
使用pip安裝
我們建議在所有平臺上使用pip來安裝pymongo:
C:\Users\Administrator>python -m pip install pymongo
Collecting pymongo
Downloading pymongo-3.4.0.tar.gz (583kB)
... ....
Installing collected packages: pymongo
Running setup.py install for pymongo ... done
Successfully installed pymongo-3.4.0
要獲得pymongo的特定版本:
$ python -m pip install pymongo==3.1.1
要升級pymongo的版本:
$ python -m pip install --upgrade pymongo
Python版本依賴
PyMongo支持CPython 2.6,2.7,3.3+,PyPy和PyPy3。
GSSAPI和TLS的可選依賴關係:
GSSAPI認證需要Windows上的Unix或WinKerberos上的pykerberos。PyMongo可以自動安裝正確的依賴關係:
$ python -m pip install pymongo[gssapi]
2.使用MongoClient建立連接
使用PyMongo時,第一步是運行 mongod 實例創建一個MongoClient。如下:
from pymongo import MongoClient
client = MongoClient()
上述代碼將連接默認主機和端口。 也可以明確指定主機和端口,如下所示:
from pymongo import MongoClient
#client = MongoClient()
client = MongoClient('localhost', 27017)
或使用MongoDB URI格式:
client = MongoClient('mongodb://localhost:27017/')
3.獲取數據庫
MongoDB的一個實例可以支持多個獨立的數據庫。 在使用PyMongo時,可以使用MongoClient實例上的屬性的方式來訪問數據庫:
db = client.pythondb
如果數據庫名稱使用屬性方式訪問無法正常工作(如:python-db
),則可以使用字典樣式訪問:
db = client['python-db']
4.獲取集合
集合是存儲在MongoDB中的一組文檔,可以類似於關係數據庫中的表。 在PyMongo中獲取集合的工作方式與獲取數據庫相同:
collection = db.python_collection
或(使用字典方式訪問):
collection = db['python-collection']
MongoDB中關於集合(和數據庫)的一個重要注意事項是它們是懶創建的 - 上述任何命令都沒有在MongoDB服務器上實際執行任何操作。當第一個文檔插入集合時才創建集合和數據庫。
集合是存儲在MongoDB中的一組文檔,可以被認爲大致相當於關係數據庫中的表。 在PyMongo中獲取集合的工作方式與獲取數據庫相同:
5.文檔
MongoDB中的數據使用JSON方式來表示文檔(並存儲)。 在PyMongo中使用字典來表示文檔。例如,以下字典可能用於表示博客文章:
import datetime
from pymongo import MongoClient
client = MongoClient()
post = {"author": "Mike",
"text": "My first blog post!",
"tags": ["mongodb", "python", "pymongo"],
"date": datetime.datetime.utcnow()}
`
6.插入文檔
要將文檔插入到集合中,可以使用insert_one()
方法:
#!/usr/bin/python3
#coding=utf-8
import datetime
from pymongo import MongoClient
client = MongoClient()
db = client.pythondb
post = {"author": "Maxsu",
"text": "My first blog post!",
"tags": ["mongodb", "python", "pymongo"],
"date": datetime.datetime.utcnow()}
posts = db.posts
post_id = posts.insert_one(post).inserted_id
print ("post id is ", post_id)
執行上面代碼,得到以下結果 -
post id is 595965fe4959eb09c4451091
插入文檔時,如果文檔尚未包含「_id
」鍵,則會自動添加「_id
」。 「_id
」的值在集合中必須是唯一的。 insert_one()
返回一個InsertOneResult
的實例。 有關「_id
」的更多信息,請參閱有關_id文檔。
插入第一個文檔後,實際上已經在服務器上創建了帖子(posts
)集合。可以列出數據庫中的所有集合:
#!/usr/bin/python3
#coding=utf-8
import datetime
from pymongo import MongoClient
client = MongoClient()
db = client.pythondb
"""
post = {"author": "Maxsu",
"text": "My first blog post!",
"tags": ["mongodb", "python", "pymongo"],
"date": datetime.datetime.utcnow()}
posts = db.posts
post_id = posts.insert_one(post).inserted_id
print ("post id is ", post_id)
"""
cur_collection = db.collection_names(include_system_collections=False)
print("cur_collection is :", cur_collection)
執行上面代碼,得到以下結果 -
cur_collection is : ['posts']
7.使用find_one()獲取單個文檔
MongoDB中執行的最基本的查詢類型是find_one()
。 此方法返回與查詢匹配的單個文檔(如果沒有匹配,則返回None
)。 當知道只有一個匹配的文檔,或只對第一個匹配感興趣時則可考慮使用find_one()
方法。下面示例中使用find_one()
從帖子(posts
)集中獲取第一個文檔:
#!/usr/bin/python3
#coding=utf-8
import datetime
import pprint
from pymongo import MongoClient
client = MongoClient()
db = client.pythondb
'''
post = {"author": "Maxsu",
"text": "My first blog post!",
"tags": ["mongodb", "python", "pymongo"],
"date": datetime.datetime.utcnow()}
'''
posts = db.posts
#post_id = posts.insert_one(post).inserted_id
#print ("post id is ", post_id)
pprint.pprint(posts.find_one())
執行上面代碼,得到以下結果 -
{'_id': ObjectId('595965fe4959eb09c4451091'),
'author': 'Maxsu',
'date': datetime.datetime(2017, 7, 2, 21, 30, 38, 402000),
'tags': ['mongodb', 'python', 'pymongo'],
'text': 'My first blog post!'}
結果是匹配之前插入的字典格式(Json)。注意: 返回的文檔包含一個「_id
」,它是在插入時自動添加的。
find_one()
方法還支持查詢結果文檔必須匹配的特定元素。要查詢作者是「Maxsu
」的文檔,可以指定查詢的條件,如下所示:
#!/usr/bin/python3
#coding=utf-8
import datetime
import pprint
from pymongo import MongoClient
client = MongoClient()
db = client.pythondb
post = {"author": "Minsu",
"text": "This blog post belong to Minsu!",
"tags": ["MySQL", "Oracle", "pymongo"],
"date": datetime.datetime.utcnow()}
posts = db.posts
post_id = posts.insert_one(post).inserted_id
post = posts.find_one({"author": "Maxsu"})
pprint.pprint(post)
#print (post)
執行上面代碼,得到以下結果 -
{'_id': ObjectId('595965fe4959eb09c4451091'),
'author': 'Maxsu',
'date': datetime.datetime(2017, 7, 2, 21, 30, 38, 402000),
'tags': ['mongodb', 'python', 'pymongo'],
'text': 'My first blog post!'}
8.通過ObjectId查詢
也可以通過它的_id
找到一個帖子(post
),下面的示例子中演示如何根據給定的一個ObjectId
查詢數據:
#!/usr/bin/python3
#coding=utf-8
import datetime
import pprint
from pymongo import MongoClient
client = MongoClient()
db = client.pythondb
post = {"_id": 100,
"author": "Kuber",
"text": "This is is my first post!",
"tags": ["Docker", "Shell", "pymongo"],
"date": datetime.datetime.utcnow()}
posts = db.posts
post_id = posts.insert_one(post).inserted_id
print("post_id is :", post_id)
post = posts.find_one({"_id": post_id})
print("Find By Post ID:")
pprint.pprint(post)
#print (post)
執行上面代碼,得到以下結果 -
post_id is : 100
Find By Post ID:
{'_id': 100,
'author': 'Kuber',
'date': datetime.datetime(2017, 7, 3, 14, 14, 8, 28000),
'tags': ['Docker', 'Shell', 'pymongo'],
'text': 'This is is my first post!'}
Web應用程序中的常見任務是從請求URL獲取ObjectId並找到匹配的文檔。 在這種情況下,必須將ObjectId
從一個字符串轉換到find_one()
:
from bson.objectid import ObjectId
# The web framework gets post_id from the URL and passes it as a string
def get(post_id):
# Convert from string to ObjectId:
document = client.db.collection.find_one({'_id': ObjectId(post_id)})
9.關於Unicode字符串的註釋
您可能已經注意到,我們先前存儲的常規Python字符串在從服務器檢索時看起來是不同的(例如,u’Mike而不是「Mike」)。一個簡短的解釋是有序的字符串。
MongoDB以BSON格式存儲數據。BSON字符串是UTF-8編碼的,所以PyMongo必須確保它存儲的任何字符串只包含有效的UTF-8
數據。 常規字符串(<type'str'>
)被驗證並保存不變。 Unicode字符串(<type'unicode'>
)首先被編碼爲UTF-8
。 我們的示例字符串在Python shell中表示爲u'Mike
而不是「Mike」
的原因是PyMongo將每個BSON字符串解碼爲Python unicode
字符串,而不是常規str。
10.批量插入
爲了執行更復雜一些的查詢,我們再插入一些文檔。 除了插入單個文檔外,還可以通過將列表作爲第一個參數傳遞給insert_many()
來執行批量插入操作。 這將在列表中插入每個文檔,只向服務器發送一個命令:
#!/usr/bin/python3
#coding=utf-8
import datetime
import pprint
from pymongo import MongoClient
client = MongoClient()
db = client.pythondb
new_posts = [{"_id": 1000,
"author": "Curry",
"text": "Another post!",
"tags": ["bulk", "insert"],
"date": datetime.datetime(2017, 11, 12, 11, 14)},
{"_id": 1001,"author": "Maxsu",
"title": "MongoDB is fun",
"text": "and pretty easy too!",
"date": datetime.datetime(2019, 11, 10, 10, 45)}]
posts = db.posts
result = posts.insert_many(new_posts)
print("Bulk Inserts Result is :", result.inserted_ids)
#print (post)
執行上面代碼,得到以下結果 -
Bulk Inserts Result is : [1000, 1001]
有幾個有趣的事情要注意這個例子:
-
insert_many()
的結果現在返回兩個ObjectId
實例,每個ID表示插入的一個文檔。 -
new_posts[1]
具有與其他帖子不同的「形狀」(數據結構) - 沒有「tags
」字段,添加了一個新字段「title
」。MongoDB是無模式的,表示的就是這個意思。
11.查詢多個文檔
要查詢獲得超過單個文檔作爲查詢的結果,可使用find()
方法。find()
返回一個Cursor
實例,它允許遍歷所有匹配的文檔。如下示例,遍歷帖子集合中的每個文檔:
#!/usr/bin/python3
#coding=utf-8
import datetime
import pprint
from pymongo import MongoClient
client = MongoClient()
db = client.pythondb
posts = db.posts
for post in posts.find():
pprint.pprint(post)
執行上面代碼,得到以下結果 -
{'_id': ObjectId('595965fe4959eb09c4451091'),
'author': 'Maxsu',
'date': datetime.datetime(2017, 7, 2, 21, 30, 38, 402000),
'tags': ['mongodb', 'python', 'pymongo'],
'text': 'My first blog post!'}
{'_id': 100,
'author': 'Kuber',
'date': datetime.datetime(2017, 7, 3, 14, 14, 8, 28000),
'tags': ['Docker', 'Shell', 'pymongo'],
'text': 'This is is my first post!'}
{'_id': 1000,
'author': 'Curry',
'date': datetime.datetime(2017, 11, 12, 11, 14),
'tags': ['bulk', 'insert'],
'text': 'Another post!'}
{'_id': 1001,
'author': 'Maxsu',
'date': datetime.datetime(2019, 11, 10, 10, 45),
'text': 'and pretty easy too!',
'title': 'MongoDB is fun'}
類似使用find_one()
一樣,我們可以將文檔傳遞給find()
來限制返回的結果。 在這裏,只希望得到作者是「Maxsu」的文檔:
#!/usr/bin/python3
#coding=utf-8
import datetime
import pprint
from pymongo import MongoClient
client = MongoClient()
db = client.pythondb
posts = db.posts
for post in posts.find({"author": "Maxsu"}):
pprint.pprint(post)
執行上面的代碼,得到以下結果 -
{'_id': ObjectId('595965fe4959eb09c4451091'),
'author': 'Maxsu',
'date': datetime.datetime(2017, 7, 2, 21, 30, 38, 402000),
'tags': ['mongodb', 'python', 'pymongo'],
'text': 'My first blog post!'}
{'_id': 1001,
'author': 'Maxsu',
'date': datetime.datetime(2019, 11, 10, 10, 45),
'text': 'and pretty easy too!',
'title': 'MongoDB is fun'}
12.計數統計
如果只想知道有多少文檔匹配查詢,可以執行count()
方法操作,而不是一個完整的查詢。 可以得到一個集合中的所有文檔的計數:
#!/usr/bin/python3
#coding=utf-8
import datetime
import pprint
from pymongo import MongoClient
client = MongoClient()
db = client.pythondb
posts = db.posts
print("posts count is = ", posts.count())
print("posts's author is Maxsu count is =", posts.find({"author": "Maxsu"}).count())
執行上面代碼,得到以下結果 -
posts count is = 4
posts's author is Maxsu count is = 2
13.範圍查詢
MongoDB支持許多不同類型的高級查詢。例如,可以執行一個查詢,將結果限制在比特定日期更早的帖子,而且還可以按作者對結果進行排序:
#!/usr/bin/python3
#coding=utf-8
import datetime
import pprint
from pymongo import MongoClient
client = MongoClient()
db = client.pythondb
posts = db.posts
d = datetime.datetime(2019, 11, 12, 12)
for post in posts.find({"date": {"$lt": d}}).sort("author"):
pprint.pprint(post)
這裏使用特殊的「$lt
」運算符做範圍查詢,並且還可以調用sort()
來按作者對結果進行排序。
14.索引
添加索引可以幫助加速某些查詢,並且還可以添加額外的功能來查詢和存儲文檔。在這個例子中,將演示如何在一個鍵上創建一個唯一的索引,該索引將拒絕已經存在值的文檔插入。
首先,我們創建索引:
result = db.profiles.create_index([('user_id', pymongo.ASCENDING)], unique=True)
sorted(list(db.profiles.index_information()))
請注意,現在有兩個索引:一個是MongoDB自動創建的在_id
索引,另一個是剛剛創建在user_id
上的索引。
現在來設置一些用戶配置文件:
user_profiles = [{'user_id': 211, 'name': 'Luke'},{'user_id': 212, 'name': 'Ziltoid'}]
result = db.profiles.insert_many(user_profiles)
該索引將阻止 user_id
已經在集合中的文檔插入:
new_profile = {'user_id': 213, 'name': 'Drew'}
duplicate_profile = {'user_id': 212, 'name': 'Tommy'}
result = db.profiles.insert_one(new_profile) # This is fine.
result = db.profiles.insert_one(duplicate_profile)
## 出現錯誤提示...
Traceback (most recent call last):
DuplicateKeyError: E11000 duplicate key error index: test_database.profiles.$user_id_1 dup key: { : 212 }