MongoDB连接

neevop 三月 2, 2023

连接数据库

# python use mongoengine -- pip3 install mongoengine
import mongoengine
mongoengine.connect(host="mongodb://172.16.34.82:27017/test")
# or connect with keywords
mongoengine.connect("test", host="172.16.34.82", port=27017)
# if should use auth
mongoengine.connect(host="mongodb://my_user:my_password@hostname:port/my_db?authSource=admin")
# or with keywords
mongoengine.connect('my_db', username='my_user', password='my_password', authentication_source='admin')
# provide a alias name when connect
mongoengine.connect(alias="db1", db="test")
# disconnect
mongoengine.disconnect()
# disconnect a particular connection
mongoengine,disconnect(alias="test")
// use mongosh -- apt install mongodb-mongosh
// connect to local server
mongosh "mongodb://localhost:27017"
mongosh --port 28015
// connect to remmote deployment
mongosh --host mongodb0.example.com --port 28015
mongosh "mongodb://mongodb0.example.com:28015"
//disconnect
exit

切换数据库

# python
import mongoengine
mongoengine.connect(db="sit", alias="sit", host="172.16.34.82", port=27017)
mongoengine.connect(db="test", alias="test", host="172.16.34.82", port=27017)
class SIT(mongoengine.Document):
    name = mongoengine.StringField()
    meta = {'db_alias': "sit"}
    
class TEST(mongoengine.Document):
    name = mongoengine.StringField()
    meta = {'db_alias': "test"}


from mongoengine import context_managers
# Make sure any aliases have been registered with register_connection() or connect() 
# before using the context manager.
mongoengine.connect(db="result", alias="result", host="172.16.34.82", port=27017)
with context_managers.switch_db(TEST, "result") as TEST:
  	TEST(name='ROSS').save() # saves the "result"
// mongosh
// get all dbs
show dbs
// switch to sit db
use sit

定义集合

# python
from mongoengine import context_managers

# 指定集合
class Page(Document):
    title = StringField(max_length=200, required=True)
    meta = {'collection': 'cmsPage'}

# 切换集合
with context_managers.switch_collection(TEST, "result") as TEST:
  	TEST(name='ROSS').save() # saves the "result"
    
# 固定集合(Capped Collection)
class Log(Document):
    ip_address = StringField()
    meta = {'max_documents': 1000, 'max_size': 2000000}

定义文档

在 MongoDB 中,文档大致相当于关系型数据库中的一行。使用关系数据库时,行存储在表中,表具有行遵循的严格模式(schema)。MongoDB 将文档存储在集合中而不是表中——主要区别在于没有在数据库级别强制执行模式。好的schema通常需要试验和迭代。

MongoEngine 允许您为文档定义模式,因为这有助于减少编码错误,并允许在可能存在的字段上定义实用方法。

要为文档定义架构,请创建一个继承自 Document. 通过将字段对象作为类属性添加到文档类来指定字段

import mongoengine as mg
import datetime

# 连接数据库,创建数据库别名
mg.connect(db="test", alias="test", host="172.16.34.82", port=27017)

# 定义文档模式,定义字段和所属数据库
class Page(mg.Document):
    # 默认情况下,定义的字段不是必填,可以通过设置`required=True`将该字段设置为必填
    title = mg.StringField(max_length=200, required=True)
    # 字段也可以采用默认值,如果未提供值,将使用默认值
    date = mg.DateTimeField(default=datetime.datetime.utcnow())
    meta = {'db_alias': "test"}

# 实例化一个文档
page = Page(title="first documents")

# 指定插入的集合,插入文档
from mongoengine import context_managers
with context_managers.switch_collection(Page, "result"):
    page.save()

结果如下:

test> db.result.find()
[
  {
    _id: ObjectId("63fdeb239f17ff42a578fda0"),
    title: 'first documents',
    date: ISODate("2023-02-28T11:51:46.763Z")
  }
]

mongoengine中定义的字段类型有:

  • BinaryField

  • BooleanField

  • ComplexDateTimeField

  • DateTimeField

  • DecimalField

  • DictField

    字典字段类型可以存储复杂的数据、其他字典、列表、对其他对象的引用,因此是最灵活的可用字段类型。

  • DynamicField

  • EmailField

  • EmbeddedDocumentField

    # 要创建嵌入式文档,只需像往常一样定义文档,但继承自 EmbeddedDocument 而不是 Document
    class Comment(EmbeddedDocument):
        content = StringField()
    # 使用 EmbeddedDocumentField 字段类型,提供嵌入文档类作为第一个参数
    class Page(Document):
        comments = ListField(EmbeddedDocumentField(Comment))
    
    # 实例化嵌入式文档字段
    comment1 = Comment(content='Good work!')
    comment2 = Comment(content='Nice article!')
    page = Page(comments=[comment1, comment2])
    
  • EmbeddedDocumentListField

  • EnumField

  • FileField

  • FloatField

  • GenericEmbeddedDocumentField

  • GenericReferenceField

    # 另一种引用字段类型,类似(ReferenceField),允许引用任何类型的文档
    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()
    
  • GenericLazyReferenceField

  • GeoPointField

  • ImageField

  • IntField

  • ListField

    # 列表字段的第一个参数定义为该列表元素的字段类型
    class Page(Document):
        tags = ListField(StringField(max_length=50))
    
  • LongField

  • MapField

  • ObjectIdField

  • ReferenceField

    class User(Document):
        name = StringField()
    # 字段值引用存储到数据库中的其他文档
    class Page(Document):
        content = StringField()
        author = ReferenceField(User)
    
    john = User(name="John Smith")
    john.save()
    
    post = Page(content="Test Page")
    post.author = john
    post.save()
    
    # 使用引用字段实现多对多关系(many to many)
    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()
    
    # Find all pages Bob authored
    Page.objects(authors__in=[bob])
    
    # Find all pages that both Bob and John have authored
    Page.objects(authors__all=[bob, john])
    
    # Remove Bob from the authors for a page.
    Page.objects(id='...').update_one(pull__authors=bob)
    
    # Add John to the authors for a page.
    Page.objects(id='...').update_one(push__authors=john)
    
    # MongoDB默认不会检查数据完整性,所以删除引用数据后可能带来完整性问题。
    # Mongoengine 的 ReferenceField 添加了一些功能来防止这些类型的数据库完整性问题,为每个引用提供删除规则规范
    # 此示例中的声明意味着当删除 Employee 对象时,引用该员工的 ProfilePage 也将被删除。
    class ProfilePage(Document):
        employee = ReferenceField('Employee', reverse_delete_rule=mongoengine.CASCADE)
    
    # 如下几个选项:
    # - mongoengine.DO_NOTHING : 默认设置,不会做任何事情。可能会导致数据引用悬空。
    # - mongoengine.DENY : 如果该文档存在引用,拒绝删除。
    # - mongoengine.NULLIFY : 引用该字段的字段将会被置为None(use mongoDB uset)
    # - mongoengine.CASCADE : 同时阐述包含该引用字段的文档
    # - mongoengine.PULL : 从其他文档字段中删除该对象的引用(use mongoDB pull)
    
  • LazyReferenceField

  • SequenceField

  • SortedListField

  • StringField

  • URLField

  • UUIDField

  • PointField

  • LineStringField

  • PolygonField

  • MultiPointField

  • MultiLineStringField

  • MultiPolygonField

字段可以设置设置的选项有:

  • db_field (Default: None)

    # 可以定义字段在MongoDB中的属性标签
    class Page(Document):
        page_number = IntField(db_field="pageNumber")
    
  • required (Default: False)

    定义字段是否必填

  • default (Default: None)

    定义字段默认值

  • unique (Default: False)

    定义该字段值是否需要保持唯一,否则会引发 NotUniqueError

  • unique_with (Default: None)

    # 定义该字段在多个字段中保持唯一,可以是单个字段名称,也可以是字段名称的列表或元组
    class User(Document):
        username = StringField(unique=True)
        first_name = StringField()
        last_name = StringField(unique_with='first_name')
    
  • primary_key (Default: False)

    定义是否此字段用作集合的主键

  • choices (Default: None)

    SIZE = ('S', 'M', 'L', 'XL', 'XXL')
    # 可迭代选项限定字段值范围
    class Shirt(Document):
        size = StringField(max_length=3, choices=SIZE)
    
  • validation (Optional)

    def _not_empty(val):
        if not val:
            raise ValidationError('value can not be empty')
    # 可以调用一个对象验证字段的值
    class Person(Document):
        name = StringField(validation=_not_empty)
    
  • **kwargs (Optional)

索引

指定索引可以提高查询速度。可以在文档模式的meta属性中定义索引字段。也可以通过嵌入式字段定义复合索引。

class Page(Document):
    category = IntField()
    title = StringField()
    rating = StringField()
    created = DateTimeField()
    meta = {
        'indexes': [
          	# may either be a single field name, a tuple containing multiple field names, or a dictionary containing a full index definition.
            'title',   # single-field index
            '$title',  # text index
            '#title',  # hashed index
            ('title', '-rating'),  # compound index
            ('category', '_cls'),  # compound index
            {
                'fields': ['created'],
                'expireAfterSeconds': 3600  # ttl index
            }
        ]
    }

2dsphere索引是mongoDB推荐的索引,可以提供更好的查询性能。如下是

TTL索引(Time To Live)是一种特殊的索引,可以决定文档失效的时间。

class Session(Document):
    created = DateTimeField(default=datetime.utcnow)
    meta = {
        'indexes': [
            {'fields': ['created'], 'expireAfterSeconds': 3600}
        ]
    }

排序

可以在文档模式的meta字段中定义文档的排序方式,预定义的排序可以通过调用order_by() 来覆盖。

class BlogPost(Document):
    title = StringField()
    published_date = DateTimeField()

    meta = {
        'ordering': ['-published_date']
    }

文档继承

可以在文档模式的meta字段中设置allow_inheritance为True来允许类继承。

# Stored in a collection named 'page'
class Page(Document):
    title = StringField(max_length=200, required=True)

    meta = {'allow_inheritance': True}

# Also stored in the collection named 'page'
class DatedPage(Page):
    date = DateTimeField()
    
Page(title='a funky title').save()
DatedPage(title='another title', date=datetime.utcnow()).save()

print(Page.objects().count())         # 2
print(DatedPage.objects().count())    # 1

文档抽象类

如果给文档模式类添加一些额外功能,不想子类继承这些功能,可以在meta字段中指定抽象类。

class BaseDocument(Document):
    meta = {
        'abstract': True,
    }
    def check_permissions(self):
        ...

class User(BaseDocument):
   ...