服务器之家:专注于服务器技术及软件下载分享
分类导航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|Sqlite|DB2|mariadb|Access|数据库技术|

服务器之家 - 数据库 - MongoDB - Python 和 MongoDB 其实很配

Python 和 MongoDB 其实很配

2023-05-07 04:06未知服务器之家 MongoDB

[[420498]] MongoDB 其实就是一个大大的 JSON,在 Python 的世界里 dict 也是最吃香的类型,所以,他们天生就是一对。 MongoDB 的安装 推荐使用 Docker 来部署管理,一行命令就可以搞定,官方版本: dockerrun-d --namemongodb\ -eMONGO_INITDB_ROOT_USER

[[420498]]

 MongoDB 其实就是一个大大的 JSON,在 Python 的世界里 dict 也是最吃香的类型,所以,他们天生就是一对。

MongoDB 的安装

推荐使用 Docker 来部署管理,一行命令就可以搞定,官方版本:

  1. docker run -d --name mongodb \ 
  2.     -e MONGO_INITDB_ROOT_USERNAME=admin \ 
  3.     -e MONGO_INITDB_ROOT_PASSWORD=admin \ 
  4.     -v ~/data/mongo_dir:/data/db \ 
  5.     -p 27017:27017 \ 
  6.     mongo 

官方版本的 Docker 啥都好,就是体积有点大。还有一个小体积的 alpine 版本,开发时使用很方便,不过不能配置账户和密码。

  1. docker run -d --name mongo-lite \ 
  2.   -p 27018:27017 \ 
  3.   -v ~/data/mongo_lite:/data/db \ 
  4.   mvertes/alpine-mongo 

如果想尝试 Mongo 的命令行 (Mongo Shell),直接进到 Docker 里:

  1. $ docker exec -it mongo-lite mongo 
  2. MongoDB shell version v4.0.6 
  3. connecting to: mongodb://127.0.0.1:27017/?gssapiServiceName=mongodb 
  4. ... 
  5.  
  6. > use mydb 
  7. switched to db mydb 
  8.  
  9. > db.User.insertOne({"name":"Toby",age:18}) 
  10.  "acknowledged" : true
  11.  "insertedId" : ObjectId("612c84c5d93795436ad27ebc"
  12.  
  13. > db.User.find() 
  14. "_id" : ObjectId("612c84c5d93795436ad27ebc"), "name" : "Toby""age" : 18 } 

Mongo Shell 官方文档:https://docs.mongodb.com/manual/reference/mongo-shell/

PyMongo 五分钟上手

安装 PyMongo 可以通过 pip 搞定。

  1. pip install pymongo 

以下内容也可以参考官方文档:https://pymongo.readthedocs.io/en/stable/

连接数据库

常见方式如下:

  1. from pymongo import MongoClient 
  2.  
  3. # 连接有密码的Mongo 
  4. client = MongoClient('mongodb://admin:admin@localhost:27017/'
  5.  
  6. # 连接没密码的Mongo 
  7. client = MongoClient('mongodb://localhost:27018/'
  8.  
  9. # 列出所有已经存在的DB 
  10. for db in client.list_databases(): 
  11.     print(db) 
  12.  
  13. # 使用Mongo里的某个DB,这个DB可以不存在,后面写数据时会被创建出来 
  14. db = client.mydb 

插入数据

插入的每条数据都是一个 dict,一样的字段允许类型不一样,也允许每次插入的数据字段不一样,可以理解成动态类型数据,你想放什么都行,唯一的约束就是他们会被放在同一个 Document 里。

  1. # 插入一条数据 
  2. def add_one_user(): 
  3.     db.User.insert_one({ 
  4.         'name''Toby'
  5.         'age': 18 
  6.     }) 
  7.  
  8. # 插入多条数据 
  9. def add_many_users(): 
  10.     db.User.insert_many([{ 
  11.         'name''Tom'
  12.         'age': 10 
  13.     }, { 
  14.         'name''Toby'
  15.         'age''unknown'
  16.         'hobbies': ['write bugs''raise dogs'
  17.     }]) 

这里的 User 约等于关系型数据库的表,但它的名字叫 Document,每次数据插入完成后会返回一个_id,这是 Mongo 里最重要的东西了,它就是靠这个_id 来保证数据的一致性,后续的数据修改和删除主要就是靠这个_id 来完成,所以一般针对某条特定的数据的处理,都是需要先查询它的_id,然后再进行后面的操作。

查询数据

  1. # 查询多个数据 
  2. def show_users(): 
  3.     # 一个表里所有数据 
  4.     for e in db.User.find(): 
  5.         print(e) 
  6.  
  7.     # 匹配条件的多条数据 
  8.     for e in db.User.find({'name''Toby'}): 
  9.         print(e) 
  10.  
  11.  
  12. # 查询单个数据 
  13. def query_user(name): 
  14.     return db.User.find_one({'name'name}) 
  15.  
  16.  
  17. # 忽略大小写 
  18. def query_user_ignore_case(name): 
  19.     return db.User.find_one({'name': re.compile(name, re.IGNORECASE)}) 
  20.  
  21.  
  22. # 使用运算符 https://docs.mongodb.com/manual/reference/operator/query/ 
  23. def query_teenager(): 
  24.     return db.User.find_one({'age': {'$lt': 18}}) 

Mongo 的查询主要还是依赖 DB 自己提供的运算符,在 PyMongo 里要注意,这里不会抛出异常,如果找不到数据,默认返回 None。

通过运算符查询数据:https://docs.mongodb.com/manual/reference/operator/query/

通过聚合查询数据:https://docs.mongodb.com/manual/aggregation/

修改数据

  1. # 修改一个数据 
  2. def update_user(user, attributes: dict): 
  3.     user.update(attributes) 
  4.     result = db.User.replace_one({'_id'user['_id']}, user, upsert=True
  5.     return {'affected_count': result.modified_count} 
  6.  
  7. u = query_user_ignore_case('toby'
  8. result = update_user(u, {'code''python'}) 
  9.  
  10. # 修改多个数据,注意有坑,Replace 和 Update是不一样的 
  11. def update_many(): 
  12.     todo = [ 
  13.         UpdateOne({'age': 19}, {'$set': {'name''Toby'}}), 
  14.         ReplaceOne({'name''Tom'}, {'age': 19}),  # name 会被吃掉 
  15.     ] 
  16.     result = db.User.bulk_write(todo) 
    1. def delete_user(name): 
    2.     result = db.User.delete_one({'name'name}) 
    3.     return {'affected_count': result.deleted_count} 
  17.     print(result.matched_count) 

Replace 是替换,所以要带上原有字段,这里有点坑。Update 不接受单独的 dict,需要用 $set / $unset 来标识修改的字段的方式。

  1.    { $set: { status: "Modified", comments: [ "$misc1""$misc2" ] } }, 
  2.    { $unset: [ "misc1""misc2" ] } 

删除数据

  1. def delete_user(name): 
  2.     result = db.User.delete_one({'name'name}) 
  3.     return {'affected_count': result.deleted_count} 

删除多个数据:

  1. >>> db.test.count_documents({'x': 1}) 
  2. >>> result = db.test.delete_many({'x': 1}) 
  3. >>> result.deleted_count 
  4. >>> db.test.count_documents({'x': 1}) 

常见问题

有什么办法可以让 Mongo 不自动添加 _id 到我的数据里?

几乎没有,这是 MongoDB 的特性决定的,如果你的数据没有 ID 的话,并且进行高并发插入时,大概率会遇到 BulkWriteError 这个错误。

  1. >>> doc = {} 
  2. >>> collection.insert_many(doc for _ in range(10)) 
  3. Traceback (most recent call last): 
  4. ... 
  5. pymongo.errors.BulkWriteError: batch op errors occurred 
  6. >>> doc 
  7. {'_id': ObjectId('560f171cfba52279f0b0da0c')} 
  8.  
  9. >>> docs = [{}] 
  10. >>> collection.insert_many(docs * 10) 
  11. Traceback (most recent call last): 
  12. ... 
  13. pymongo.errors.BulkWriteError: batch op errors occurred 
  14. >>> docs 
  15. [{'_id': ObjectId('560f1933fba52279f0b0da0e')}] 

如果你不想要自动生成的 ID,可以自己在插入数据前指定这个字段。

为啥我指定了_id 还是查询不到我的数据?

比如我要查询数据库里的某个 post:

  1. >>> post_id_as_str = str(post_id) 
  2. >>> posts.find_one({"_id": post_id_as_str}) # No result 

因为 pyMongo 里的这个 ID 不是字符串类型,你需要做一下数据转换。

  1. from bson.objectid import ObjectId 
  2.  
  3. # The web framework gets post_id from the URL and passes it as a string 
  4. def get(post_id): 
  5.     # Convert from string to ObjectId: 
  6.     document = client.db.collection.find_one({'_id': ObjectId(post_id)}) 

用标准库里的 json 模块来序列化和反序列化 Mongo 的数据会有什么问题?

有一些数据类型在反序列后会得不到预期的结果,比如 ObjectId 和 DBRef,PyMongo 为了解决这个问题自己封装了一个辅助类 json_util,可以很好的解决这些问题。

  1. from bson.json_util import loads 
  2. from bson.json_util import dumps 

总结

Mongo 属于非关系型数据库,使用 Mongo 作为 DB 的思维需要做比较大的转变:

  1. 关系型数据库一般读写容易,修改难,容易理解
  2. 非关系型数据库一般是读写改容易,设计难(相对而言)

“关系型数据库支持 ACID (Atomicity, Consistency, Isolation, Duration) 即原子性,一致性,隔离性和持续性。相对而言,NoSQL 采用更宽松的模型 BASE (Basically Available, Soft state, Eventual Consistency) 即基本可用,软状态和最终一致性。

NoSQL 在精心的设计下查询性能会更高,数据结构也十分有弹性,特别适合快速发展和属性不确定的产品功能,但 Mongo 不支持事务,如何确保数据一致性是个挺大的挑战。

在选择上可以考虑从以下角度去思考:

  1. 需要 ACID 还是 BASE
  2. 需要结构化数据还是非结构化数据
  3. 需要对数据进行灵活扩展
  4. 开发人员的经验

很多情况只考虑最后一点就可以了。

 

延伸 · 阅读

精彩推荐
  • MongoDBMongoDB 内存使用情况分析

    MongoDB 内存使用情况分析

    都说 MongoDB 是个内存大户,但是怎么知道它到底用了多少内存呢...

    MongoDB教程网10002020-09-29
  • MongoDBmongodb基本命令实例小结

    mongodb基本命令实例小结

    这篇文章主要介绍了mongodb基本命令,结合实例形式总结分析了MongoDB数据库切换、查看、删除、查询等基本命令用法与操作注意事项,需要的朋友可以参考下...

    dawn-liu3652020-05-26
  • MongoDBMongoDB安装图文教程

    MongoDB安装图文教程

    这篇文章主要为大家详细介绍了MongoDB安装图文教程,分为两大部分为大家介绍下载MongoDB和安装MongoDB的方法,感兴趣的小伙伴们可以参考一下 ...

    Yangyi.He6132020-05-07
  • MongoDBMongoDB中javascript脚本编程简介和入门实例

    MongoDB中javascript脚本编程简介和入门实例

    作为一个数据库,MongoDB有一个很大的优势——它使用js管理数据库,所以也能够使用js脚本进行复杂的管理——这种方法非常灵活 ...

    MongoDB教程网6982020-04-24
  • MongoDB分布式文档存储数据库之MongoDB分片集群的问题

    分布式文档存储数据库之MongoDB分片集群的问题

    这篇文章主要介绍了分布式文档存储数据库之MongoDB分片集群的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋...

    Linux-18743072020-12-20
  • MongoDBMongodb实现定时备份与恢复的方法教程

    Mongodb实现定时备份与恢复的方法教程

    这篇文章主要给大家介绍了Mongodb实现定时备份与恢复的方法教程,文中通过示例代码介绍的非常详细,对大家具有一定的参考学习价值,需要的朋友们下面...

    chenjsh364522020-05-13
  • MongoDB迁移sqlserver数据到MongoDb的方法

    迁移sqlserver数据到MongoDb的方法

    这篇文章主要介绍了迁移sqlserver数据到MongoDb的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...

    听枫xl9682021-01-03
  • MongoDBMongoDB凭什么跻身数据库排行前五

    MongoDB凭什么跻身数据库排行前五

    MongoDB以比去年同期超出65.96分的成绩继续雄踞榜单前五,这个增幅在全榜仅次于PostgreSQL的77.99,而其相对于4月份的6.10分的增长也是仅次于微软SQL Server排名...

    孙浩峰3892020-05-22