数据库,一个数据仓库能够包含多个集合html
集合,相似于关系数据库中的表。一个集合能够包含多个文档。python
capped collection(限制集合):设定空间上线,循环写入,新数据覆盖旧数据git
文档,一个文档保存着一份数据记录。github
一个集合下的文档,不会默认要求它们具备相同的数据模式。也就是说,同一个集合下的多个文档:1.字段能够不一样;2.同名字段的类型能够不一样;相反,若是对文档数据类型有要求,能够在建立集合时设置validator(例如使用JSON schema)来限制集合下文档的数据类别。正则表达式
文档使用BSON来存储,BSON是JSON的二进制表现形式,所以能够适应更多的数据类型。
存储在文档的数据格式,与JSON相似,以键值对的形式存储。
默认主键_id,为ObjectId类型。redis
使用insert_one()方法sql
book = { 'name': 'computer_science', 'page': 238, } result = db.books.insert_one(book) _id = result.inserted_id # 返回插入项的id,类型为ObjectId
使用insert_many()方法mongodb
results = db.collection_name.insert_many([document1, document2, ...]) id_list = results.inserted_ids # 返回插入项的id列表,列表元素为ObjectId
从pymongo3.0版本开始,已经不推荐使用insert()方法插入数据,虽然它能同时知足单条或多条数据的处理需求。
官方建议使用insert_one()和insert_many()来执行替代操做shell
假设预先执行了数据插入:数据库
db.inventory.insert_many([ {"item": "journal", "qty": 25, "size": {"h": 14, "w": 21, "uom": "cm"}, "status": "A"}, {"item": "notebook", "qty": 50, "size": {"h": 8.5, "w": 11, "uom": "in"}, "status": "A"}, {"item": "paper", "qty": 100, "size": {"h": 8.5, "w": 11, "uom": "in"}, "status": "D"}, {"item": "planner", "qty": 75, "size": {"h": 22.85, "w": 30, "uom": "cm"}, "status": "D"}, {"item": "postcard", "qty": 45, "size": {"h": 10, "w": 15.25, "uom": "cm"}, "status": "A"}])
使用find()方法执行查询,返回游标cursor
查询全部记录时,find()内的filter参数为空
cursor = db.inventory.find({})
上述查询,相似于关系数据库SQL语句:
SELECT * FROM inventory
cursor = db.inventory.find({"status": "D"})
上述查询语句,相似于关系数据库SQL语句:
SELECT * FROM inventory WHERE status = "D"
cursor = db.inventory.find({"status": {"$in": ["A", "D"]}})
上述查询语句,相似于关系数据库SQL语句:
SELECT * FROM inventory WHERE status in ("A", "D")
cursor = db.inventory.find({"status": "A", "qty": {"$lt": 30}})
上述查询语句,相似于关系数据库SQL语句:
SELECT * FROM inventory WHERE status = "A" AND qty < 30
cursor = db.inventory.find({"$or": [{"status": "A"}, {"qty": {"$lt": 30}}]})
上述查询语句,相似于关系数据库SQL语句:
SELECT * FROM inventory WHERE status = "A" OR qty < 30
cursor = db.inventory.find({ "status": "A", "$or": [{"qty": {"$lt": 30}}, {"item": {"$regex": "^p"}}]})
上述查询语句,相似于关系数据库SQL语句:
SELECT * FROM inventory WHERE status = "A" AND ( qty < 30 OR item LIKE "p%")
查询操做符定义了查询条件,如:大于、等于、小于等,如下是整理的查询操做符及说明:
比较操做符 | 说明 |
---|---|
$eq | 等于 |
$gt | 大于 |
$gte | 大于等于 |
$in | 包含 |
$lt | 小于 |
$lte | 小于等于 |
$ne | 不等于 |
$nin | 不包含于 |
逻辑操做符 | 说明 |
---|---|
$and | 与 |
$not | 非 |
$nor | 或非 |
$or | 或 |
元素操做符 | 说明 |
---|---|
$exists | 指定field存在 |
$type | 指定field的type |
查看field的type类型说明
其余操做符说明请见:Query and Projection Operators
对于文档中存在的嵌套结构的查询,能够对文档中的嵌套结构进行 匹配查询 ,也能够对嵌套内容中的某个字段进行 嵌套字段查询
假设文档数据以下:
from bson.son import SON db.inventory.insert_many([ {"item": "journal", # 物品名称 "qty": 25, # 数量 "size": SON([("h", 14), ("w", 21), ("uom", "cm")]), # 嵌套结构(高度,宽度,度量单位) "status": "A"}, # 状态 {"item": "notebook", "qty": 50, "size": SON([("h", 8.5), ("w", 11), ("uom", "in")]), "status": "A"}, {"item": "paper", "qty": 100, "size": SON([("h", 8.5), ("w", 11), ("uom", "in")]), "status": "D"}, {"item": "planner", "qty": 75, "size": SON([("h", 22.85), ("w", 30), ("uom", "cm")]), "status": "D"}, {"item": "postcard", "qty": 45, "size": SON([("h", 10), ("w", 15.25), ("uom", "cm")]), "status": "A"}])
from bson.son import SON cursor = db.inventory.find( {"size": SON([("h", 14), ("w", 21), ("uom", "cm")])}) # 上述查询语句中的filter条件,须要彻底匹配嵌套文档中的内容,不然没法查询到相关记录。
mongo使用点表示法指定文档中的嵌套字段:"field.nested_field"
# 查询嵌套字段uom的值为cm的记录 cursor = db.inventory.find({"size.uom": "cm"}) # 使用操做符查询高度大于10的记录 cursor = db.inventory.find({"size.h": {"$gt": 10}}) # 多个字段的复合查询 cursor = db.inventory.find( {"size.h": {"$lt": 15}, "size.uom": "in", "status": "D"})
假设向collection中插入以下数据
db.inventory.insert_many([ {"item": "journal", "qty": 25, "tags": ["blank", "red"], "dim_cm": [14, 21]}, {"item": "notebook", "qty": 50, "tags": ["red", "blank"], "dim_cm": [14, 21]}, {"item": "paper", "qty": 100, "tags": ["red", "blank", "plain"], "dim_cm": [14, 21]}, {"item": "planner", "qty": 75, "tags": ["blank", "red"], "dim_cm": [22.85, 30]}, {"item": "postcard", "qty": 45, "tags": ["blue"], "dim_cm": [10, 15.25]}])
与2.2.3中嵌套文档查询相似,能够对整个数组进行 匹配查询 , 也能够对数组中的某个元素进行查询
cursor = db.inventory.find({"tags": ["red", "blank"]})
# 全部tags中包含red元素的数组都会被查询到 cursor = db.inventory.find({"tags": "red"}) # 对数组中某个元素进行条件查询 # dim_cm中任意一个元素大于25的记录查询 cursor = db.inventory.find({"dim_cm": {"$gt": 25}})
查询dim_cm中的某个元素能同时知足大于15小于20的查询条件的记录
或者 dim_cm中的一个元素大于15,而且存在另外一个元素小于20的记录
# 这种查询是不限制单个数组元素的,多个数组元素分别知足查询条件亦可 cursor = db.inventory.find({"dim_cm": {"$gt": 15, "$lt": 20}})
若要指定数组中某一个元素知足多个查询条件,须要使用 __$elemMatch__操做符来进行查询
# 查询数组中存在某一个元素同时知足大于22而且小于30 cursor = db.inventory.find( {"dim_cm": {"$elemMatch": {"$gt": 22, "$lt": 30}}})
查询dim_cm的第一个元素大于25的记录
cursor = db.inventory.find({"dim_cm.1": {"$gt": 25}})
查询数组长度是否符合查询条件,须要使用 $size 操做符
查询全部tags长度等于3的的记录
cursor = db.inventory.find({"tags": {"$size": 3}})
假设向collection中插入以下数据:
from bson.son import SON db.inventory.insert_many([ {"item": "journal", "instock": [ SON([("warehouse", "A"), ("qty", 5)]), SON([("warehouse", "C"), ("qty", 15)])]}, {"item": "notebook", "instock": [ SON([("warehouse", "C"), ("qty", 5)])]}, {"item": "paper", "instock": [ SON([("warehouse", "A"), ("qty", 60)]), SON([("warehouse", "B"), ("qty", 15)])]}, {"item": "planner", "instock": [ SON([("warehouse", "A"), ("qty", 40)]), SON([("warehouse", "B"), ("qty", 5)])]}, {"item": "postcard", "instock": [ SON([("warehouse", "B"), ("qty", 15)]), SON([("warehouse", "C"), ("qty", 35)])]}])
能够看到instock数组内部每个元素都是一个嵌套文档。对这类数据的查询方法,是2.2.3嵌套文档查询和2.2.4数组类型查询的结合。
# 匹配查询对于嵌套文档内的field顺序有要求, # 查询结果只展现与查询条件中field排列顺序相同的记录。 cursor = db.inventory.find( {"instock": SON([("warehouse", "A"), ("qty", 5)])})
# 查询全部文档中,instock数组中至少有一个元素的qty值大于20的记录 cursor = db.inventory.find({'instock.qty': {"$lte": 20}})
# 查询全部文档中,instock数组的第0个嵌套文档元素中,qty的值小于等于20的全部记录 cursor = db.inventory.find({'instock.0.qty': {"$lte": 20}})
# 数组内的某个文档同时知足qty=5而且warehouse值为A的查询条件 cursor = db.inventory.find( {"instock": {"$elemMatch": {"qty": 5, "warehouse": "A"}}}) # 数组内的某个文档的qty值大于10而且小于等于20 cursor = db.inventory.find( {"instock": {"$elemMatch": {"qty": {"$gt": 10, "$lte": 20}}}})
MonogDB返回的查询结果,默认包含文档中全部的field,使用者能够经过让mongo返回指定的field,来限制返回内容的数量。
查询表达式以下:
# 返回指定的field cursor = db.inventory.find( {"status": "A"}, {"item": 1, "status": 1, "size.uom": 1}) # 不返回指定的field cursor = db.inventory.find({"status": "A"}, {"size.uom": 0, "status": 0}) # 对于数组形式的field,指定只返回最后一个元素(使用$slice操做符) cursor = db.inventory.find( {"status": "A"}, {"instock": {"$slice": -1}})
cursor = db.inventory.find({"item": None})
# type值为10时表示的是null类型 cursor = db.inventory.find({"item": {"$type": 10}})
# 查询全部文档中,没有item字段的记录 cursor = db.inventory.find({"item": {"$exists": False}})
MongoDB提供了一系列的操做符来帮助完成文档数据更新,具体说明可查看连接:https://docs.mongodb.com/manu...
使用pymongo的update_one方法
db.inventory.update_one( {"item": "paper"}, # filter筛选条件, 只更新符合该条件的第一条数据 {"$set": {"size.uom": "cm", "status": "P"}, "$currentDate": {"lastModified": True}}) # 数据更新表达式,使用$set操做符来更新数据
使用pymongo的update_many()方法
db.inventory.update_many( {"qty": {"$lt": 50}}, # filter筛选条件,更新符合该条件的全部数据 {"$set": {"size.uom": "in", "status": "P"}, "$currentDate": {"lastModified": True}})# 数据更新表达式,一样使用$set操做符来更新数据
使用pymongo的replace()方法
注:替换方法只替换除_id之外的其余字段
db.inventory.replace_one( {"item": "paper"}, # filter筛选条件,替换符合该条件的第一条数据 {"item": "paper", # 替换后的文档数据 "instock": [ {"warehouse": "A", "qty": 60}, {"warehouse": "B", "qty": 40}]})
不管是update_one()方法仍是update_many()方法,亦或是replace_one()方法,都包含upsert:bool 选项,当upsert为True时,这些方法将具有在filter未筛选到文档时,执行文档插入的能力。
pymongo提供了delete_one()和delete_many()两种方法执行删除操做。其中,delete_one()方法一次执行一条文档的删除任务,delete_manyI()可执行多条文档删除任务。
db.inventory.delete_one({"status": "D"}) # 删除符合status值为D的第一条数据 db.inventory.delete_many({"status": "A"}) # 删除符合status值为A的全部数据
值得一提的是,删除操做并不会改变collection的索引设置,即使删除了这个collection下的全部文档。
pymongo提供了批量写入方法:bulk_write(),相似于redis中的pipe_line,它能够将多个写入操做做为一个list参数传入,而后一块儿执行。它支持insert、update、replace、delete的多种方法,如下是官方文档提供的示例:
try { db.characters.bulkWrite( [ { insertOne : { "document" : { "_id" : 4, "char" : "Dithras", "class" : "barbarian", "lvl" : 4 } } }, { insertOne : { "document" : { "_id" : 5, "char" : "Taeln", "class" : "fighter", "lvl" : 3 } } }, { updateOne : { "filter" : { "char" : "Eldon" }, "update" : { $set : { "status" : "Critical Injury" } } } }, { deleteOne : { "filter" : { "char" : "Brisbane"} } }, { replaceOne : { "filter" : { "char" : "Meldane" }, "replacement" : { "char" : "Tanys", "class" : "oracle", "lvl" : 4 } } } ] ); } catch (e) { print(e); }
1)同一个集合下,不一样文档的字段能够不一致;同一个集合下,不一样文档的相同字段,类型能够不一致;
2)能够经过对一个文档的字段进行增删改操做,或是变动字段类型,来改变文档的结构。
1) __嵌入式文档结构__,即在一个文档内,能够嵌套子文档内容,实现逻辑相关的数据结构嵌套组合。嵌套式文档结构以下:
# 用户信息表 { _id: ObjectId_1, username: "youjia", sex: "man", age: 29, contact: { # 嵌入式文档 phone: 18195181469, email: "jia.you@shunwang.com", } }
2) __引用式__,不一样类型数据使用id引用进行关联,上例可变为:
# 用户表 { _id: ObjectId_1, username: "youjia", sex: "man", age: 29, } # 联系信息表(原嵌入式文档) { _id: ObjectId_2, user_id: ObjectId_1, # 对应用户表_id phone: 18195181469, email: "jia.you@shunwang.com", }
1) 对一个文档的写入操做是原子性的,即便这个写入操做包含了对嵌套文档的数据写入。
2) 因为对嵌套文档的写入动做是原子性的,所以嵌套式的文档结构设计,更加促进了写入操做原子化,提升了写入效率和数据一致性。
3) 当执行相似updateMany等操做时,虽然只执行了一条指令,但其内部执行过程实际上包含了对多个文档的原子操做。所以这类批量执行指令是非原子性的。
4) 因为对多个文档的批量指令执行是非原子性的,所以在对多个文档进行写入操做时,写入任务可能与其余批量写入任务交叉。
5) 从MongoDB4.0开始,为了保证多文档写入/读取数据的一致性,加入了多表操做事务
6) 多表操做事务相比单表操做,会形成大的多的性能消耗,所以官方仍然认为,在多数状况下 __嵌入式文档结构是更好的选择__。
官方提供了多种数据模型校验的方法,包括:1. JSON Schema校验,2. 查询表达式校验。官方推荐使用前者。
一个典型的JSON Schema语法示例:
db.createCollection("students", { validator: { $jsonSchema: { bsonType: "object", required: [ "name", "year", "major", "gpa" ], properties: { name: { bsonType: "string", description: "must be a string and is required" }, gender: { bsonType: "string", description: "must be a string and is not required" }, year: { bsonType: "int", minimum: 2017, maximum: 3017, exclusiveMaximum: false, description: "must be an integer in [ 2017, 3017 ] and is required" }, major: { enum: [ "Math", "English", "Computer Science", "History", null ], description: "can only be one of the enum values and is required" }, gpa: { bsonType: [ "double" ], minimum: 0, description: "must be a double and is required" } } } } })
关于JSON Schema的更详细信息,可参照网上教程:https://spacetelescope.github...
3.1.1中介绍了模型的两种设计结构:__嵌入式文档结构__,和 引用式 文档结构。在设计数据模型时,要考虑根据不一样状况选择适合的文档结构进行设计。
如下状况下适合使用嵌入式文档结构:
1) 两类数据是一对一而且具备包含关系。例如:用户我的信息-用户联系信息
2) 两类数据时一对多关系,可是在应用过程当中一般两类数据须要联合查询使用,使用“一”时一般会查询“多”。
嵌套式文档结构的优势:
注意:MongoDB默认限制单个文档大小最大为16MB,所以单个文档大小不能无限扩大。Mongo提供了其余大致量数据的存储方式:GridFS
如下状况适合使用引用式文档结构:
1) 当采用嵌套式文档结构时,被嵌套的数据会有大量重复,而且大量重复数据形成影响大于嵌套文档的优点时,选择引用式文档结构时更好的选择。
2) 要设计“多对多”关系时。
3) 为大型分层数据集建模时。
引用式文档的优势:
对于查询需求较多的文档,能够经过在适合的字段创建索引来提升查询效率。可是对文档创建过多的索引,会影响写入效率,增长磁盘和内存的空间使用率。
对文档内单个字段创建索引,称为单字段索引。适合对文档内单个字段有频繁查询请求的场景。
db.person.createIndex( {id_no: 1} )
{id_no: 1}表明升序索引,{id_no: -1}表明降序索引,在单字段索引类型下,升序与降序没有区别。
复合索引是对多个字段联合建立一个索引。适合对文档内某些字段有频繁查询请求,以及查询与排序请求并存的业务场景。
db.person.createIndex( {age: 1, name: 1} )
建立符合索引时,field的顺序是有关系的。索引将按照第一个field进行升序/降序排列,在此基础上,再对第二个field进行升序/降序排列,以此类推。
当索引的字段为数组时,建立出的索引称为多key索引,多key索引会为数组的每一个元素创建一条索引,好比person表加入一个habbit字段(数组)用于描述兴趣爱好,须要查询有相同兴趣爱好的人就能够利用habbit字段的多key索引。
//文档格式 {"name" : "jack", "age" : 19, habbit: ["football, runnning"]} // 自动建立多key索引 db.person.createIndex( {habbit: 1} ) db.person.find( {habbit: "football"} )
保证索引对应的字段不会出现相同的值,文档主键_id的索引,就是惟一索引。
能够针对某个时间字段,指定文档的过时时间(通过指定时间后过时 或 在某个时间点过时)
只针对符合某个特定条件的文档创建索引,好比某字段值大于5,或者某字段值符合某正则表达式,才创建索引,注意:3.2版本才支持该特性.
只针对存在索引字段的文档创建索引,可看作是部分索引的一种特殊状况
单字段索引
复合索引
# 建立索引 db.collection.create_index( { "x" : 1, "y" : -1 } ) # 支持下列查询/排序 db.collection.find().sort( { "x": 1, "y": -1 } ) db.collection.find().sort( { "x": -1, "y": 1 } ) # 对下列查询/排序,索引不生效 db.collection.find().sort( { "x": 1, "y": 1 } )
# 建立索引 db.collection.create_index( { "x" : 1, "y" : 1 , "z": 1} ) # 支持对下列查询/排序场景 db.collection.find().sort({"x": 1}) db.collection.find().sort({"x": 1, "y": 1}) db.collection.find().sort({"x": 1, "z": 1}) # 效率较低 db.collection.find().sort({"x": 1, "y": 1, "z": 1}) # 不支持下列查询/排序场景 db.collection.find().sort({"y": 1}) db.collection.find().sort({"z": 1}) db.collection.find().sort({"y": 1, "z": 1})
多key索引
# 建立复合索引 db.collection.create_index( { "x" : 1, "y" : 1 } ) # 索引支持的文档 { "x" : 1, "y" : [1,2,3] } {"x": [1,2,3], "y": 1} # 不支持的状况 {"x": [1,2,3], "y": [1,2,3]}
# 文档结构 { "x": "test", "y": 1, "z": [ {"a": 1, "b": "test"}, {"a": 2, "b": "some"}, ... ], } # 创建嵌入式文档的多key索引 db.collection_name.create_index({"z.a": 1, "z.b": -1})