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://www.tutorialspoint.com/mongodb/index.htm

MongoEngine - MongoDB 指南针

MongoDB 还开发了一个 GUI 工具来处理 MongoDB 数据库。它被称为 MongoDB Compass。它是一个方便的工具,无需手动编写查询即可执行所有 CRUD 操作。它有助于许多活动,例如索引、文档验证等。

从https://www.mongodb.com/download-center/compass下载社区版 MongoDB Compass并启动MongoDBCompassCommunity.exe(在启动 Compass 之前确保 MongoDB 服务器正在运行)。通过提供正确的主机和端口号连接到本地服务器。

数据库

当前可用的所有数据库将列出如下 -

新数据库

单击+按钮(显示在左面板底部)创建新数据库。

数据库名称

从列表中选择数据库名称并选择一个集合,如下所示 -

CSV 或 JSON 文件

您可以直接添加文档或从 ​​CSV 或 JSON 文件导入。

插入文档

从“添加数据”下拉列表中选择“插入文档”。

添加的文档

添加的文档将以 JSON、列表或表格形式显示 -

关系型数据库

请注意,就像关系数据库中的表有一个主键一样,MongoDB 数据库中的文档有一个自动生成的名为“ _id ”的特殊键。

MongoDB Inc. 提供了用于连接 MongoDB 数据库的 Python 驱动程序。它称为PyMongo,其用法类似于标准 SQL 查询。

安装 PyMongo 模块后,我们需要 MongoClient 类的对象来与 MongoDB 服务器交互。

<<< from pymongo import MongoClient
<<< client=MongoClient() 

使用以下语句创建新数据库 -

db=client.mydatabase 

该数据库上的 CRUD 操作是通过 insert_one() (或 insert_many())、find()、update() 和 delete() 等方法执行的。PyMongo 库的详细讨论可参见https://www.tutorialspoint.com/python_data_access/python_mongodb_introduction.htm

然而,Python的用户定义对象不能存储在数据库中,除非转换为MongoDB的数据类型。这就是我们需要MongoEngine库的地方。

MongoEngine - 对象文档映射器

MongoDB 是一个基于文档的数据库。每个文档都是类似 JSON 的字段和值表示。MongoDB 中的文档大致相当于 RDBMS 表中的一行(MongoDB 相当于表是 Collection)。尽管 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

  • 六>=1.10.0

  • dateutil>=2.1.0

  • Pillow>=2.0.0

要验证安装是否正确,请运行导入命令并检查版本,如下所示 -

>>> import mongoengine
>>> mongoengine.__version__
'0.19.1'

连接到 MongoDB 数据库

如前所述,您应该首先使用 mongod 命令启动 MongoDB 服务器。

MongoEngine 提供 connect() 函数来连接到正在运行的 mongodb 服务器实例。

从 mongoengine 导入连接
连接('mydata.db')

默认情况下,MongoDB 服务器在本地主机和端口 27017 上运行。要进行自定义,您应该为 connect() 提供主机和端口参数 -

连接('mydata.db',主机='192.168.1.1',端口=12345)

如果数据库需要身份验证,则应提供其凭据,例如用户名、密码和authentication_source 参数。

连接('mydata.db',用户名='user1',密码='***',authentication_source='admin')

MongoEngine 还支持 URI 样式连接而不是 IP 地址。

连接('mydata.db',host='mongodb://localhost/database_name')

connect() 函数还有另一个可选参数,称为replicaset。MongoDB 是一个分布式数据库。存储在一台服务器中的数据通常会复制到许多服务器实例中,以确保高可用性。MongoDB 中的副本集是一组维护相同数据集的 mongod 进程。副本集是所有生产部署的基础。

连接(主机='mongodb://localhost/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 类的对象。以下是典型文档类的示例 -

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 中的模型类。默认情况下,数据库中Collection的名称是Python类的名称,并将其名称转换为小写。但是,可以在 Document 类的元属性中指定不同的集合名称。

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 来使用 Capped Collection。

max_documents - 允许存储在集合中的最大文档数。

max_size - 集合的最大大小(以字节为单位)。max_size 之前由 MongoDB 内部和 mongoengine 向上舍入为 256 的下一个倍数。

如果未指定 max_size 而指定了 max_documents,则 max_size 默认为 10485760 字节 (10MB)。

Document 类的其他参数列出如下 -

物体 在访问时延迟创建的 QuerySet 对象。
级联保存() 递归保存文档上的所有引用和通用引用。
干净的() 用于在运行验证之前进行文档级数据清理的挂钩。
创建索引() 如果需要,创建给定的索引。
drop_collection() 从数据库中删除与此文档类型关联的整个集合。
from_json() 将 json 数据转换为 Document 实例。
调整() 对数据库中的文档执行Atomics更新,并使用更新后的版本重新加载文档对象。
PK 获取主键。
节省() 将文档保存到数据库。如果文档已经存在,则更新该文档,否则创建该文档。返回保存的对象实例。
删除() 从数据库中删除当前文档。
插入() 执行批量插入操作。

MongoEngine - 字段

MongoEngine 文档类具有一个或多个属性。每个属性都是 Field 类的一个对象。BaseField 是基类或所有字段类型。BaseField 类构造函数具有以下参数 -

BaseField(db_field,必填,默认,唯一,主键)

db_field表示数据库字段的名称。

required 参数决定该字段的值是否为必填项,默认为 false。

默认参数包含该字段的默认值

unique参数默认为 false 如果您希望此字段对于每个文档都具有唯一值,则设置为 true。

Primary_key参数默认为 false True 使该字段成为主键。

有许多从 BaseField 派生的 Field 类。

数字字段

IntField(32 位整数)、LongField(64 位整数)、FloatField(浮点数)字段构造函数具有 min_value 和 max_value 参数。

还有DecimalField类。该字段对象的值是一个浮点数,其精度可以指定。以下参数是为 DecimalField 类定义的 -

DecimalField(最小值、最大值、force_string、精度、舍入)
最小值 指定最小可接受值
最大值 指定字段可以具有的最大值
强制字符串 如果为 True,则该字段的值存储为字符串
精确 将浮动表示形式限制为位数
四舍五入 根据以下预定义常量对数字进行舍入: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 字符作为电子邮件的一部分。allowed_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()

执行上述代码时,学生集合显示如下文档 -

文档

列表字段

这种类型的字段包装任何标准字段,从而允许将多个对象用作数据库中的列表对象。该字段可以与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 类的对象存储一个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"}
            }
}

参考字段

MongoDB 文档可以使用此类字段存储对另一个文档的引用。这样,我们就可以像 RDBMS 中那样实现 join。ReferenceField 构造函数使用其他文档类的名称作为参数。

class doc1(Document):
   field1=StringField()
class doc2(Document):
   field1=StringField()
   field2=ReferenceField(doc1)

在以下示例中,StudentDB 数据库包含两个文档类:学生和教师。学生类的文档包含对教师类对象的引用。

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) - 从引用列表字段中拉出引用

您可以使用引用列表实现一对多关系。假设学生文档必须与一个或多个教师文档相关,则 Student 类必须具有 ReferenceField 实例的 ListField。

从 mongoengine 导入 *
连接('studentDB')
班主任(文件):
tid=StringField(必需=True)
名称=字符串字段()
班级学生(文件):
   sid = StringField(必需=True)
   名称 = 字符串字段()
   tid=ListField(ReferenceField(老师))
t1=老师()
t1.tid='T1'
t1.name='穆蒂'
t1.保存()
t2=老师()
t2.tid='T2'
t2.name='萨克塞纳'
t2.保存()
s1=学生()
s1.sid='S1'
s1.name='莫罕'
s1.tid=[t1,t2]
s1.save()

在 Compass 中验证上述代码的结果时,您会发现学生文档引用了两个教师文档 -

教师收藏
{
"_id":{"$oid":"5eaebcb61ae527e0db6d15e4"},
"tid":"T1","name":"Murthy"
}
{
"_id":{"$oid":"5eaebcb61ae527e0db6d15e5"},
"tid":"T2","name":"萨克塞纳"
}
学生收藏
{
"_id":{"$oid":"5eaebcb61ae527e0db6d15e6"},
"sid":"S1","name":"莫罕",
"tid":[{"$oid":"5eaebcb61ae527e0db6d15e4"},{"$oid":"5eaebcb61ae527e0db6d15e5"}]
}

日期时间字段

DateTimeField 类的实例允许 MongoDB 数据库中的日期格式的数据。MongoEngine 寻找 Python-DateUtil 库来解析适当日期格式的数据。如果当前安装中不可用,则使用内置时间模块的 time.strptime() 函数表示日期。该类型字段的默认值是当前日期时间实例。

动态场

该字段可以处理不同类型的数据。这种类型的字段由DynamicDocument类内部使用。

图像场

这种类型的字段对应于文档中可以存储图像文件的字段。此类的构造函数可以接受 size 和thumbnail_size 参数(均以像素大小表示)。

MongoEngine - 添加/删除文档

我们已经使用 Document 类的 save() 方法在集合中添加文档。save() 方法可以借助以下参数进一步自定义 -

强制插入 默认值为 False,如果设置为 True 则不允许更新现有文档。
证实 验证文件;设置为 False 以跳过。
干净的 调用文档清理方法,验证参数应为 True。
写关注 将用作生成的 getLastError 命令的选项。例如,save(..., write_concern={w: 2, fsync: True}, ...) 将等待,直到至少两个服务器记录了写入,并将在主服务器上强制进行 fsync。
级联 设置级联保存的标志。您可以通过在文档 __meta__ 中设置“cascade”来设置默认值。
级联_kwargs 要传递的可选关键字参数抛出到级联保存。相当于级联=True。
_refs 级联保存中使用的已处理引用的列表
保存条件 仅当数据库中的匹配记录满足条件时才执行保存。如果不满足条件则引发操作错误
信号_kwargs 要传递给信号调用的 kwargs 字典。

您可以在调用 save() 之前设置用于验证文档的清理规则。通过提供自定义clean()方法,您可以进行任何预验证/数据清理。

class MyDocument(Document):
   ...
   ...
   
   def clean(self):
      if <condition>==True:
         msg = 'error message.'
         raise ValidationError(msg)

请注意,仅当验证打开且调用 save() 时才会调用 Cleaning。

Document 类还具有insert()方法来执行批量插入。它有以下参数 -

文档或文档 要插入的文档或文档列表
批量加载 如果为 True,则返回文档实例的列表
写关注 额外的关键字参数被传递给 insert() ,它将用作结果 getLastError 命令的选项。
信号_kwargs (可选)要传递给信号调用的 kwargs 字典

如果文档包含任何 ReferenceField 对象,则默认情况下 save() 方法不会保存对这些对象的任何更改。如果您还希望保存所有引用,请注意每个保存都是一个单独的查询,然后将cascade 作为 True 传递给 save 方法将级联任何保存。

通过调用delete() 方法,从文档集合中删除文档非常简单。请记住,只有先前保存过文档,它才会生效。delete() 方法有以下参数 -

信号_kwargs (可选)要传递给信号调用的 kwargs 字典。
写关注 额外的关键字参数被向下传递,这些参数将用作生成的 getLastError 命令的选项。

要从数据库中删除整个集合,请使用drop_collection()方法。它从数据库中删除与此文档类型关联的整个集合。如果文档没有集合集(如果它是抽象的,则该方法会引发OperationError)。

文档类中的modify ()方法对数据库中的文档执行Atomics更新并重新加载其更新版本。如果文档已更新,则返回 True;如果数据库中的文档与查询不匹配,则返回 False。请注意,如果该方法返回 True,则对文档所做的所有未保存的更改都将被拒绝。

参数

询问 仅当数据库中的文档与查询匹配时才会执行更新
更新 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 类具有对象属性,可以访问与数据库关联的对象。

newdb 数据库有一个与下面的 Document 类相对应的产品集合。为了获取所有文档,我们使用对象属性,如下所示 -

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 - 过滤器

对象属性是一个查询集管理器。它在访问时创建并返回一个 QuerySet。可以借助字段名称作为关键字参数来对查询进行过滤。例如,在上面的产品集合中,要打印产品名称为“TV”的文档的详细信息,我们使用 Name 作为关键字参数。

for product in products.objects(Name='TV'):
print ('ID:',product.ProductID, 'Name:',product.Name, 'Price:',product.price)

您可以使用 QuerySet 对象的 filter 方法将过滤器应用于查询。以下代码片段还返回 name='TV' 的产品详细信息。

qset=products.objects
for product in qset.filter(Name='TV'):
   print ('ID:',product.ProductID, 'Name:',product.Name, 'Price:',product.price)

MongoEngine - 查询运算符

除了用于检查相等性的 = 运算符之外,MongoEngine 中还定义了以下逻辑运算符。

不等于
少于
LTE 小于或等于
GT 比...更棒
通用电气 大于或等于
不是 否定标准检查,可以在其他运算符之前使用
值在列表中
值不在列表中
模组 value % x == y,其中 x 和 y 是两个提供的值
全部 提供的值列表中的每个项目都在数组中
尺寸 数组的大小是
存在 字段值存在

这些运算符必须附加到带有双下划线 __ 的字段名称。

要使用大于 (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

您可以使用以下运算符作为正则表达式的快捷方式,以将过滤器应用于查询 -

精确的 字符串字段与值完全匹配
精确的 字符串字段与值完全匹配(不区分大小写)
包含 字符串字段包含值
图标包含 字符串字段包含值(不区分大小写)
以。。开始 字符串字段以值开头
开始于 字符串字段以值开头(不区分大小写)
以。。结束 字符串字段以值结尾
朋友 字符串字段以值结尾(不区分大小写)
匹配 执行 $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 对象拥有以下用于查询数据库的有用方法。

第一的()

返回满足查询的第一个文档。以下代码将返回产品集合中价格 < 20000 的第一个文档。

qset=products.objects(price__lt=20000)
doc=qset.first()
print ('Name:',doc.Name, 'Price:',doc.price)

输出

Name: Router Price: 2000

排除()

这将导致提到的字段从查询集中排除。这里,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}

字段()

使用此方法可以操作要在查询集中加载哪些字段。使用字段名称作为关键字参数,并设置为 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 的工作方式与 except() 方法类似。

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"}

仅有的()

该方法的效果与 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"}

和()

此方法计算查询集中给定字段的总和。

平均的()

该方法计算查询集中给定字段的平均值。

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 - 自定义查询集

默认情况下,文档类上的对象属性返回一个查询集,而不应用任何过滤器。但是,您可以在修改查询集的文档上定义类方法。这样的方法应该接受两个参数 - doc_cls 和 queryset,并且需要用 queryset_manager() 进行修饰才能被识别。

@queryset_manager
   def qry_method(docs_cls,queryset):
       ….
       ----

在下面的示例中,名为 products 的文档类具有一个 由 @queryset_manager 修饰的昂贵_prods() 方法。该方法本身对查询集应用过滤器,以便仅返回价格 >20000 的对象。此方法现在是默认文档查询,产品类的对象属性返回过滤后的文档。

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 类的子类,并将其用作元字典中的 queryset_class 属性的值。

下面的示例使用 MyQuerySet 类作为自定义查询集的定义。此类中的 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 类定义的元字典中指定“indexes”键来创建索引。

Indexes 属性的值是字段列表。在下面的示例中,我们要求学生集合中的文档根据名称字段建立索引。

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()

默认情况下,索引顺序为升序。可以通过在前面添加“+”(表示升序)或“-”(表示降序)来指定顺序。

要创建复合索引,请使用字段名称元组,可以选择附加 + 或 – 符号来指示排序顺序。

在下面的示例中,学生文档类包含名称和课程的复合索引的定义(注意 - 课程字段前面的符号,这意味着索引是按名称升序和按课程降序构建的。

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 将显示索引如下 -

指数价值

“索引”的值可能是各种选项的字典,如下所示 -

领域 要索引的字段。
CLS 如果allow_inheritance打开,您可以配置索引是否应该自动添加_cls字段。
索引是否应该稀疏。
独特的 索引是否应该是唯一的。
秒后过期 通过设置时间(以秒为单位)自动使集合中的数据过期
姓名 允许您指定索引的名称
整理 允许创建不区分大小写的索引

以下示例在名称字段上创建索引,该索引将在 3600 秒后过期。

from mongoengine import *
con=connect('mydata')

class student(Document):
   name=StringField(required=True)
   course=StringField()
   meta = {'indexes':[{
            'fields': ['name'],
            'expireAfterSeconds': 3600
           }
    ]
}

要指定文本索引,请在字段名称前添加“$”符号,对于散列索引,请使用“#”作为前缀。

当文档添加到集合中时,会自动创建如此指定的索引。要禁用自动创建,请在元属性中将“ auto_create_index ”设置为 False。

我们有Document 类的list_indexes()方法,用于显示可用索引的列表。

print (student.list_indexes())

[[('name', 1)], [('_id', 1)]]

要在元字典中没有的字段上创建索引,请使用create_index()方法。以下代码将在课程字段上创建索引 -

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 管道创建中定义了以下阶段 -

姓名 描述
$项目 通过添加新字段或删除现有字段来重塑流中的每个文档。
$匹配 过滤文档流以仅允许匹配的文档未经修改地传递到下一阶段。$match 使用标准 MongoDB 查询。
$编辑 通过根据文档本身存储的信息限制每个文档的内容来重塑每个文档。
$限额 限制不加修改地传递到管道的文档
$跳过 跳过前 n 个文档,并将未修改的剩余文档传递到管道。
$组 按给定标识符表达式对输入文档进行分组,并将累加器表达式应用于每个组。输出文档仅包含标识符字段和累积字段。
$排序 按指定的排序键对文档流重新排序。
$出 将聚合管道的结果文档写入集合。

聚合表达式使用字段路径来访问输入文档中的字段。要指定字段路径,请使用在字段名称前面加上美元符号 $$$ 前缀的字符串。表达式可以使用一个或多个布尔运算符($and、$or、$not)和比较运算符($eq、$gt、$lt、$gte、$lte 和 $ne)。

以下算术表达式也用于聚合 -

$添加 添加数字以返回总和。接受任意数量的参数表达式
$减去 返回第一个值减去第二个值的结果
$乘 将数字相乘以返回乘积。接受任意数量的参数表达式
$除法 返回第一个数字除以第二个数字的结果。接受两个参数表达式
$mod 返回第一个数字除以第二个数字的余数。接受两个参数表达式

以下字符串表达式也可以用于聚合 -

$连接 连接任意数量的字符串
$子字符串 返回字符串的子字符串,从指定索引位置开始直到指定长度
降低$ 将字符串转换为小写。接受单个参数表达式
$到上层 将字符串转换为大写。接受单个参数表达式
$strcasecmp 执行字符串比较,如果两个字符串相等则返回 0,如果第一个字符串大于第二个字符串则返回 1,如果第一个字符串小于第二个字符串则返回 -1

为了演示aggregate()函数在MongoEngine中的工作原理,我们首先定义一个名为orders的Document类。

from mongoengine import *
con=connect('mydata')

class orders(Document):
   custID = StringField()
   amount= IntField()
   status = StringField()

然后我们在订单集合中添加以下文档 -

_ID 客户ID 数量 地位
对象 ID("5eba52d975fa1e26d4ec01d0") A123 500 A
对象 ID("5eba536775fa1e26d4ec01d1") A123 250 A
对象 ID(“5eba53b575fa1e26d4ec01d2”) B212 200 D
对象 ID("5eba540e75fa1e26d4ec01d3") B212 400 A

仅当状态等于“A”时,aggregate() 函数才用于查找每个 custID 的金额字段总和。因此,管道构造如下。

管道的第一阶段使用 $match 来过滤 status='A' 的文档。第二阶段使用 $group 标识符对 CustID 上的文档进行分组并执行金额求和。

 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

注意- 城市属性的值用作默认值。由于未指定 PIN 默认值,因此打印 None。

如果需要缺失字段,可以调用 reload() 函数。

当文档类具有 ListField 或 DictField 时,在迭代它时,任何 DBREf 对象都会自动取消引用。为了进一步提高效率,特别是在文档有ReferenceField的情况下,可以使用select_lated()函数来限制查询数量,该函数将QuerySet转换为列表并实现取消引用。

MongoEngine API 包含 Q 类,可用于构建由多个约束组成的高级查询。Q 表示查询的一部分,可以通过关键字参数语法和二进制 & 和 | 来初始化。运营商。

person.objects(Q(name__startswith=’H’) &Q(city=’Mumbai’))

MongoEngine - 文档继承

可以定义任何用户定义的 Document 类的继承类。如果需要,继承的类可以添加额外的字段。但是,由于此类不是 Document 类的直接子类,因此它不会创建新的集合,而是将其对象存储在其父类使用的集合中。在父类中,元属性' allow_inheritance下面的示例中,我们首先将employee定义为文档类并将allow_inheritance设置为true。工资等级源自employee,增加了两个字段dept和sal。Employee 对象以及工资类别存储在雇员集合中。

在下面的示例中,我们首先将employee定义为文档类并将allow_inheritance设置为true。工资等级源自employee,增加了两个字段dept和sal。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()

我们可以验证两个文档存储在员工集合中,如下所示 -

{
"_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 - Atomics更新

Atomics性是 ACID 事务属性之一。数据库事务必须是不可分割和不可简化的,以便它要么完全发生,要么根本不发生。这个属性称为Atomics性。MongoDB 仅支持单个文档的Atomics性,而不支持多文档事务。

MongoEngine 提供以下方法用于查询集的Atomics更新。

update_one() - 覆盖或添加与查询匹配的第一个文档。

update() - 对查询匹配的字段执行Atomics更新。

modify() - 更新文档并返回它。

这些方法可以使用以下修饰符。(这些修饰符位于字段之前,而不是之后)。

设置一个特定值
未设置 删除特定值
公司 将值增加给定量
十二月 将值减少给定量
将值附加到列表中
推送全部 将多个值附加到列表中
流行音乐 根据值删除列表的第一个或最后一个元素
从列表中删除一个值
全部拉动 从列表中删除多个值
添加到集合 仅当列表中尚不存在时才将值添加到列表中

下面是一个Atomics更新的例子,我们首先创建一个名为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()方法将名称字段从 XYZ 更新到 MongoDB。

tests.objects(name='XYZ').update_one(set__name='MongoDB')

Push 修饰符用于在 ListField(分数)中添加数据。

tests.objects(name='MongoDB').update_one(push__scores=50)

要将 attempts 字段加一,我们可以使用 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 中访问)

此外,还为函数的作用域提供了更多变量,如下所示 -

  • collection - 对应于 Document 类的集合的名称。这应该用于在 Javascript 代码中从数据库获取 Collection 对象。

  • query - QuerySet 对象生成的查询;传递到 Javascript 函数中 Collection 对象的 find() 方法。

  • options - 包含传递给 exec_js() 的关键字参数的对象。

请注意,MongoEngine 文档类中的属性可能在数据库中使用不同的名称(使用 Field 构造函数的 db_field 关键字参数进行设置)。