用Docker启动Mango

yaml 文件

# Use root/example as user/password credentials
version: '3.1'

services:

  mongo:
    image: mongo
    restart: always
    ports:
      - 27017:27017
    volumes:
      - ./data:/data/db
      - ./conf:/etc/mongo
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: passwd

  mongo-express:
    image: mongo-express
    restart: always
    ports:
      - 8081:8081
    environment:
      ME_CONFIG_MONGODB_ADMINUSERNAME: root
      ME_CONFIG_MONGODB_ADMINPASSWORD: passwd
      ME_CONFIG_MONGODB_URL: mongodb://root:passwd@mongo:27017/

安装 MongoEngine

pip install mongoengine
数据类型

在MongoEngine中,你可以使用多种不同的字段类型来定义模型的属性,以适应不同的数据类型和需求。以下是一些常用的MongoEngine字段类型:

  1. StringField: 字符串类型,用于存储文本数据。
  2. IntField: 整数类型,用于存储整数数据。
  3. FloatField: 浮点数类型,用于存储浮点数数据。
  4. BooleanField: 布尔类型,用于存储True或False值。
  5. DateTimeField: 日期时间类型,用于存储日期和时间数据。
  6. ListField: 列表类型,用于存储多个值的列表。
  7. DictField: 字典类型,用于存储键值对的字典。
  8. EmbeddedDocumentField: 嵌套文档类型,用于嵌套存储其他文档。
  9. ReferenceField: 引用类型,用于存储对其他文档的引用。
  10. BinaryField: 二进制数据类型,用于存储二进制数据。
  11. DecimalField: 十进制数类型,用于存储精确的十进制数。
  12. URLField: URL类型,用于存储URL链接。
  13. EmailField: 邮件地址类型,用于存储电子邮件地址。
  14. GeoPointField: 地理坐标类型,用于存储地理位置坐标。
  15. UUIDField: UUID类型,用于存储通用唯一标识符。
  16. ComplexDateTimeField: 复杂日期时间类型,支持更复杂的日期时间表示。

这只是一些常见的MongoEngine字段类型,实际上还有更多类型和选项可供选择。根据你的数据需求,你可以选择适当的字段类型来定义模型的属性。详细的字段类型和选项可以在MongoEngine的官方文档中找到。

字段声明

在MongoEngine中,你可以使用各种字段参数来自定义字段的行为和特性。这些参数可以用于字段的定义,以调整字段在模型中的行为。以下是一些常用的字段参数:

  1. required: 指定字段是否是必需的,如果设置为True,插入文档时会检查该字段是否存在。
  2. default: 设置字段的默认值,在插入文档时如果未提供该字段的值,则会使用默认值。
  3. choices: 提供一个列表,限制字段的值只能从指定的选项中选择。
  4. unique: 指定字段的值是否必须在集合中是唯一的。
  5. min_valuemax_value: 用于限制数值字段的最小值和最大值。
  6. min_lengthmax_length: 用于限制字符串字段的最小长度和最大长度。
  7. regex: 使用正则表达式来验证字段的值。
  8. sparse: 对于索引字段,设置为True表示在集合中仅包含具有该字段的文档。
  9. choices: 限制字段的值只能从指定的选项列表中选择。
  10. primary_key: 设置字段作为模型的主键,仅用于某些字段类型,如ObjectIdField。
  11. db_field: 自定义数据库中存储字段的名称。
  12. validation: 用于自定义验证逻辑的函数。
  13. verbose_name: 用于设置字段的可读性更好的名称。
  14. unique_with: 与unique一起使用,指定另一个字段来共同创建唯一约束。
  15. auto_created: 用于与信号(signals)一起工作,指定是否自动创建字段。

这只是一些常用的字段参数,实际上还有更多参数可以用来控制字段的行为。具体的参数取决于字段类型,你可以在MongoEngine的官方文档中找到关于每种字段类型及其参数的详细信息。

连接到数据库
# 连接到MongoDB数据库
connect(
    db='App',  # 数据库名称
    host='10.1.1.25',  # 数据库主机地址
    port=27017,  # 数据库端口号
    username='root',  # 可选:数据库用户名
    password='passwd',  # 可选:数据库密码
)
模型声明
from mongoengine import Document, StringField, IntField, connect, SequenceField

class User(Document):
    """
    user_id         # 自增
    name            # 用户名
    passwd          # 密码
    address         # 地址
    phone           # 电话号码
    balance         # 积分,默认为0
    """
    user_id = SequenceField(primary_key=True)       # 主键自增
    name = StringField(required=True)               # 不能为空
    passwd = StringField(required=True)     
    address = StringField()
    phone = StringField()
    user_token = StringField()
    balance = IntField(default=0)                   # 默认值0
引入字段(外键)
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()
通用引用字段

还存在第二种参考字段。 ](https://docs.mongoengine.org/apireference.html#mongoengine.fields.GenericReferenceField)这允许您引用任何 kind of [,因此不将``子类作为构造函数参数:

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()
创建数据和修改数据
# 创建对象并保存
user = User(name='Mek', passwd='123456', phone='10086')
user.save()

# 修改对象并保存
user.address = 'YiBin'
user.balance = 100
user.save()

# 删除单条数据
user.delete()
批量插入
class User(Document):
    """
    user_id         # 自增
    name            # 用户名
    passwd          # 密码
    address         # 地址
    phone           # 电话号码
    balance         # 积分,默认为0
    """
    user_id = SequenceField(primary_key=True)  # 主键自增
    name = StringField(required=True)  # 不能为空
    passwd = StringField(required=True)
    address = StringField()
    phone = StringField()
    user_token = StringField()
    balance = IntField(default=0)  # 默认值0

    meta = {
        'indexes': [
            {'fields': ['name']}
        ]
    }
def batch_(N: int = 10000):
    # 创建一个空列表
    data_list = []
    for _ in range(N):
        # 将对象声明为字典加入列表
        data_list.append(
            User(
                name=Mm.get_random_letters(8),
                passwd=Mm.get_random_letters(12),
                phone=str(Mm.get_random_numbe(12)),
                address=str(Mm.get_uuid4()),
                balance=int(Mm.get_random_numbe(4))
            )
        )
    #  使用insert() 批量插入
    User.objects.insert(data_list)
删除数据
# 删除所有数据
all_user = User.objects()
all_user.delete()

查询数据

MongoEngine支持许多查询操作符,用于在查询中构建各种条件,以下是一些常见的查询操作符示例:

  1. 相等操作符
    • __exact: 精确匹配。
    • =: 等于,与__exact等效。
  2. 模糊匹配操作符
    • __iexact: 不区分大小写的精确匹配。
    • __contains: 包含指定值。
    • __icontains: 包含指定值,不区分大小写。
    • __startswith: 以指定值开头。
    • __istartswith: 以指定值开头,不区分大小写。
    • __endswith: 以指定值结尾。
    • __iendswith: 以指定值结尾,不区分大小写。
  3. 比较操作符
    • __gt: 大于。
    • __gte: 大于等于。
    • __lt: 小于。
    • __lte: 小于等于。
  4. 数组操作符
    • __contains: 数组字段包含指定元素。
    • __all: 数组字段包含所有指定元素。
    • __size: 数组字段的大小等于指定值。
  5. 范围操作符
    • __range: 字段在指定范围内。
    • __in: 字段的值在指定值列表中。
    • __nin: 字段的值不在指定值列表中。
  6. 正则表达式操作符
    • __regex: 使用正则表达式进行匹配。
    • __iregex: 使用正则表达式进行匹配,不区分大小写。

这些只是一些常见的查询操作符示例,MongoEngine还支持更多操作符,可以根据实际需求在查询中使用。查询操作符的详细列表和用法可以在MongoEngine的官方文档中找到。

查询单条数据
# get() 函数
user = User.objects.get(name='VBZCFNOF')
print(user.name, user.phone, user.address)

# first() 函数
user = User.objects(name='VBZ2CFNOF').first()
# 区别是first()函数没有获取到数据时会返回None
比较查询
# __gt		# 大于
# __gte  	# 大于等于
# __lt		# 小于
# __lte		# 小于等于
# __exact 或者 = 		# 等于

user_list = User.objects(balance__lte=1048)
    for user in user_list:
        print(user.name, user.balance)
文本匹配查询
# __icontains 包含且不区分大小写
user_list = User.objects(address__icontains='98')
for user in user_list:
    print(user.name, user.balance, user.address)

# 按条件删除    
user_list.delete()
多条件查询
# AND 查询
users = list(User.objects(name__contains="MEK", balance__gt=8000))
len(users)
for user in users:
	print(user.name, user.balance)
    
# 更复杂的查询 与 或
# | 或表达
user_list = list(User.objects(Q(name__contains='MEB') | Q(name__contains='MED')))
for user in user_list:
	print(user.name)
# & 与表达    
user_list = list(User.objects(Q(name__contains='MEB') & Q(name__contains='A')))
for user in user_list:
	print(user.name)

进阶操作

批量插入
# 创建一个空列表
data_list = []
# 将对象声明为字典加入列表
data_list.append(
    User(
        name=Mm.get_random_letters(8),
        passwd=Mm.get_random_letters(12),
        phone=str(Mm.get_random_numbe(12)),
        address=str(Mm.get_uuid4()),
        balance=int(Mm.get_random_numbe(4))
    )
)
#  使用insert() 批量插入
User.objects.insert(data_list)
获取数据总条数
total_documents = User.objects.count()
print(total_documents)
查询时按字段排序
# 按balance 降序
user_list = User.objects(address__icontains='981a').order_by('-balance')
for user in user_list:
    print(user.name, user.balance, user.address)

# 按balance 升序
user_list = User.objects(address__icontains='981a').order_by('balance')
for user in user_list:
        print(user.name, user.balance, user.address)

获取指定行数
# 按balance 降序 获取前100条记录
first_100_users = User.objects[:100].order_by('-balance')
for user in first_100_users:
	print(user.name, user.passwd, user.phone, user.balance)
分页

实现分页可以通过结合切片操作和计算来实现。你可以指定每页的记录数和当前页码,然后根据这些信息计算出起始位置和结束位置,再使用切片操作获取对应的记录。以下是一个简单的分页示例:

# 定义模型
class Product(Document):
    name = StringField(required=True)
    price = FloatField()

# 分页查询函数
def get_paginated_products(page_number, page_size):
    start_index = (page_number - 1) * page_size
    end_index = start_index + page_size

    products = Product.objects.order_by('price')[start_index:end_index]
    return products

# 分页参数
page_number = 2  # 第2页
page_size = 10   # 每页10条记录

# 获取分页结果
paginated_products = get_paginated_products(page_number, page_size)

# 打印分页结果
for product in paginated_products:
    print(product.name, product.price)

索引

索引是数据库中用于加快数据检索速度的数据结构。在MongoDB中,索引是用于提高查询性能的重要机制。通过创建索引,可以让数据库更有效地定位和检索数据,从而减少查询操作的时间复杂度。

以下是一些关于MongoDB索引的详细信息:

  1. 索引类型:MongoDB支持多种类型的索引,包括单字段索引、复合索引、文本索引、地理空间索引等。不同类型的索引适用于不同的查询需求。
  2. 索引数据结构:MongoDB使用B树(B-tree)数据结构来实现索引。B树是一种自平衡的二叉树,它可以快速地进行插入、删除和查找操作。
  3. 单字段索引:最简单的索引类型,用于一个字段上的查询。你可以使用create_index()方法来在集合上创建单字段索引。
  4. 复合索引:索引多个字段的组合,可以优化涉及多个字段的查询。复合索引的字段顺序很重要,因为查询字段的顺序应该与索引的字段顺序一致。
  5. 唯一索引:保证索引字段的值在整个集合中是唯一的。可以通过设置unique=True来创建唯一索引。
  6. 文本索引:用于全文搜索,可以在文本字段上创建,支持文本搜索相关的查询操作。
  7. 地理空间索引:用于存储地理位置数据,例如经纬度。可以用来执行地理空间查询,如查找附近的点。
  8. 稀疏索引:创建索引时,只会为非空字段创建索引,可以节省存储空间。
  9. TTL索引:用于自动删除过期数据,可以设置索引字段的过期时间。
  10. 复合分片键索引:用于MongoDB分片集群中,用于划分数据到不同分片的依据。

在使用索引时需要注意以下几点:

  • 索引会增加写入操作的开销,因为每次插入、更新或删除数据时都需要更新索引。
  • 不适当的索引使用会导致查询性能下降,因此需要根据实际查询需求来选择创建索引。
  • MongoDB中的查询优化器会根据查询计划自动选择最佳的索引,但仍然可以通过hint()方法来指定使用特定索引。
  • 需要定期检查索引性能,删除不再使用的索引,以及考虑重建或重新组织索引以优化性能。

创建索引可以使用create_index()方法,例如:

# 在"users"集合上创建"name"字段的单字段索引
db.users.create_index("name")
其他索引的创建方法
  1. 单字段索引:最简单的索引类型,用于单个字段上的查询。可以通过在字段的定义中使用Index类来创建单字段索引。

    from mongoengine import Document, StringField, Index
    
    class Person(Document):
        name = StringField(required=True)
        age = IntField()
    
        meta = {
            'indexes': [
                {'fields': ['name']}
            ]
        }
    
  2. 复合索引:索引多个字段的组合,用于优化涉及多个字段的查询。复合索引的字段顺序很重要,应该与查询中字段的顺序一致。

    class Person(Document):
        name = StringField(required=True)
        age = IntField()
    
        meta = {
            'indexes': [
                {'fields': ['name', 'price']}
            ]
        }
    
  3. 文本索引:用于全文搜索,可以在文本字段上创建,支持文本相关的查询操作。

    class Post(Document):
        title = StringField(required=True)
        content = StringField(required=True)
    
        meta = {
            'indexes': [
                {'fields': ['$title', '$content'], 'default_language': 'english'}
            ]
        }
    
  4. 地理空间索引:用于存储地理位置数据,例如经纬度。用于执行地理空间查询,如查找附近的点。

    class Location(Document):
        name = StringField(required=True)
        coordinates = GeoPointField()
    
        meta = {
            'indexes': [
                Index([('coordinates', '2dsphere')])
            ]
        }
    
  5. TTL索引:用于自动删除过期数据,可以设置索引字段的过期时间(以秒为单位)。

    class Session(Document):
        session_id = StringField(required=True)
        created_at = DateTimeField()
    
        meta = {
            'indexes': [
                Index([('created_at', 1)], expireAfterSeconds=3600)  # 1小时后过期
            ]
        }
    

    6.唯一索引:我们为Account模型的username字段创建了一个唯一索引。

from mongoengine import Document, StringField

class Account(Document):
    username = StringField(unique=True, required=True)

    meta = {
        'indexes': [
            {'fields': ['username'], 'unique': True}
        ]
    }

这些是一些常见的索引类型,可以根据实际查询需求来选择适合的索引类型。