- MongoEngine 教程
- MongoEngine - 首頁
- MongoEngine - MongoDB
- MongoEngine - MongoDB Compass
- MongoEngine - 物件文件對映器
- MongoEngine - 安裝
- MongoEngine - 連線到 MongoDB 資料庫
- MongoEngine - 文件類
- MongoEngine - 動態模式
- MongoEngine - 欄位
- MongoEngine - 新增/刪除文件
- MongoEngine - 查詢資料庫
- MongoEngine - 過濾器
- MongoEngine - 查詢運算子
- MongoEngine - QuerySet 方法
- MongoEngine - 排序
- MongoEngine - 自定義 QuerySet
- MongoEngine - 索引
- MongoEngine - 聚合
- MongoEngine - 高階查詢
- MongoEngine - 文件繼承
- MongoEngine - 原子更新
- MongoEngine - Javascript
- MongoEngine - GridFS
- MongoEngine - 訊號
- MongoEngine - 文字搜尋
- MongoEngine - 擴充套件
- MongoEngine 有用資源
- MongoEngine 快速指南
- MongoEngine - 有用資源
- MongoEngine - 討論
MongoEngine 快速指南
MongoEngine - MongoDB
在過去十年中,NoSQL 資料庫的普及率不斷上升。在當今即時 Web 應用程式的世界中,移動和嵌入式裝置產生了海量資料。傳統的關聯資料庫(如 Oracle、MySQL 等)不適合處理字串。由於這些資料庫具有固定且預定義的模式,並且不可擴充套件,因此處理此類資料也很困難。NoSQL 資料庫具有靈活的模式,並以分散式方式儲存在大量社群伺服器上。
NoSQL 資料庫根據資料的組織方式進行分類。MongoDB 是一種流行的文件儲存 NoSQL 資料庫。MongoDB 資料庫的基本組成部分稱為文件。文件是以 JSON 格式儲存的鍵值對集合。多個文件儲存在一個集合中。集合可以被認為類似於任何關係資料庫中的表,而文件類似於表中的一行。但是,需要注意的是,由於 MongoDB 是無模式的,因此集合中每個文件的鍵值對數量不必相同。
MongoDB 由 MongoDB Inc. 開發。它是一個通用的分散式基於文件的資料庫。它提供企業版和社群版。Windows 作業系統的社群版的最新版本可以從 https://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2012plus-4.2.6-signed.msi 下載。
將 MongoDB 安裝到您選擇的資料夾中,並使用以下命令啟動伺服器:
D:\mongodb\bin>mongod
伺服器現在已準備好接收 27017 埠上的傳入連線請求。MongoDB 資料庫儲存在 bin/data 目錄中。此位置可以透過上述命令中的 –dbpath 選項更改。
在另一個命令終端中,使用以下命令啟動 MongoDB 控制檯:
D:\mongodb\bin>mongo
MongoDB 提示符與我們在 MySQL 或 SQLite 終端中通常看到的類似。所有資料庫操作,例如建立資料庫、插入文件、更新和刪除以及檢索文件,都可以在控制檯中完成。
E:\mongodb\bin>mongo
MongoDB shell version v4.0.6
connecting to: mongodb://127.0.0.1:27017/?gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("0d848b11-acf7-4d30-83df-242d1d7fa693") }
MongoDB server version: 4.0.6
---
>
預設使用的資料庫是 test。
> db Test
使用“use”命令可以將任何其他資料庫設定為當前資料庫。如果指定的資料庫不存在,則會建立一個新的資料庫。
> use mydb switched to db mydb
請參閱我們在 MongoDB 上的詳細教程:https://tutorialspoint.tw/mongodb/index.htm。
MongoEngine - MongoDB Compass
MongoDB 還開發了一個用於處理 MongoDB 資料庫的 GUI 工具。它被稱為 MongoDB Compass。這是一個方便的工具,無需手動編寫查詢即可執行所有 CRUD 操作。它有助於執行許多活動,例如索引、文件驗證等。
從 https://www.mongodb.com/download-center/compass 下載 MongoDB Compass 的社群版並啟動 **MongoDBCompassCommunity.exe**(確保在啟動 Compass 之前 MongoDB 伺服器正在執行)。透過提供正確的主機和埠號連線到本地伺服器。
所有當前可用的資料庫都將列出如下:
單擊 + 按鈕(顯示在左側面板底部)以建立新的資料庫。
從列表中選擇資料庫名稱並選擇一個集合,如下所示:
您可以直接新增文件或從 CSV 或 JSON 檔案匯入。
從“新增資料”下拉選單中選擇“插入文件”。
新增的文件將以 JSON、列表或表格形式顯示:
請注意,就像關係資料庫中的表有一個主鍵一樣,MongoDB 資料庫中的文件也有一個名為“**_id**”的特殊鍵,它是自動生成的。
MongoDB Inc. 提供了一個 Python 驅動程式,用於連線 MongoDB 資料庫。它被稱為 **PyMongo**,其使用方法類似於標準 SQL 查詢。
安裝 PyMongo 模組後,我們需要 MongoClient 類的物件來與 MongoDB 伺服器互動。
<<< from pymongo import MongoClient <<< client=MongoClient()
使用以下語句建立新的資料庫:
db=client.mydatabase
對該資料庫的 CRUD 操作使用 insert_one()(或 insert_many())、find()、update() 和 delete() 等方法執行。PyMongo 庫的詳細討論可在 https://tutorialspoint.tw/python_data_access/python_mongodb_introduction.htm 找到。
但是,除非將 Python 的使用者定義物件轉換為 MongoDB 的資料型別,否則無法將其儲存在資料庫中。這就是我們需要 **MongoEngine** 庫的地方。
MongoEngine - 物件文件對映器
MongoDB 是一個基於文件的資料庫。每個文件都是欄位和值的 JSON 式表示。MongoDB 中的文件大致相當於 RDBMS 表中的一行(MongoDB 中表的等效項是集合)。即使 MongoDB 不強制執行任何預定義的模式,文件中的欄位物件也具有一定的資料型別。MongoDB 資料型別與 Python 的主要資料型別非常相似。如果要儲存 Python 使用者定義類的物件,則必須手動將它的屬性解析為等效的 MongoDB 資料型別。
MongoEngine 在 PyMongo 之上提供了一個方便的抽象層,並將 Document 類的每個物件對映到 MongoDB 資料庫中的文件。MongoEngine API 由 Hary Marr 於 2013 年 8 月開發。MongoEngine 的最新版本是 0.19.1。
MongoEngine 之於 MongoDB,就像 SQLAlchemy 之於 RDBMS 資料庫。MongoEngine 庫提供了一個 Document 類,用作定義自定義類的基類。此類的屬性構成 MongoDB 文件的欄位。Document 類定義了執行 CRUD 操作的方法。在後續主題中,我們將學習如何使用它們。
MongoEngine - 安裝
要使用 MongoEngine,您需要已經安裝 MongoDB,並且 MongoDB 伺服器應該像前面描述的那樣執行。
安裝 MongoEngine 最簡單的方法是使用 PIP 安裝程式。
pip install mongoengine
如果您的 Python 安裝沒有安裝 Setuptools,則必須從 https://github.com/MongoEngine/mongoengine 下載 MongoEngine 並執行以下命令:
python setup.py install
MongoEngine 具有以下依賴項:
pymongo>=3.4
six>=1.10.0
dateutil>=2.1.0
pillow>=2.0.0
要驗證正確的安裝,請執行 import 命令並檢查版本,如下所示:
>>> import mongoengine >>> mongoengine.__version__ '0.19.1'
連線到 MongoDB 資料庫
如前所述,您應該首先使用 mongod 命令啟動 MongoDB 伺服器。
MongoEngine 提供 connect() 函式來連線到正在執行的 mongodb 伺服器例項。
from mongoengine import connect connect(‘mydata.db’)
預設情況下,MongoDB 伺服器在 localhost 上的 27017 埠執行。要自定義,您應該向 connect() 提供主機和埠引數:
connect('mydata.db', host='192.168.1.1', port=12345)
如果資料庫需要身份驗證,則應提供其憑據,例如使用者名稱、密碼和 authentication_source 引數。
connect('mydata.db', username='user1', password='***', authentication_source='admin')
MongoEngine 還支援 URI 樣式連線而不是 IP 地址。
connect('mydata.db', host='mongodb:///database_name')
connect() 函式還有另一個可選引數,稱為 replicaset。MongoDB 是一個分散式資料庫。為了確保高可用性,通常在一個伺服器上儲存的資料會在許多伺服器例項中複製。MongoDB 中的副本集是一組 mongod 程序,在這些程序上維護相同的資料集。副本集是所有生產部署的基礎。
connect(host='mongodb:///dbname?replicaSet=rs-name')
副本集方法定義如下:
| rs.add() | 將成員新增到副本集。 |
| rs.conf() | 返回副本集配置文件。 |
| rs.freeze() | 阻止當前成員在一段時間內當選為主節點。 |
| rs.initiate() | 初始化一個新的副本集。 |
| rs.reconfig() | 透過應用新的副本集配置物件來重新配置副本集。 |
| rs.remove() | 從副本集中移除成員。 |
MongoEngine 還允許連線到多個數據庫。您需要為每個資料庫提供唯一的別名。例如,以下程式碼將 Python 指令碼連線到兩個 MongoDB 資料庫。
connect(alias='db1', db='db1.db') connect(alias='db2', db='db2.db')
MongoEngine - 文件類
MongoEngine 被稱為 ODM(**物件文件對映器**)。MongoEngine 定義了一個 Document 類。這是一個基類,其繼承類用於定義儲存在 MongoDB 資料庫中的文件集合的結構和屬性。此子類的每個物件都構成資料庫中集合中的文件。
此 Document 子類中的屬性是各種 Field 類的物件。以下是一個典型的 Document 類的示例:
from mongoengine import *
class Student(Document):
studentid = StringField(required=True)
name = StringField(max_length=50)
age = IntField()
def _init__(self, id, name, age):
self.studentid=id,
self.name=name
self.age=age
這類似於 SQLAlchemy ORM 中的模型類。預設情況下,資料庫中集合的名稱是 Python 類的名稱,其名稱轉換為小寫。但是,可以在 Document 類的 meta 屬性中指定不同的集合名稱。
meta={collection': 'student_collection'}
現在宣告此類的物件並呼叫 save() 方法以將文件儲存到資料庫中。
s1=Student('A001', 'Tara', 20)
s1.save()
MongoEngine - 動態模式
MongoDB 資料庫的優點之一是它支援動態模式。要建立一個支援動態模式的類,請從 DynamicDocument 基類繼承它。以下是具有動態模式的 Student 類:
>>> class student(DynamicDocument): ... name=StringField()
第一步是像以前一樣新增第一個文件。
>>> s1=student()
>>> s1.name="Tara"
>>> connect('mydb')
>>> s1.save()
現在向第二個文件新增另一個屬性並儲存。
>>> s2=student() >>> setattr(s2,'age',20) >>> s2.name='Lara' >>> s2.save()
在資料庫中,學生集合將顯示具有動態模式的兩個文件。
文件類的元資料字典可以透過指定max_documents和max_size來使用帶上限的集合。
max_documents − 集合中允許儲存的最大文件數。
max_size − 集合的最大大小(以位元組為單位)。MongoDB內部和之前的mongoengine會將max_size向上舍入到下一個256的倍數。
如果未指定max_size而指定了max_documents,則max_size預設為10485760位元組(10MB)。
文件類的其他引數列在下面:
| objects | 訪問時延遲建立的QuerySet物件。 |
| cascade_save() | 遞迴儲存文件上的任何引用和泛型引用。 |
| clean() | 在執行驗證之前執行文件級別資料清理的鉤子。 |
| create_index() | 根據需要建立給定的索引。 |
| drop_collection() | 刪除資料庫中與此文件型別關聯的整個集合。 |
| from_json() | 將json資料轉換為文件例項。 |
| modify() | 執行資料庫中文件的原子更新,並使用更新後的版本重新載入文件物件。 |
| pk | 獲取主鍵。 |
| save() | 將文件儲存到資料庫。如果文件已存在,則將更新它,否則將建立它。返回儲存的物件例項。 |
| delete() | 從資料庫中刪除當前文件。 |
| insert() | 執行批次插入操作。 |
MongoEngine - 欄位
MongoEngine文件類具有一個或多個屬性。每個屬性都是Field類的一個物件。BaseField是所有欄位型別的基類。BaseField類建構函式具有以下引數:
BaseField(db_field, required, default, unique, primary_key)
db_field表示資料庫欄位的名稱。
required引數決定此欄位的值是否必填,預設為false。
default引數包含此欄位的預設值。
unique引數預設為false。如果希望此欄位對每個文件具有唯一值,則將其設定為true。
primary_key引數預設為false。設定為true可將此欄位設為主鍵。
有許多從BaseField派生的Field類。
數值欄位
IntField(32位整數)、LongField(64位整數)、FloatField(浮點數)欄位建構函式具有min_value和max_value引數。
還有一個DecimalField類。此欄位物件的 value 是一個浮點數,其精度可以指定。DecimalField類定義了以下引數:
DecimalField(min_value, max_value, force_string, precision, rounding)
| min_value | 指定最小可接受值。 |
| max_value | 指定欄位可以具有的最大值。 |
| force_string | 如果為True,則此欄位的值將儲存為字串。 |
| precision | 將浮點表示限制為位數。 |
| rounding | 根據以下預定義常量對數字進行舍入:decimal.ROUND_CEILING(朝正無窮大方向舍入)、decimal.ROUND_DOWN(朝零方向舍入)、decimal.ROUND_FLOOR(朝負無窮大方向舍入)、decimal.ROUND_HALF_DOWN(最接近的,與零相近時朝零方向舍入)、decimal.ROUND_HALF_EVEN(最接近的,與零相近時朝最近的偶數舍入)、decimal.ROUND_HALF_UP(最接近的,與零相近時遠離零方向舍入)、decimal.ROUND_UP(遠離零方向舍入)、decimal.ROUND_05UP(如果朝零方向舍入後的最後一位數字為0或5,則遠離零方向舍入;否則朝零方向舍入)。 |
文字欄位
StringField物件可以儲存任何Unicode值。您可以在建構函式中指定字串的min_length和max_length。URLField物件是一個StringField,具有將輸入驗證為URL的功能。EmailField將字串驗證為有效的電子郵件表示形式。
StringField(max-length, min_length) URLField(url_regex) EmailField(domain_whiltelist, allow_utf8_user, allow_ip_domain)
domain_whitelist引數包含您不支援的無效域列表。如果設定為True,allow_utf8_user引數允許字串包含UTF8字元作為電子郵件的一部分。allow_ip_domain引數預設為false,但如果為true,它可以是有效的IPV4或IPV6地址。
以下示例使用數值欄位和字串欄位:
from mongoengine import *
connect('studentDB')
class Student(Document):
studentid = StringField(required=True)
name = StringField()
age=IntField(min_value=6, max-value=20)
percent=DecimalField(precision=2)
email=EmailField()
s1=Student()
s1.studentid='001'
s1.name='Mohan Lal'
s1.age=20
s1.percent=75
s1.email='mohanlal@gmail.com'
s1.save()
執行上述程式碼後,學生集合將顯示如下文件:
ListField
此型別的欄位包裝任何標準欄位,從而允許將多個物件用作資料庫中的列表物件。此欄位可以與ReferenceField一起使用以實現一對多關係。
上述示例中的學生文件類修改如下:
from mongoengine import *
connect('studentDB')
class Student(Document):
studentid = StringField(required=True)
name = StringField(max_length=50)
subjects = ListField(StringField())
s1=Student()
s1.studentid='A001'
s1.name='Mohan Lal'
s1.subjects=['phy', 'che', 'maths']
s1.save()
新增的文件以JSON格式顯示如下:
{
"_id":{"$oid":"5ea6a1f4d8d48409f9640319"},
"studentid":"A001",
"name":"Mohan Lal",
"subjects":["phy","che","maths"]
}
DictField
DictField類的一個物件儲存Python字典物件。在相應的資料庫欄位中,這也將被儲存。
在上面的例子中,我們將ListField的型別更改為DictField。
from mongoengine import *
connect('studentDB')
class Student(Document):
studentid = StringField(required=True)
name = StringField(max_length=50)
subjects = DictField()
s1=Student()
s1.studentid='A001'
s1.name='Mohan Lal'
s1.subjects['phy']=60
s1.subjects['che']=70
s1.subjects['maths']=80
s1.save()
資料庫中的文件如下所示:
{
"_id":{"$oid":"5ea6cfbe1788374c81ccaacb"},
"studentid":"A001",
"name":"Mohan Lal",
"subjects":{"phy":{"$numberInt":"60"},
"che":{"$numberInt":"70"},
"maths":{"$numberInt":"80"}
}
}
ReferenceField
MongoDB文件可以使用此型別的欄位來儲存對另一個文件的引用。這樣,我們就可以像在RDBMS中一樣實現連線。ReferenceField建構函式使用其他文件類的名稱作為引數。
class doc1(Document): field1=StringField() class doc2(Document): field1=StringField() field2=ReferenceField(doc1)
在下面的示例中,StudentDB資料庫包含兩個文件類:student和teacher。Student類的文件包含對teacher類物件的引用。
from mongoengine import *
connect('studentDB')
class Teacher (Document):
tid=StringField(required=True)
name=StringField()
class Student(Document):
sid = StringField(required=True)
name = StringField()
tid=ReferenceField(Teacher)
t1=Teacher()
t1.tid='T1'
t1.name='Murthy'
t1.save()
s1=Student()
s1.sid='S1'
s1.name='Mohan'
s1.tid=t1
s1.save()
執行上述程式碼並在Compass GUI中驗證結果。在StudentDB資料庫中建立了對應於兩個文件類的兩個集合。
新增的教師文件如下:
{
"_id":{"$oid":"5ead627463976ea5159f3081"},
"tid":"T1",
"name":"Murthy"
}
學生文件的內容如下所示:
{
"_id":{"$oid":"5ead627463976ea5159f3082"},
"sid":"S1",
"name":"Mohan",
"tid":{"$oid":"5ead627463976ea5159f3081"}
}
請注意,Student文件中的ReferenceField儲存相應Teacher文件的_id。訪問時,Student物件會自動轉換為引用,並在訪問相應的Teacher物件時取消引用。
要新增對正在定義的文件的引用,請使用“self”代替其他文件類作為ReferenceField的引數。需要注意的是,使用ReferenceField可能會導致文件檢索方面的效能下降。
ReferenceField建構函式還有一個可選引數reverse_delete_rule。它的值決定了如果引用的文件被刪除該怎麼做。
可能的值如下:
DO_NOTHING (0) - 什麼也不做(預設)。
NULLIFY (1) - 將引用更新為null。
CASCADE (2) - 刪除與引用關聯的文件。
DENY (3) - 阻止刪除引用物件。
PULL (4) - 從引用的ListField中刪除引用。
您可以使用引用列表實現一對多關係。假設學生文件必須與一個或多個教師文件相關聯,則Student類必須具有ReferenceField例項的ListField。
from mongoengine import *
connect('studentDB')
class Teacher (Document):
tid=StringField(required=True)
name=StringField()
class Student(Document):
sid = StringField(required=True)
name = StringField()
tid=ListField(ReferenceField(Teacher))
t1=Teacher()
t1.tid='T1'
t1.name='Murthy'
t1.save()
t2=Teacher()
t2.tid='T2'
t2.name='Saxena'
t2.save()
s1=Student()
s1.sid='S1'
s1.name='Mohan'
s1.tid=[t1,t2]
s1.save()
在Compass中驗證上述程式碼的結果後,您會發現學生文件具有兩個教師文件的引用:
Teacher Collection
{
"_id":{"$oid":"5eaebcb61ae527e0db6d15e4"},
"tid":"T1","name":"Murthy"
}
{
"_id":{"$oid":"5eaebcb61ae527e0db6d15e5"},
"tid":"T2","name":"Saxena"
}
Student collection
{
"_id":{"$oid":"5eaebcb61ae527e0db6d15e6"},
"sid":"S1","name":"Mohan",
"tid":[{"$oid":"5eaebcb61ae527e0db6d15e4"},{"$oid":"5eaebcb61ae527e0db6d15e5"}]
}
DateTimeField
DateTimeField類的一個例項允許在MongoDB資料庫中使用日期格式的資料。MongoEngine查詢Python-DateUtil庫以解析適當日期格式的資料。如果當前安裝中沒有此庫,則使用內建time模組的time.strptime()函式表示日期。此型別欄位的預設值為當前日期時間例項。
DynamicField
此欄位可以處理不同且變化型別的資料。此型別的欄位由DynamicDocument類內部使用。
ImageField
此型別的欄位對應於文件中可以儲存影像檔案的欄位。此類的建構函式可以接受size和thumbnail_size引數(均以畫素大小表示)。
MongoEngine - 新增/刪除文件
我們已經使用Document類的save()方法向集合中新增文件。save()方法可以藉助以下引數進一步自定義:
| force_insert | 預設為False,如果設定為True,則不允許更新現有文件。 |
| validate | 驗證文件;設定為False以跳過。 |
| clean | 呼叫文件clean方法,validate引數應為True。 |
| write_concern | 將用作生成的getLastError命令的選項。例如,save(..., write_concern={w: 2, fsync: True}, ...) 將等待至少兩臺伺服器記錄寫入,並將強制在主伺服器上執行fsync。 |
| cascade | 設定級聯儲存的標誌。您可以透過在文件__meta__中設定“cascade”來設定預設值。 |
| cascade_kwargs | 要傳遞給級聯儲存的可選關鍵字引數。等同於cascade=True。 |
| _refs | 級聯儲存中使用的已處理引用的列表。 |
| save_condition | 僅當資料庫中的匹配記錄滿足條件時才執行儲存。如果條件不滿足,則引發OperationError。 |
| signal_kwargs | 要傳遞給訊號呼叫的kwargs字典。 |
您可以在呼叫save()之前設定用於驗證文件的清理規則。透過提供自定義clean()方法,您可以執行任何預驗證/資料清理。
class MyDocument(Document):
...
...
def clean(self):
if <condition>==True:
msg = 'error message.'
raise ValidationError(msg)
請注意,只有在啟用驗證並呼叫save()時才會呼叫清理。
Document類還有一個insert()方法用於執行批次插入。它具有以下引數:
| doc_or_docs | 要插入的文件或文件列表。 |
| load_bulk | 如果為True,則返回文件例項列表。 |
| write_concern | 額外的關鍵字引數將傳遞給insert(),它將用作生成的getLastError命令的選項。 |
| signal_kwargs | (可選) 要傳遞給訊號呼叫的kwargs字典。 |
如果文件包含任何ReferenceField物件,則預設情況下,save()方法不會儲存對這些物件的任何更改。如果您希望也儲存所有引用(注意每個儲存都是一個單獨的查詢),則將cascade作為True傳遞給save方法將級聯任何儲存。
從其集合中刪除文件非常容易,只需呼叫delete()方法即可。請記住,只有在文件之前已儲存的情況下,它才會生效。delete()方法具有以下引數:
| signal_kwargs | (可選) 要傳遞給訊號呼叫的kwargs字典。 |
| write_concern | 傳遞的額外關鍵字引數將用作生成的getLastError命令的選項。 |
要刪除資料庫中的整個集合,請使用drop_collecction()方法。此方法將刪除與該文件型別關聯的資料庫中的整個集合。如果文件未設定集合(例如,如果它是抽象的),則該方法會引發OperationError。
文件類中的modify()方法執行資料庫中文件的原子更新並重新載入其更新版本。如果文件已更新,則返回True;如果資料庫中的文件與查詢不匹配,則返回False。請注意,如果方法返回True,則對文件所做的所有未儲存更改都將被拒絕。
引數
| query | 只有當資料庫中的文件與查詢匹配時,才會執行更新。 |
| update | Django風格的更新關鍵字引數 |
MongoEngine - 查詢資料庫
connect()函式返回一個MongoClient物件。使用此物件可用的list_database_names()方法,我們可以檢索伺服器上的資料庫數量。
from mongoengine import *
con=connect('newdb')
dbs=con.list_database_names()
for db in dbs:
print (db)
還可以使用list_collection_names()方法獲取資料庫中集合的列表。
collections=con['newdb'].list_collection_names() for collection in collections: print (collection)
如前所述,Document類具有objects屬性,該屬性允許訪問與資料庫關聯的物件。
newdb資料庫具有與下面的Document類對應的products集合。要獲取所有文件,我們使用方法objects屬性如下:
from mongoengine import *
con=connect('newdb')
class products (Document):
ProductID=IntField(required=True)
Name=StringField()
price=IntField()
for product in products.objects:
print ('ID:',product.ProductID, 'Name:',product.Name, 'Price:',product.price)
輸出
ID: 1 Name: Laptop Price: 25000 ID: 2 Name: TV Price: 50000 ID: 3 Name: Router Price: 2000 ID: 4 Name: Scanner Price: 5000 ID: 5 Name: Printer Price: 12500
MongoEngine - 過濾器
objects屬性是一個QuerySet管理器。它在訪問時建立並返回一個QuerySet。可以使用欄位名稱作為關鍵字引數對查詢進行過濾。例如,從上面的products集合中,要列印產品名稱為“TV”的文件的詳細資訊,我們使用Name作為關鍵字引數。
for product in products.objects(Name='TV'):
print ('ID:',product.ProductID, 'Name:',product.Name, 'Price:',product.price)
您可以使用QuerySet物件的filter方法來對查詢應用過濾器。以下程式碼片段也返回名稱為“TV”的產品詳細資訊。
qset=products.objects
for product in qset.filter(Name='TV'):
print ('ID:',product.ProductID, 'Name:',product.Name, 'Price:',product.price)
MongoEngine - 查詢運算子
除了=運算子用於檢查相等性之外,MongoEngine中還定義了以下邏輯運算子。
| ne | 不等於 |
| lt | 小於 |
| lte | 小於或等於 |
| gt | 大於 |
| gte | 大於或等於 |
| not | 否定標準檢查,可以在其他運算子之前使用 |
| in | 值在列表中 |
| nin | 值不在列表中 |
| mod | value % x == y,其中x和y是提供的兩個值 |
| all | 提供的列表中的每個專案都在陣列中 |
| size | 陣列的大小是 |
| exists | 欄位的值存在 |
這些運算子必須用雙下劃線__附加到欄位名稱。
要使用大於(gt)運算子,請使用以下格式:
#using greater than operator
for product in products.objects(price__gt=10000):
print ('ID:',product.ProductID, 'Name:',product.Name, 'Price:',product.price)
輸出
ID: 1 Name: Laptop Price: 25000 ID: 2 Name: TV Price: 50000 ID: 5 Name: Printer Price: 12500
in運算子類似於Python的in運算子。對於與列表中的名稱匹配的產品名稱,使用以下程式碼:
for product in products.objects(Name__in=['TV', 'Printer']):
print ('ID:',product.ProductID, 'Name:',product.Name, 'Price:',product.price)
輸出
ID: 2 Name: TV Price: 50000 ID: 5 Name: Printer Price: 12500
您可以使用以下運算子作為正則表示式的快捷方式來對查詢應用過濾器:
| exact | 字串欄位完全匹配值 |
| iexact | 字串欄位完全匹配值(不區分大小寫) |
| contains | 字串欄位包含值 |
| icontains | 字串欄位包含值(不區分大小寫) |
| startswith | 字串欄位以值開頭 |
| istartswith | 字串欄位以值開頭(不區分大小寫) |
| endswith | 字串欄位以值結尾 |
| iendswith | 字串欄位以值結尾(不區分大小寫) |
| match | 執行$elemMatch,以便您可以匹配陣列中的整個文件 |
例如,以下程式碼列印名稱中包含“o”的產品詳細資訊:
for product in products.objects(Name__contains='o'):
print ('ID:',product.ProductID, 'Name:',product.Name, 'Price:',product.price)
輸出
ID: 1 Name: Laptop Price: 25000 ID: 3 Name: Router Price: 2000
在另一個字串查詢示例中,以下程式碼顯示以“er”結尾的名稱:
for product in products.objects(Name__endswith='er'):
print ('ID:',product.ProductID, 'Name:',product.Name, 'Price:',product.price)
輸出
ID: 3 Name: Router Price: 2000 ID: 4 Name: Scanner Price: 5000 ID: 5 Name: Printer Price: 12500
MongoEngine - QuerySet 方法
QuerySet物件擁有以下用於查詢資料庫的有用方法。
first()
返回滿足查詢的第一個文件。以下程式碼將返回products集合中的第一個文件,其價格<20000。
qset=products.objects(price__lt=20000)
doc=qset.first()
print ('Name:',doc.Name, 'Price:',doc.price)
輸出
Name: Router Price: 2000
exclude()
這將導致從Query Set中排除提到的欄位。這裡,使用Document類的to_json()方法來獲取Document的JSON化版本。ProductID欄位將不會出現在結果中。
for product in products.objects.exclude('ProductID'):
print (product.to_json())
輸出
{"_id": {"$oid": "5c8dec275405c12e3402423c"}, "Name": "Laptop", "price": 25000}
{"_id": {"$oid": "5c8dec275405c12e3402423d"}, "Name": "TV", "price": 50000}
{"_id": {"$oid": "5c8dec275405c12e3402423e"}, "Name": "Router", "price": 2000}
{"_id": {"$oid": "5c8dec275405c12e3402423f"}, "Name": "Scanner", "price": 5000}
{"_id": {"$oid": "5c8dec275405c12e34024240"}, "Name": "Printer", "price": 12500}
fields()
使用此方法來操作在查詢集中載入哪些欄位。使用欄位名稱作為關鍵字引數,設定為1表示包含,設定為0表示排除。
for product in products.objects.fields(ProductID=1,price=1): print (product.to_json())
輸出
{"_id": {"$oid": "5c8dec275405c12e3402423c"}, "ProductID": 1, "price": 25000}
{"_id": {"$oid": "5c8dec275405c12e3402423d"}, "ProductID": 2, "price": 50000}
{"_id": {"$oid": "5c8dec275405c12e3402423e"}, "ProductID": 3, "price": 2000}
{"_id": {"$oid": "5c8dec275405c12e3402423f"}, "ProductID": 4, "price": 5000}
{"_id": {"$oid": "5c8dec275405c12e34024240"}, "ProductID": 5, "price": 12500}
在fields()方法中將欄位關鍵字引數設定為0與exclude()方法的效果類似。
for product in products.objects.fields(price=0): print (product.to_json())
輸出
{"_id": {"$oid": "5c8dec275405c12e3402423c"}, "ProductID": 1, "Name": "Laptop"}
{"_id": {"$oid": "5c8dec275405c12e3402423d"}, "ProductID": 2, "Name": "TV"}
{"_id": {"$oid": "5c8dec275405c12e3402423e"}, "ProductID": 3, "Name": "Router"}
{"_id": {"$oid": "5c8dec275405c12e3402423f"}, "ProductID": 4, "Name": "Scanner"}
{"_id": {"$oid": "5c8dec275405c12e34024240"}, "ProductID": 5, "Name": "Printer"}
only()
此方法的效果類似於fields()方法。查詢集中將只顯示與關鍵字引數對應的欄位。
for product in products.objects.only('Name'):
print (product.to_json())
輸出
{"_id": {"$oid": "5c8dec275405c12e3402423c"}, "Name": "Laptop"}
{"_id": {"$oid": "5c8dec275405c12e3402423d"}, "Name": "TV"}
{"_id": {"$oid": "5c8dec275405c12e3402423e"}, "Name": "Router"}
{"_id": {"$oid": "5c8dec275405c12e3402423f"}, "Name": "Scanner"}
{"_id": {"$oid": "5c8dec275405c12e34024240"}, "Name": "Printer"}
sum()
此方法計算查詢集中給定欄位的總和。
average()
此方法計算查詢集中給定欄位的平均值。
avg=products.objects.average('price')
ttl=products.objects.sum('price')
print ('sum of price field',ttl)
print ('average of price field',avg)
輸出
sum of price field 94500 average of price field 18900.0
MongoEngine - 排序
QuerySet的order_by()函式用於以排序的方式獲取查詢結果。用法如下:
Qset.order_by(‘fieldname’)
預設情況下,排序順序為升序。對於降序,請將“-”符號附加到欄位名稱。例如,要按價格升序獲取列表:
from mongoengine import *
con=connect('newdb')
class products (Document):
ProductID=IntField(required=True)
company=StringField()
Name=StringField()
price=IntField()
for product in products.objects.order_by('price'):
print ("Name:{} company:{} price:{}".format(product.Name, product.company, product.price))
輸出
Name:Router company:Iball price:2000 Name:Scanner company:Cannon price:5000 Name:Printer company:Cannon price:12500 Name:Laptop company:Acer price:25000 Name:TV company:Philips price:31000 Name:Laptop company:Dell price:45000 Name:TV company:Samsung price:50000
以下程式碼將按名稱降序獲取列表:
for product in products.objects.order_by('-Name'):
print ("Name:{} company:{} price:{}".format(product.Name, product.company, product.price))
輸出
Name:TV company:Samsung price:50000 Name:TV company:Philips price:31000 Name:Scanner company:Cannon price:5000 Name:Router company:Iball price:2000 Name:Printer company:Cannon price:12500 Name:Laptop company:Acer price:25000 Name:Laptop company:Dell price:45000
您還可以對多個欄位進行排序。此程式碼將按公司、價格升序獲取列表。
for product in products.objects.order_by('company','price'):
print ("Name:{} company:{} price:{}".format(product.Name, product.company, product.price))
輸出
Name:Laptop company:Acer price:25000 Name:Scanner company:Cannon price:5000 Name:Printer company:Cannon price:12500 Name:Laptop company:Dell price:45000 Name:Router company:Iball price:2000 Name:TV company:Philips price:31000 Name:TV company:Samsung price:50000
MongoEngine - 自定義 QuerySet
預設情況下,文件類上的objects屬性返回一個QuerySet,而不應用任何過濾器。但是,您可以在修改queryset的文件上定義一個classmethod。此類方法應該接受兩個引數——doc_cls和queryset,並且需要用queryset_manager()裝飾才能被識別。
@queryset_manager
def qry_method(docs_cls,queryset):
….
----
在下面的示例中,名為products的文件類有一個用@queryset_manager裝飾的expensive_prods()方法。該方法本身會對queryset應用過濾器,以便只返回價格>20000的物件。此方法現在是預設的文件查詢,products類的objects屬性返回過濾後的文件。
from mongoengine import *
con=connect('newdb')
class products (Document):
ProductID=IntField(required=True)
company=StringField()
Name=StringField()
price=IntField()
@queryset_manager
def expensive_prods(docs_cls,queryset):
return queryset.filter(price__gt=20000)
for product in products.expensive_prods():
print ("Name:{} company:{} price:{}".format(product.Name, product.company, product.price))
輸出
Name:Laptop company:Acer price:25000 Name:TV company:Samsung price:50000 Name:TV company:Philips price:31000 Name:Laptop company:Dell price:45000
如果您希望自定義過濾文件的方法,首先宣告QuerySet類的子類,並將其用作meta字典中queryset_class屬性的值。
下面的示例使用MyQuerySet類作為自定義queryset的定義。此類中的myqrymethod()過濾名稱欄位以“er”結尾的文件。在products類中,meta屬性引用此queryset子類作為queryset_class屬性的值。
from mongoengine import *
con=connect('newdb')
class MyQuerySet(QuerySet):
def myqrymethod(self):
return self.filter(Name__endswith='er')
class products (Document):
meta = {'queryset_class': MyQuerySet}
ProductID=IntField(required=True)
company=StringField()
Name=StringField()
price=IntField()
for product in products.objects.myqrymethod():
print ("Name:{} company:{} price:{}".format(product.Name, product.company, product.price))
輸出
Name:Router company:Iball price:2000 Name:Scanner company:Cannon price:5000 Name:Printer company:Cannon price:12500
MongoEngine - 索引
索引集合可以加快查詢處理速度。預設情況下,每個集合都會在_id欄位上自動建立索引。此外,您還可以在一個或多個欄位上建立索引。
使用Compass,我們可以非常輕鬆地構建索引。單擊下圖所示“索引”選項卡上的“建立索引”按鈕:
將出現一個對話方塊,如圖所示。選擇索引名稱、要索引的欄位、索引順序(升序或降序)和其他選項。
使用MongoEngine時,索引是透過在Document類定義的meta字典中指定“indexes”鍵來建立的。
indexes屬性的值是一個欄位列表。在下面的示例中,我們要求student集合中的文件根據name欄位建立索引。
from mongoengine import *
con=connect('mydata')
class student(Document):
name=StringField(required=True)
course=StringField()
meta = {'indexes':['name']}
s1=student()
s1.name='Avinash'
s1.course='DataScience'
s1.save()
s2=student()
s2.name='Anita'
s2.course='WebDesign'
s2.save()
預設情況下,索引順序為升序。可以透過在前面新增“+”表示升序或“-”表示降序來指定順序。
要建立複合索引,請使用欄位名稱的元組,可以選擇附加“+”或“-”符號來指示排序順序。
在下面的示例中,student文件類包含name和course上的複合索引定義(注意course欄位字首為“-”符號,這意味著索引按name升序和course降序構建)。
from mongoengine import *
con=connect('mydata')
class student(Document):
name=StringField(required=True)
course=StringField()
meta = {'indexes':[('name','-course')]}
s1=student()
s1.name='Avinash'
s1.course='DataScience'
s1.save()
s2=student()
s2.name='Anita'
s2.course='WebDesign'
s2.save()
MongoDB Compass將顯示索引如下:
“indexes”的值可以是具有各種選項的字典,如下所示:
| fields | 要索引的欄位。 |
| cls | 如果啟用allow_inheritance,您可以配置是否應自動新增_cls欄位。 |
| sparse | 索引是否應為稀疏索引。 |
| unique | 索引是否應為唯一索引。 |
| expireAfterSeconds | 透過設定秒數來自動使集合中的資料過期 |
| name | 允許您指定索引的名稱 |
| collation | 允許建立不區分大小寫的索引 |
以下示例在name欄位上建立索引,該索引在3600秒後過期。
from mongoengine import *
con=connect('mydata')
class student(Document):
name=StringField(required=True)
course=StringField()
meta = {'indexes':[{
'fields': ['name'],
'expireAfterSeconds': 3600
}
]
}
要指定文字索引,請在欄位名稱前新增“$”符號;對於雜湊索引,請使用“#”作為字首。
如此指定的索引會在文件新增到集合時自動建立。要停用自動建立,請在meta屬性中將“auto_create_index”設定為False。
我們有Document類中的list_indexes()方法,該方法顯示可用索引的列表。
print (student.list_indexes())
[[('name', 1)], [('_id', 1)]]
要在meta字典中不存在的欄位上建立索引,請使用create_index()方法。以下程式碼將在course欄位上建立索引:
class student(Document):
name=StringField(required=True)
course=StringField()
meta = {'indexes':[{
'fields': ['name'],
'expireAfterSeconds': 3600
}
]}
student.create_index(['course'])
MongoEngine - 聚合
術語“聚合”用於處理資料並返回計算結果的操作。在集合中的文件的一個或多個欄位上查詢總和、計數和平均值可以稱為聚合函式。
MongoEngine提供aggregate()函式,該函式封裝了PyMongo的聚合框架。聚合操作使用集合作為輸入,並返回一個或多個文件作為結果。
MongoDB使用資料處理管道的概念。一個管道可以有多個階段。基本階段提供過濾器並像查詢一樣操作。其他階段提供用於按一個或多個欄位分組和/或排序、字串連線任務、陣列聚合工具等的工具。
以下階段在MongoDB管道建立中定義:
| 名稱 | 描述 |
|---|---|
| $project | 透過新增新欄位或刪除現有欄位來重塑流中的每個文件。 |
| $match | 過濾文件流,只允許匹配的文件以未修改的形式傳遞到下一階段。$match使用標準的MongoDB查詢。 |
| $redact | 透過基於文件本身儲存的資訊來限制每個文件的內容來重塑每個文件。 |
| $limit | 限制要以未修改的形式傳遞到管道的文件 |
| $skip | 跳過前 n 個文件,並將剩餘文件未經修改地傳遞到管道。 |
| $group | 根據給定的識別符號表示式對輸入文件進行分組,並將累加器表示式應用於每個組。輸出文件僅包含識別符號欄位和累加欄位。 |
| $sort | 根據指定的排序鍵重新排序文件流。 |
| $out | 將聚合管道的結果文件寫入集合。 |
聚合表示式使用欄位路徑訪問輸入文件中的欄位。要指定欄位路徑,請使用以美元符號 $ 為字首的欄位名稱的字串。表示式可以使用一個或多個布林運算子($and、$or、$not)和比較運算子($eq、$gt、$lt、$gte、$lte 和 $ne)。
以下算術表示式也用於聚合:
| $add | 將數字相加以返回總和。接受任意數量的引數表示式。 |
| $subtract | 返回從第一個值減去第二個值的結果。 |
| $multiply | 將數字相乘以返回乘積。接受任意數量的引數表示式。 |
| $divide | 返回第一個數字除以第二個數字的結果。接受兩個引數表示式。 |
| $mod | 返回第一個數字除以第二個數字的餘數。接受兩個引數表示式。 |
以下字串表示式也可用於聚合:
| $concat | 連線任意數量的字串。 |
| $substr | 返回字串的子字串,從指定的索引位置開始到指定的長度。 |
| $toLower | 將字串轉換為小寫。接受單個引數表示式。 |
| $toUpper | 將字串轉換為大寫。接受單個引數表示式。 |
| $strcasecmp | 執行字串比較,如果兩個字串等效則返回 0,如果第一個字串大於第二個字串則返回 1,如果第一個字串小於第二個字串則返回 -1。 |
為了演示 **aggregate()** 函式在 MongoEngine 中的工作方式,讓我們首先定義一個名為 orders 的 Document 類。
from mongoengine import *
con=connect('mydata')
class orders(Document):
custID = StringField()
amount= IntField()
status = StringField()
然後我們在 orders 集合中新增以下文件:
| _id | custID | amount | status |
|---|---|---|---|
| ObjectId("5eba52d975fa1e26d4ec01d0") | A123 | 500 | A |
| ObjectId("5eba536775fa1e26d4ec01d1") | A123 | 250 | A |
| ObjectId("5eba53b575fa1e26d4ec01d2") | B212 | 200 | D |
| ObjectId("5eba540e75fa1e26d4ec01d3") | B212 | 400 | A |
aggregate() 函式用於查詢僅當 status 等於 'A' 時每個 custID 的 amount 欄位的總和。相應地,管道構建如下。
管道中的第一階段使用 $match 過濾 status='A' 的文件。第二階段使用 $group 識別符號根據 CustID 對文件進行分組,並計算 amount 的總和。
pipeline = [
{"$match" : {"status" : "A"}},
{"$group": {"_id": "$custID", "total": {"$sum": "$amount"}}}
]
此管道現在用作 aggregate() 函式的引數。
docs = orders.objects().aggregate(pipeline)
我們可以使用 for 迴圈迭代文件遊標。完整的程式碼如下:
from mongoengine import *
con=connect('mydata')
class orders(Document):
custID = StringField()
amount= IntField()
status = StringField()
pipeline = [
{"$match" : {"status" : "A"}},
{"$group": {"_id": "$custID", "total": {"$sum": "$amount"}}}
]
docs = orders.objects().aggregate(pipeline)
for doc in docs:
print (x)
對於給定的資料,將生成以下輸出:
{'_id': 'B212', 'total': 400}
{'_id': 'A123', 'total': 750}
MongoEngine - 高階查詢
為了提高檢索文件中欄位子集的效率,請使用 Objects 屬性的 only() 方法。這將顯著提高效能,特別是對於長度極長的欄位(例如 ListField)。將所需欄位傳遞給 only() 函式。如果在執行 only() 查詢後訪問其他欄位,則返回預設值。
from mongoengine import *
con=connect('newdb')
class person (Document):
name=StringField(required=True)
city=StringField(default='Mumbai')
pin=IntField()
p1=person(name='Himanshu', city='Delhi', pin=110012).save()
doc=person.objects.only('name').first()
print ('name:',doc.name)
print ('city:', doc.city)
print ('PIN:', doc.pin)
輸出
name: Himanshu city: Mumbai PIN: None
**注意** - city 屬性的值用作預設值。由於 PIN 未指定預設值,因此它列印 None。
如果需要缺失的欄位,可以呼叫 reload() 函式。
當文件類具有 ListField 或 DictField 時,在迭代過程中,任何 DBREf 物件都會自動取消引用。為了進一步提高效率,特別是如果文件具有 ReferenceField,可以使用 select_related() 函式限制查詢數量,該函式將 QuerySet 轉換為列表並影響取消引用。
MongoEngine API 包含 Q 類,該類可用於構建包含多個約束的高階查詢。Q 表示查詢的一部分,可以透過關鍵字引數語法和二進位制 & 和 | 運算子進行初始化。
person.objects(Q(name__startswith=’H’) &Q(city=’Mumbai’))
MongoEngine - 文件繼承
可以定義任何使用者定義的 Document 類的繼承類。如果需要,繼承類可以新增額外的欄位。但是,由於這樣的類不是 Document 類的直接子類,因此它不會建立新的集合,而是其物件儲存在其父類使用的集合中。在父類中,元屬性 ‘**allow_inheritance**’。在下面的示例中,我們首先將 employee 定義為文件類並將 allow_inheritance 設定為 true。salary 類派生自 employee,並添加了兩個欄位 dept 和 sal。Employee 和 salary 類的物件都儲存在 employee 集合中。
在下面的示例中,我們首先將 employee 定義為文件類並將 allow_inheritance 設定為 true。salary 類派生自 employee,並添加了兩個欄位 dept 和 sal。Employee 和 salary 類的物件都儲存在 employee 集合中。
from mongoengine import *
con=connect('newdb')
class employee (Document):
name=StringField(required=True)
branch=StringField()
meta={'allow_inheritance':True}
class salary(employee):
dept=StringField()
sal=IntField()
e1=employee(name='Bharat', branch='Chennai').save()
s1=salary(name='Deep', branch='Hyderabad', dept='Accounts', sal=25000).save()
我們可以驗證如下兩個文件是否儲存在 employee 集合中:
{
"_id":{"$oid":"5ebc34f44baa3752530b278a"},
"_cls":"employee",
"name":"Bharat",
"branch":"Chennai"
}
{
"_id":{"$oid":"5ebc34f44baa3752530b278b"},
"_cls":"employee.salary",
"name":"Deep",
"branch":"Hyderabad",
"dept":"Accounts",
"sal":{"$numberInt":"25000"}
}
請注意,為了識別相應的 Document 類,MongoEngine 添加了一個 “_cls” 欄位並將它的值設定為 "employee" 和 "employee.salary"。
如果要為一組 Document 類提供額外的功能,但又無需繼承的開銷,可以先建立一個 **抽象** 類,然後從同一個類派生一個或多個類。要使類成為抽象類,請將元屬性 ‘abstract’ 設定為 True。
from mongoengine import *
con=connect('newdb')
class shape (Document):
meta={'abstract':True}
def area(self):
pass
class rectangle(shape):
width=IntField()
height=IntField()
def area(self):
return self.width*self.height
r1=rectangle(width=20, height=30).save()
MongoEngine - 原子更新
原子性是 ACID 事務屬性之一。資料庫事務必須是不可分割且不可約分的,以便它要麼完全發生,要麼根本不發生。此屬性稱為原子性。MongoDB 只支援單文件的原子性,而不支援多文件事務。
MongoEngine 提供以下方法用於對 QuerySet 進行原子更新。
**update_one()** - 覆蓋或新增查詢匹配的第一個文件。
**update()** - 對查詢匹配的欄位執行原子更新。
**modify()** - 更新文件並返回它。
可以使用這些方法的以下修飾符。(這些修飾符在欄位之前,而不是之後)。
| set | 設定特定值。 |
| unset | 刪除特定值。 |
| inc | 將值增加給定數量。 |
| dec | 將值減少給定數量。 |
| push | 將值附加到列表。 |
| push_all | 將多個值附加到列表。 |
| pop | 根據值刪除列表的第一個或最後一個元素。 |
| pull | 從列表中刪除值。 |
| pull_all | 從列表中刪除多個值。 |
| add_to_set | 僅當值不在列表中時才將其新增到列表。 |
以下是一個原子更新示例,我們首先建立一個名為 tests 的 Document 類並在其中新增一個文件。
from mongoengine import *
con=connect('newdb')
class tests (Document):
name=StringField()
attempts=IntField()
scores=ListField(IntField())
t1=tests()
t1.name='XYZ'
t1.attempts=0
t1.scores=[]
t1.save()
讓我們使用 **update_one()** 方法將 name 欄位從 XYZ 更新為 MongoDB。
tests.objects(name='XYZ').update_one(set__name='MongoDB')
push 修飾符用於在 ListField (scores) 中新增資料。
tests.objects(name='MongoDB').update_one(push__scores=50)
要將 attempts 欄位加 1,可以使用 inc 修飾符。
tests.objects(name='MongoDB').update_one(inc__attempts=1)
更新後的文件如下所示:
{
"_id":{"$oid":"5ebcf8d353a48858e01ced04"},
"name":"MongoDB",
"attempts":{"$numberInt":"1"},
"scores":[{"$numberInt":"50"}]
}
MongoEngine - Javascript
MongoEngine 的 QuerySet 物件具有 **exec_js()** 方法,該方法允許在 MongoDB 伺服器上執行 Javascript 函式。此函式處理以下引數:
exec_js(code, *field_names, **options)
其中,
**code** - 包含要執行的 Javascript 程式碼的字串。
**fields** - 將在函式中使用,並將作為引數傳遞。
**options** - 你希望函式可用的選項(透過 Javascript 中的 options 物件訪問)。
此外,還有一些其他變數可用於函式的範圍,如下所示:
**collection** - 與 Document 類對應的集合的名稱。這應該用於從 Javascript 程式碼中的 db 獲取 Collection 物件。
**query** - 由 QuerySet 物件生成的查詢;在 Javascript 函式中傳遞到 Collection 物件上的 find() 方法。
**options** - 包含傳遞到 exec_js() 的關鍵字引數的物件。
請注意,MongoEngine 文件類中的屬性可能在資料庫中使用不同的名稱(使用 Field 建構函式的 db_field 關鍵字引數設定)。
class BlogPost(Document): title = StringField(db_field='doctitle')
為此,存在一種機制可以在 Javascript 程式碼中用資料庫欄位名稱替換 MongoEngine 欄位屬性。
當訪問集合物件上的欄位時,請使用方括號表示法,並在 MongoEngine 欄位名稱前新增波浪號 (~) 符號。波浪號後面的欄位名稱將轉換為資料庫中使用的名稱。
document': doc[~title];
請注意,當 Javascript 程式碼引用嵌入文件中的欄位時,應在嵌入文件中欄位的名稱之前使用 EmbeddedDocumentField 的名稱後跟一個點。
MongoEngine - GridFS
在 MongoDB 中,大於 16 MB 的檔案使用 GridFS 規範儲存。檔案被分成多個塊,每個塊的預設大小為 255KB。大塊可以根據需要任意大。GridFS 使用兩個集合,一個用於塊,另一個用於元資料。
如果要訪問檔案而不必將其完全載入到記憶體中,可以使用 GridFS 儲存任何檔案。
MongoEngine API 透過 **FileField** 物件支援 GridFS。使用此物件,可以插入和檢索資料。FileField 物件的 **put()** 方法有助於將檔案寫入文件的一部分。
from mongoengine import *
con=connect('newdb')
class lang (Document):
name=StringField()
developer=StringField()
logo=FileField()
l1=lang()
l1.name='Python'
l1.developer='Van Rossum'
f=open('pylogo.png','rb')
l1.logo.put(f,content_type='image/png')
l1.save()
FileField 的內容可以透過 Python 的 File 物件的 read() 方法檢索。
logo = l1.logo.read()
還有一個 **delete()** 方法用於刪除儲存的檔案。
l1 = lang.objects(name='Python').first() l1.logo.delete() l1.save()
請注意,FileField 只儲存單獨 GridFS 集合中檔案的 ID。因此,delete() 方法不會物理刪除檔案。
**replace()** 方法有助於用另一個檔案替換檔案的引用。
l1 = lang.objects(name='Python').first()
f=open('newlogo.png','rb')
l1.logo.replace(f,content_type='image/png')
l1.save()
MongoEngine - 訊號
訊號是由傳送者物件分發的事件,任意數量的接收者物件可以訂閱此類事件。訊號接收者可以訂閱特定的傳送者,也可以接收來自許多傳送者的訊號。
在 MongoEngine 中,訊號處理由 blinker 庫支援,這意味著你需要使用 pip 實用程式安裝它。mongoengine.signals 模組包含以下訊號的定義:
| pre_init | 在建立新的 Document 或 EmbeddedDocument 例項期間呼叫,並在收集建構函式引數後但對其進行任何額外處理之前執行。 |
| post_init | 在完成新的 Document 或 EmbeddedDocument 例項的所有處理後呼叫。 |
| pre_save | 在 save() 內呼叫,在執行任何操作之前。 |
| pre_save_post_validation | 在 save() 內呼叫,在驗證發生後但在儲存之前。 |
| post_save | 在 save() 內呼叫,在大多數字操作(驗證、插入/更新)成功完成之後。傳遞一個額外的布林關鍵字引數以指示儲存是插入還是更新。 |
| pre_delete | 在 delete() 內呼叫,在嘗試刪除操作之前。 |
| post_delete | 在 delete() 內呼叫,在成功刪除記錄後。 |
| pre_bulk_insert | 在驗證要插入的文件後,但在寫入任何資料之前呼叫。 |
| post_bulk_insert | 批次插入操作成功後呼叫。一個額外的布林引數 `loaded` 用於標識文件的內容:`True` 表示文件例項,`False` 表示插入記錄的主鍵值列表。 |
然後將事件處理函式附加到 `Document` 類。注意,**`EmbeddedDocument`** 僅支援 `pre/post_init` 訊號。`pre/post_save` 等應僅附加到 `Document` 類。
您還可以使用裝飾器快速建立多個訊號並將它們作為類裝飾器附加到您的 `Document` 或 `EmbeddedDocument` 子類。
在下面的示例中,作為訊號處理程式演示,我們還使用了 Python 的標準庫模組——`logging` 並將日誌級別設定為除錯。
from mongoengine import * from mongoengine import signals import logging logging.basicConfig(level=logging.DEBUG)
然後,我們編寫一個文件類,以便在 `newdb` 資料庫中建立相應的集合。在類內部,定義了兩個類方法 `pre_save()` 和 `post_save()` 方法,它們旨在在文件儲存到 `Author` 集合之前和之後呼叫。
class Author(Document):
name = StringField()
def __unicode__(self):
return self.name
@classmethod
def pre_save(cls, sender, document, **kwargs):
logging.debug("Pre Save: %s" % document.name)
@classmethod
def post_save(cls, sender, document, **kwargs):
logging.debug("Post Save: %s" % document.name)
if 'created' in kwargs:
if kwargs['created']:
logging.debug("Created")
else:
logging.debug("Updated")
這兩個類方法都定義了用於類名、傳送者物件和文件的引數,以及可選的關鍵字引數列表。
最後,我們註冊訊號處理程式。
signals.pre_save.connect(Author.pre_save, sender=Author) signals.post_save.connect(Author.post_save, sender=Author)
當我們建立一個 `Document` 子類的例項時,控制檯日誌將顯示由各個事件處理程式處理的預儲存和後儲存訊號。
Author(name="Lathkar").save()
Python 控制檯報告日誌如下:
DEBUG:root:Pre Save: Lathkar DEBUG:root:Post Save: Lathkar DEBUG:root:Created
MongoEngine - 文字搜尋
MongoDB 支援使用查詢運算子對字串內容執行文字搜尋。如前所述,要設定文字索引,請在索引名稱前加上 `$` 符號。對於文字索引,已索引欄位的權重表示該欄位相對於文字搜尋分數中的其他已索引欄位的重要性。您還可以在類的元資料字典中指定預設語言。
支援的語言列表可在 https://docs.mongodb.com/manual/reference/text-search-languages/ 找到。MongoEngine API 包含用於 `QuerySet` 物件的 `search_text()` 方法。要在已索引欄位中搜索的字串作為引數給出。
在下面的示例中,我們首先定義一個名為 `lang` 的 `Document` 類,其中包含兩個字串欄位:語言名稱及其功能。我們還在這兩個欄位上建立索引,並分別設定權重。
from mongoengine import *
con=connect('newdb')
class lang (Document):
name=StringField()
features=StringField()
meta = {'indexes': [
{'fields': ['$name', "$features"],
'default_language': 'english',
'weights': {'name': 2, 'features': 10}
}]
}
l1=lang()
l1.name='C++'
l1.features='Object oriented language for OS development'
l1.save()
l2=lang()
l2.name='Python'
l2.features='dynamically typed and object oriented for data science, AI and ML'
l2.save()
l3=lang()
l3.name='HTML'
l3.features='scripting language for web page development'
l3.save()
為了搜尋單詞“oriented”,我們使用 `search_text()` 方法如下:
docs=lang.objects.search_text('oriented')
for doc in docs:
print (doc.name)
上述程式碼的輸出將是描述中包含單詞“oriented”的語言名稱(在本例中為“Python”和“C++”)。
MongoEngine - 擴充套件
MongoEngine 與以下庫完美整合:
marshmallow_mongoengine
marshmallow 是一個與 ORM/ODM/框架無關的序列化/反序列化庫,用於將複雜資料型別(如物件)轉換為和從原生 Python 資料型別轉換。使用 MongoEngine 的此擴充套件,我們可以輕鬆執行序列化/反序列化操作。
首先,像往常一樣建立一個 `Document` 類,如下所示:
import mongoengine as me class Book(me.Document): title = me.StringField()
然後使用以下程式碼生成 marshmallow 模式:
from marshmallow_mongoengine import ModelSchema
class BookSchema(ModelSchema):
class Meta:
model = Book
b_s = BookSchema()
使用以下程式碼儲存文件:
book = Book(title='MongoEngine Book').save()
然後使用以下程式碼使用 `dump()` 和 `load()` 執行序列化/反序列化:
data = b_s.dump(book).data b_s.load(data).data
Flask-MongoEngine
這是一個 Flask 擴充套件,它提供了與 MongoEngine 的整合。此庫可以輕鬆處理應用程式的 MongoDB 資料庫連線管理。您還可以使用 WTForms 作為模型的模型表單。
安裝 `flask-mongoengine` 包後,使用以下設定初始化 Flask 應用程式:
from flask import Flask
from flask_mongoengine import MongoEngine
app = Flask(__name__)
app.config['MONGODB_SETTINGS'] = {
'db': 'mydata',
'host': 'localhost',
'port':27017
}
db = MongoEngine(app)
然後使用以下程式碼定義一個 `Document` 子類:
class book(me.Document): name=me.StringField(required=True)
訪問特定路由時,宣告上述類的物件並呼叫 `save()` 方法。
@app.route('/')
def index():
b1=book(name='Introduction to MongoEngine')
b1.save()
return 'success'
extras-mongoengine
此擴充套件包含其他欄位型別和其他功能。
Eve-MongoEngine
Eve 是一個為人類設計的開源 Python REST API 框架。它允許輕鬆構建和部署高度可定製、功能齊全的 RESTful Web 服務。
Eve 由 Flask 和 Cerberus 提供支援,它提供對 MongoDB 資料儲存的原生支援。Eve-MongoEngine 提供 MongoEngine 與 Eve 的整合。
使用以下程式碼安裝並匯入擴充套件:
import mongoengine from eve import Eve from eve_mongoengine import EveMongoengine
配置設定並初始化 Eve 例項。
my_settings = {
'MONGO_HOST': 'localhost',
'MONGO_PORT': 27017,
'MONGO_DBNAME': 'eve_db'
app = Eve(settings=my_settings)
# init extension
ext = EveMongoengine(app)
定義如下所示的 `Document` 類:
class Person(mongoengine.Document): name = mongoengine.StringField() age = mongoengine.IntField()
新增模型並執行應用程式,最後使用以下程式碼:
ext.add_model(Person) app.run()
Django-MongoEngine
此擴充套件旨在將 MongoEngine 與 Django API 整合,Django API 是一個非常流行的 Python Web 開發框架。該專案仍在開發中。