MongoEngine中文文档


MongoEngine中文文档 

MongoEngine是一个基于pymongo开发的ODM库,对应与SQLAlchemy。同时,在MongoEngine基础上封装了Flask-MongoEngine,用于支持flask框架。

官方地址:http://docs.mongoengine.org/index.html

入门教程

pip3 install mongoengine

2.连接至MongoDB

-2.11 方法一 连接本地数据库

from mongoengine import connect
connect('dbname', host='远程服务器IP地址', post=开放的端口号)

-2.13 方法三 连接带有验证的远程数据库

from mongoengine import connect

# Regular connect
connect('dbname', replicaset='rs-name')

# URI风格连接
connect(host='mongodb://localhost/dbname?replicaSet=rs-name')

2.3 连接至多个数据库

通过在元数据中提供db_alias,可以将各个文档附加到不同的数据库 。这允许DBRef 对象指向数据库和集合。下面是一个示例模式,使用3个不同的数据库来存储数据

connect (alias = 'user-db-alias' , db = 'user-db' )
connect (alias = 'book-db-alias' , db = 'book-db' )
connect (alias = 'users-books-db -alias' , db = 'users-books-db'class  UserDocument ):
    name  =  StringField ()
    meta  =  { 'db_alias''user-db-alias' } 

class  BookDocument ):
    name  =  StringField ()
    meta  =  { 'db_alias''book-db-alias' } 

class  AuthorBooksDocument ):
    author  =  ReferenceField (User )
    book  =  ReferenceField (Book )
    meta  =  { 'db_alias'' users-books-db-alias' }

-2.32 断开现有连接

有时您可能希望切换数据库或集合以进行查询

- 2.4.1 切换数据库 switch_db()

switch_collection()上下文管理器允许更改集合,允许快速和方便地跨集合访问:

from mongoengine.context_managers import switch_collection

class Group(Document):
    name = StringField()

Group(name='test').save()  # 保存至默认数据库

with switch_collection(Group, 'group2000') as Group:
    Group(name='hello Group 2000 collection!').save()  # 将数据保存至 group2000 集合

3.【定义文档 Defining Documents】

MongoEngine允许为文档定义模式,因为这有助于减少编码错误,并允许在可能存在的字段上定义方法。
为文档定义模式,需创建一个继承自Document的类,将字段对象作为类属性添加到文档类:

from mongoengine import *
import datetime

class Page(Document):
    title = StringField(max_length=200, required=True)      # ===》 创建一个String型的字段title,最大长度为200字节且为必填项
    date_modified = DateTimeField(default=datetime.datetime.utcnow)      # ===》 创建一个时间类型的字段(utcnow是世界时间,now是本地计算机时间)

3.2、定义“动态”文档模型 Defining a dynamic document’s schema

字段类型包含:(以“》”开头的是常用类型,“》”仅用于标注)

BinaryField    #  二进制字段BooleanField     # 布尔型字段
》DateTimeField    # 后六位精确到毫妙的时间类型字段
ComplexDateTimeField   # 后六位精确到微妙的时间类型字段
DecimalField    # DictField    # 字典类型字段
》DynamicField   # 动态类型字段,能够处理不同类型的数据
》EmailField     # 邮件类型字段
》EmbeddedDocumentField   # 嵌入式文档类型
》StringField    # 字符串类型字段
》URLField    # URL类型字段
》SequenceField     # 顺序计数器字段,自增长
》ListField       # 列表类型字段
》ReferenceField    # 引用类型字段
LazyReferenceField
》IntField     # 整数类型字段,存储大小为32字节
LongField      # 长整型字段,存储大小为64字节
EmbeddedDocumentListField
FileField  #  列表类型字段
FloatField   # 浮点数类型字段
GenericEmbeddedDocumentField   # 
GenericReferenceField
GenericLazyReferenceField
GeoPointField
ImageField
MapField
ObjectIdField
SortedListField  
UUIDField
PointField
LineStringField
PolygonField
MultiPointField
MultiLineStringField
MultiPolygonField

- 3.3.1 字段通用参数

使用ListField字段类型可以向 Document添加项目列表。ListField将另一个字段对象作为其第一个参数,该参数指定可以在列表中存储哪些类型元素:
「举例」

class  PageDocument ):
    tags  =  ListField (StringField (max_length = 50 ))   # ===》 ListField中存放字符串字段
# 应该可以存放任意类型字段

- 3.3.3 内嵌文档 Embedded Document

不知道想要存储什么结构时,可以使用字典字段(字典字段不支持验证),字典可以存储复杂数据,其他字典,列表,对其他对象的引用,因此是最灵活的字段类型:
「举例」

class SurveyResponse(Document):
    date = DateTimeField()
    user = ReferenceField(User)     # ===》 引用字段,引用User类
    answers = DictField()

survey_response = SurveyResponse(date=datetime.utcnow(), user=request.user)
response_form = ResponseForm(request.POST)      # ===》 这段和下一段代码,我没看明白
survey_response.answers = response_form.cleaned_data()        # ===》 这段和上一段代码,我没看明白
survey_response.save()

- 3.3.5 引用字段 Reference fields

「举例」

class User(Document):
    name = StringField()

class Page(Document):
    content = StringField()
    authors = ListField(ReferenceField(User))

bob = User(name="Bob Jones").save()
john = User(name="John Smith").save()

Page(content="Test Page", authors=[bob, john]).save()
Page(content="Another Page", authors=[john]).save()

# 查找authored中包含  Bob 的文档
Page.objects(authors__in=[bob])

# 查找authored中包含  Bob 和 john 的文档
Page.objects(authors__all=[bob, john])

# 从 被引用的文档中删除作者 bob
Page.objects(id='...').update_one(pull__authors=bob)

# 将John添加到被引用文档的作者列表中
Page.objects(id='...').update_one(push__authors=john)

3.3.5.2 删除引用文档

GenericReferenceField允许您引用任何类型Document,因此不需要将 Document子类作为构造函数参数(也可以使用选项参数来限制可接受的文档类型):
「举例」

class Link(Document):
    url = StringField()

class Post(Document):
    title = StringField()

class Bookmark(Document):
    bookmark_object = GenericReferenceField()

link = Link(url='http://hmarr.com/mongoengine/')
link.save()

post = Post(title='Using MongoEngine')
post.save()

Bookmark(bookmark_object=link).save()
Bookmark(bookmark_object=post).save()

??注:使用GenericReferenceFields的效率略低于标准ReferenceFields,因此如果只引用一种文档类型,则更推荐使用 ReferenceField

- 3.3.6 唯一性约束 Uniqueness constraints

可以通过设置validate=False,调用save() 方法时跳过整个文档验证过程 :
「举例」

class Recipient(Document):
    name = StringField()
    email = EmailField()

recipient = Recipient(name='admin', email='root@localhost')
recipient.save()               # 会抛出 ValidationError 错误
recipient.save(validate=False)     #  不会抛出错误并会持久化数据

3.4 文档集

一个集合可存储大小的上限默认为10m,但可通过max_size来设置集合可存储大小,同时也可使用max_documents来设置最大文档数量:
「举例」

class Log(Document):
    ip_address = StringField()
    meta = {'max_documents': 1000, 'max_size': 2000000}    # ====》 最大文档数为1000个,存储大小为200万字节

3.5 索引 Indexes

class Page(Document):
    title = StringField()
    rating = StringField()
    meta = {
        'index_opts': {},
        'index_background': True,
        'index_cls': False,
        'auto_create_index': True,
        'index_drop_dups': True,
    }

「参数说明」
index_opts (可选)
设置默认索引选项
index_background (可选)
index_background=True时,在后台创建索引
index_cls (可选)
一种关闭_cls的特定索引的方法。
auto_create_index (可选)
当这是True(默认)时,MongoEngine将确保每次运行命令时MongoDB中都存在正确的索引。可以在单独管理索引的系统中禁用此功能。禁用此功能可以提高性能。

- 3.5.2 复合索引和索引子文档

原文:Geospatial indexes will be automatically created for all GeoPointFields
译文:将自动为所有GeoPointFields 创建地理空间索引

原文:It is also possible to explicitly define geospatial indexes. This is useful if you need to define a geospatial index on a subfield of a DictField or a custom field that contains a point. To create a geospatial index you must prefix the field with the * sign.
译文:也可以明确定义地理空间索引。如果您需要在DictField包含点的自定义字段的子字段上定义地理空间索引,这将非常有用 。要创建地理空间索引,必须在字段前加上 * 符号。

class  PlaceDocument ):
    location  =  DictField ()
    meta  =  { 
        'indexes' : [ 
            '* location.point' ,
        ],
    }

- 3.5.4. 生存时间索引 Time To Live indexes

原文:Use mongoengine.Document.compare_indexes() to compare actual indexes in the database to those that your document definitions define. This is useful for maintenance purposes and ensuring you have the correct indexes for your schema.
译文:
用mongoengine.Document.compare_indexes()实际索引在数据库中的那些文档定义进行比较。这对于维护目的很有用,并确保您拥有正确的架构索引。
将MongoEngine中定义的索引与数据库中存在的索引进行比较。返回任何缺失/额外索引。

3.6 排序 Ordering

原文:If your collection is sharded by multiple keys, then you can improve shard routing (and thus the performance of your application) by specifying the shard key, using the shard_key attribute of meta. The shard key should be defined as a tuple.

This ensures that the full shard key is sent with the query when calling methods such as save(), update(), modify(), or delete() on an existing Document instance:

如果集合是通过多个键进行分片,那么可以通过使用shard_key属性 来指定分片键来改进分片路由(以及应用程序的性能)meta。分片键应定义为元组。

class LogEntry(Document):
    machine = StringField()
    app = StringField()
    timestamp = DateTimeField()
    data = StringField()

    meta = {
        'shard_key': ('machine', 'timestamp'),
        'indexes': ('machine', 'timestamp'),
    }

3.8 文档继承 Document inheritance

由于MongoEngine不再默认需要_cls,您可以快速轻松地使用现有数据。只需定义文档以匹配数据库中的预期模式即可:

# 这个模型将在集合名为 'cmsPage'工作
class Page(Document):
    title = StringField(max_length=200, required=True)
    meta = {
        'collection': 'cmsPage'
    }

原文:If you have wildly varying schemas then using a DynamicDocument might be more appropriate, instead of defining all possible field types.
译文:如果现有数据库中存在着各式各样的数据模型,建议使用动态文档 DynamicDocument
If you use Document and the database contains data that isn’t defined then that data will be stored in the document._data dictionary.

3.9 抽象类

实例化一个对象,并提供参数给对象即可创建一个对象:

>>> page = Page(title="Test Page")
>>> page.title
'Test Page'

4.1 持久化和删除文档

class Essay(Document):
    status = StringField(choices=('Published', 'Draft'), required=True)
    pub_date = DateTimeField()

    def clean(self):
        """确保只发布的论文有'pub_date`并
        自动设置`如果pub_date`论文发表和'pub_date` 
        未设置"""
        if self.status == 'Draft' and self.pub_date is not None:
            msg = 'Draft entries should not have a publication date.'
            raise ValidationError(msg)
        #  设置已发布项目的pub_date(如果未设置).
        if self.status == 'Published' and self.pub_date is None:
            self.pub_date = datetime.now()

- 4.1.2 级联保存 Cascading Saves

delete(signal_kwargs=None, **write_concern)

「参数说明」
signal_kwargs - (可选)要传递给信号调用的kwargs字典。
write_concern - 向下传递额外的关键字参数,这些参数将用作结果getLastError命令的选项。例如,将等到至少两个服务器已记录写入并将强制主服务器上的fsync。save(…, w: 2, fsync: True)
「举例」

p=Post.objects(title='test_title').first()
p.delete()

4.2 文档ID Document IDs

Document类具有一个objects属性,用于访问与类关联的数据库中的对象。该objects属性实际上是一个 QuerySetManager,QuerySet在访问时创建并返回一个新 对象。QuerySet可以迭代该 对象以从数据库中获取文档

# 打印出所有User集合中所有文档的用户名
for user in User.objects:
    print(user.name)

5.1 过滤查询 Filtering queries

除了相等运算符外,其它运算符也可以在查询中使用 :

# 查询18岁以下的用户
young_users = Users.objects(age__lte=18)

可用的运算符如下:

ne - 不等于
lt - 少于
lte - 小于或等于
gt - 大于
gte - 大于或等于
not - negate a standard check, may be used before other operators (e.g. Q(age__not__mod=(5, 0)))
in - 值在列表中(应提供值列表)
nin - 值不在列表中(应提供值列表)
mod - value % x == y, where x and y are two provided values
all - 提供的值列表中的每个项目都在数组中
size - 数组的大小是多少
exists - 字段值存在

5.2.1. 字符串查询 String queries

There are a few special operators for performing geographical queries. The following were added in MongoEngine 0.8 for PointField, LineStringField and PolygonField:

  • geo_within – check if a geometry is within a polygon. For ease of use it accepts either a geojson geometry or just the polygon coordinates eg:

「举例」

loc.objects(point__geo_within=[[[40, 5], [40, 6], [41, 6], [40, 5]]])
loc.objects(point__geo_within={"type": "Polygon",
                         "coordinates": [[[40, 5], [40, 6], [41, 6], [40, 5]]]})
  • geo_within_box – simplified geo_within searching with a box eg:
loc.objects(point__geo_within_box=[(-125.0, 35.0), (-100.0, 40.0)])
loc.objects(point__geo_within_box=[, ])
  • geo_within_polygon – simplified geo_within searching within a simple polygon eg:
loc.objects(point__geo_within_polygon=[[40, 5], [40, 6], [41, 6], [40, 5]])
loc.objects(point__geo_within_polygon=[ [  ,  ] ,
                                        [  ,  ] ,
                                        [  ,  ] ])
  • geo_within_center – simplified geo_within the flat circle radius of a point eg:
loc.objects(point__geo_within_center=[(-125.0, 35.0), 1])
loc.objects(point__geo_within_center=[ [ ,  ] ,  ])
  • geo_within_sphere – simplified geo_within the spherical circle radius of a point eg:
loc.objects(point__geo_within_sphere=[(-125.0, 35.0), 1])
loc.objects(point__geo_within_sphere=[ [ ,  ] ,  ])
  • geo_intersects – selects all locations that intersect with a geometry eg:
# Inferred from provided points lists:
loc.objects(poly__geo_intersects=[40, 6])
loc.objects(poly__geo_intersects=[[40, 5], [40, 6]])
loc.objects(poly__geo_intersects=[[[40, 5], [40, 6], [41, 6], [41, 5], [40, 5]]])

# With geoJson style objects
loc.objects(poly__geo_intersects={"type": "Point", "coordinates": [40, 6]})
loc.objects(poly__geo_intersects={"type": "LineString",
                                  "coordinates": [[40, 5], [40, 6]]})
loc.objects(poly__geo_intersects={"type": "Polygon",
                                  "coordinates": [[[40, 5], [40, 6], [41, 6], [41, 5], [40, 5]]]})
  • near – find all the locations near a given point:
loc.objects(point__near=[40, 5])
loc.objects(point__near={"type": "Point", "coordinates": [40, 5]})
  • You can also set the maximum and/or the minimum distance in meters as well:
loc.objects(point__near=[40, 5], point__max_distance=1000)
loc.objects(point__near=[40, 5], point__min_distance=100)

- 5.2.3. 列表查询 Querying lists

如果希望使用pymongo操作数据库,可以使用__raw__关键字:
「例子」

Page.objects(__raw__={'tags': 'coding'})

??注意:此时需要您学习pymongo https://api.mongodb.com/python/current/

5.3. 限制与跳过查询 Limiting and skipping results

原文:
To retrieve a result that should be unique in the collection, use get(). This will raise DoesNotExist if no document matches the query, and MultipleObjectsReturned if more than one document matched the query. These exceptions are merged into your document definitions eg: MyDoc.DoesNotExist

A variation of this method, get_or_create() existed, but it was unsafe. It could not be made safe, because there are no transactions in mongoDB. Other approaches should be investigated, to ensure you don’t accidentally duplicate data when using something similar to this method. Therefore it was deprecated in 0.8 and removed in 0.10.

5.4. 默认文档查询 Default Document queries

如果要添加自定义方法交互或过滤文档,可以继续扩展类QuerySet。要使用自定义的QuerySet文档类,需要在Document模型中的meta词典设置queryset_class:
「例子」

class AwesomerQuerySet(QuerySet):

    def get_awesome(self):
        return self.filter(awesome=True)

class Page(Document):
    meta = {'queryset_class': AwesomerQuerySet}      ======》设置 queryset_class

# 调用:
Page.objects.get_awesome()

5.6 聚合 Aggregation

就像限制和跳过结果一样,QuerySet对象上有一个方法 - count():
??注意:虽然.count()跟len(列表对象)计算结果一样,但是count()函数性能优于len()

num_users = User.objects.count()

- 5.6.2 进一步聚合 Further aggregation

- 5.7.1 搜索文档子集(例如引用文档或者嵌入式文档) Retrieving a subset of fields

「原文」When iterating the results of ListField or DictField we automatically dereference any DBRef objects as efficiently as possible, reducing the number the queries to mongo.

There are times when that efficiency is not enough, documents that have ReferenceField objects or GenericReferenceField objects at the top level are expensive as the number of queries to MongoDB can quickly rise.

To limit the number of queries use select_related() which converts the QuerySet to a list and dereferences as efficiently as possible. By default select_related() only dereferences any references to the depth of 1 level. If you have more complicated documents and want to dereference more of the object at once then increasing the max_depth will dereference more levels of the document.

- 5.7.3 Turning off dereferencing

如果希望通过 or 或者 and 来多条件查询时,需要使用 Q(条件语句1) | Q(条件语句2) Q(条件语句1) | Q(条件语句2)
「举例」

from mongoengine.queryset.visitor import Q

# 获取已发布的文档
Post.objects(Q(published=True) | Q(publish_date__lte=datetime.now()))

# 获取 featured为真 同时 hits大于等于1000或大于等于5000  的文档
Post.objects((Q(featured=True) & Q(hits__gte=1000)) | Q(hits__gte=5000))

5.9 原子更新 Atomic updates

「原文」
Javascript functions may be written and sent to the server for execution. The result of this is the return value of the Javascript function. This functionality is accessed through the exec_js() method on QuerySet() objects. Pass in a string containing a Javascript function as the first argument.

The remaining positional arguments are names of fields that will be passed into you Javascript function as its arguments. This allows functions to be written that may be executed on any field in a collection (e.g. the sum() method, which accepts the name of the field to sum over as its argument). Note that field names passed in in this manner are automatically translated to the names used on the database (set using the name keyword argument to a field constructor).

Keyword arguments to exec_js() are combined into an object called options, which is available in the Javascript function. This may be used for defining specific parameters for your function.

Some variables are made available in the scope of the Javascript function:

  • collection – the name of the collection that corresponds to the Document class that is being used; this should be used to get the Collection object from db in Javascript code
  • query – the query that has been generated by the QuerySet object; this may be passed into the find() method on a Collection object in the Javascript function
  • options – an object containing the keyword arguments passed into exec_js()

The following example demonstrates the intended usage of exec_js() by defining a function that sums over a field on a document (this functionality is already available through sum() but is shown here for sake of example):
「举例」

def sum_field(document, field_name, include_negatives=True):
    code = """
    function(sumField) {
        var total = 0.0;
        db[collection].find(query).forEach(function(doc) {
            var val = doc[sumField];
            if (val >= 0.0 || options.includeNegatives) {
                total += val;
            }
        });
        return total;
    }
    """
    options = {'includeNegatives': include_negatives}
    return document.objects.exec_js(code, field_name, **options)

「原文」
As fields in MongoEngine may use different names in the database (set using the db_field keyword argument to a Field constructor), a mechanism exists for replacing MongoEngine field names with the database field names in Javascript code. When accessing a field on a collection object, use square-bracket notation, and prefix the MongoEngine field name with a tilde. The field name that follows the tilde will be translated to the name used in the database. Note that when referring to fields on embedded documents, the name of the EmbeddedDocumentField, followed by a dot, should be used before the name of the field on the embedded document. The following example shows how the substitutions are made:

class Comment(EmbeddedDocument):
    content = StringField(db_field='body')

class BlogPost(Document):
    title = StringField(db_field='doctitle')
    comments = ListField(EmbeddedDocumentField(Comment), name='cs')

# Returns a list of dictionaries. Each dictionary contains a value named
# "document", which corresponds to the "title" field on a BlogPost, and
# "comment", which corresponds to an individual comment. The substitutions
# made are shown in the comments.
BlogPost.objects.exec_js("""
function() {
    var comments = [];
    db[collection].find(query).forEach(function(doc) {
        // doc[~comments] -> doc["cs"]
        var docComments = doc[~comments];

        for (var i = 0; i < docComments.length; i++) {
            // doc[~comments][i] -> doc["cs"][i]
            var comment = doc[~comments][i];

            comments.push({
                // doc[~title] -> doc["doctitle"]
                'document': doc[~title],

                // comment[~comments.content] -> comment["body"]
                'comment': comment[~comments.content]
            });
        }
    });
    return comments;
}
""")

6、 GridFS

该类是在数据库发生变化时或者符合订阅标准时出发发送数据的行为
原文地址:http://docs.mongoengine.org/guide/signals.html

第三方库:https://pypi.org/project/blinker/ 第三方库文档:https://pythonhosted.org/blinker/

使用$前缀设置文本索引,查看声明:

class News(Document):
    title = StringField()
    content = StringField()
    is_active = BooleanField()

    meta = {'indexes': [
        {'fields': ['$title', "$content"],
         'default_language': 'english',
         'weights': {'title': 10, 'content': 2}
        }
    ]}

8.2 查询 Querying

objects = News.objects.search_text('mongo').order_by('$text_score')
#  搜索到的文档按 text_score 排序

9、 使用mongomock进行测试

这里有比较详细的API说明和参数说明,还有一些例子
地址: http://docs.mongoengine.org/apireference.html