-----------------------------------------MongoDB关系------------------------------------
MongoDB的关系表示多个文档之间在逻辑上的相互联系。
文档之间可以通过嵌入和引用来建立联系。、
MongoDB中的关系:1:1 1:N N:1 N:N
实例:1:N 一个用户可以有多个地址
user文档结构:
{
name:'hedanning',
contact:"123456789",
birthday:"1996_01-01"
}
address文档结构:
{
code:"code_1",
city:"shanxi"
}
嵌入式关系:
可以把用户地址嵌入到用户文档中:
{
name:'hedanning',
contact:"123456789",
birthday:"1996_01-01",
address:[
{
code:"code_1",
city:"shanxi"
},
{
code:"code_2",
city:"beijing"
}]
}
将user和address的信息都存放在单一的文档中,获取和维护数据比较容易:
查询:db.user.findOne({name:"hdn"},{address:1})
缺点:如果用户和用户地址不断增加,数据量不断变大,会影响读写性能。
引用式关系:
把用户数据文档和用户地址数据文档分开,通过引用文档的id字段来建立关系
{
name:'HDN',
contact:"123456789",
birthday:"1996_01-01",
address_ids:[
ObjectId("5d5b90bd03891dd27695ffe5"),
ObjectId("5d5b90cb03891dd27695ffe6")
]}
用户文档的address_ids字段包含用户地址的对象id数据。
我们可以读取这些用户地址的对象id来获取用户的详细地址信息。
查询:这种方法需要两次查询,第一次查询用户地址的对象id,第二次通过查询到的id获取用户的详细地址信息。
- 使用findOne(),查询符合条件的第一个
var result = db.user.findOne({name:"HDN"},{address_ids:1})
db.address.find({"_id":{"$in":result["address_ids"]}})
- 使用find(),返回的是一个数组
var result = db.user.find({name:"HDN"},{address_ids:1}).pretty()
db.address.find({_id:{"$in":result[0]["address_ids"]}})
---------------------------------------MongoDB 数据库引用--------------------------
MongoDB引用有两种:手动引用 和 DBRefs
手动引用:例子同MongoDB关系里面的实例
DBRefs:{$ref:value,$id:value,$db:value}
参数说明:
- $ref:集合名称
- $id:引用的id
- $db:数据库名称,可选参数
实例:
address_home集合:
{
code:"code_home_1",
city:"shanxi"
}
user集合:
{
address:{
$ref:'address_home',
$id:ObjectId('5d5ba2605c2b17268b794fc1'),
$db:'test'
},
name:'HEHE',
contact:'123456789',
birthdat:'19960101'
}
查询:通过指定$ref参数(address_home集合)来查找集合中指定id的用户地址信息:
-----------------------------------------MongoDB覆盖索引查询------------------------------------
覆盖查询是符合以下的查询:
- 所有的查询字段式索引的一部分
- 所有的查询返回字段在同一个索引中
由于所有出现在查询中的字段式索引的一部分,MongoDB无需在个数据文档中检索匹配查询条件和返回使用相同索引的查询结果。
从索引中获取数据比通过扫描文档读取数据要快得多。
- 使用如下的user集合:
{ "_id" : ObjectId("5d5ba99c5c2b17268b794fc3"), "name" : "hdn_1", "age" : 18, "likes" : 100 }
{ "_id" : ObjectId("5d5ba9d55c2b17268b794fc4"), "name" : "hdn_1", "age" : 18, "sex" : "W", "likes" : 100 }
{ "_id" : ObjectId("5d5ba9db5c2b17268b794fc5"), "name" : "hdn_2", "age" : 18, "sex" : "W", "likes" : 100 }
{ "_id" : ObjectId("5d5ba9df5c2b17268b794fc6"), "name" : "hdn_3", "age" : 18, "sex" : "W", "likes" : 100 }
{ "_id" : ObjectId("5d5ba9eb5c2b17268b794fc7"), "name" : "hdn_4", "age" : 19, "sex" : "W", "likes" : 101 }
{ "_id" : ObjectId("5d5ba9f55c2b17268b794fc8"), "name" : "hdn_5", "age" : 20, "sex" : "W", "likes" : 111 }
{ "_id" : ObjectId("5d5baa015c2b17268b794fc9"), "name" : "zl_5", "age" : 20, "sex" : "M", "likes" : 111 }
{ "_id" : ObjectId("5d5baa115c2b17268b794fca"), "name" : "zl_4", "age" : 20, "sex" : "M", "likes" : 11 }
{ "_id" : ObjectId("5d5baa1d5c2b17268b794fcb"), "name" : "zl_3", "age" : 20, "sex" : "M", "likes" : 110 }
{ "_id" : ObjectId("5d5baa255c2b17268b794fcc"), "name" : "zl_2", "age" : 20, "sex" : "M", "likes" : 90 }
{ "_id" : ObjectId("5d5baa2d5c2b17268b794fcd"), "name" : "zl_1", "age" : 20, "sex" : "M", "likes" : 1 }
- 在user集合中创建联合索引,字段为name和sex:
db.user.ensureIndex({name:1,sex:1})
- 查询:db.user.find({sex:'W'},{name:1,_id:0})
对于以上查询,MongoDB不会user数据库文档中去查找。相反,它会从索引中提取数据,这是非常快速的数据查询。
由于我们的索引中不包含_id字段,_id在查询中会默认返回,我们可以在MongoDB的查询结果集中排除它,这样才会索引覆盖查询。
换句话说:查询条件是索引的一部分,返回字段也是索引的一部分,这样称为索引覆盖查询。
-----------------------------------------MongoDB查询分析------------------------------------
MongoDB查询分析可以确保我们所建立的索引是否有效,是查询语句性能分析的重要工具。
MongoDB查询分析常用的函数有:explain()和hint()。
explain():提供查询信息,使用索引及查询统计等,有利于对索引优化。
在------------MongoDB覆盖索引查询------------章节已经建立索引的基础之上进行以下操作:
db.user.find({sex:'W'},{name:1,_id:0}).explain()
hint():使用hint()来强制MongoDB使用一个指定的索引。
例子: db.user.find({sex:'W'},{name:1,_id:0}).hint({name:1,sex:1})
使用explain()函数分析以上查询:db.user.find({sex:'W'},{name:1,_id:0}).hint({name:1,sex:1}).explain()
-----------------------------------------MongoDB原子操作:findAndModify()------------------------------------
注意:MongoDB不支持事务,也就是MongoDB不能保证数据的完整性。
但是MongoDB提供了许多原子操作,比如文档的保存,修改,删除等,这些都是原子操作。
所谓的原子操作就是要么这个文档保存到MongoDB,要么没有保存到MongoDB,不会出现查询到的文档没有保存完整的情况。
原子操作数据模型
{
title:'MongoDB教程',
checkout:[{
by:'hdn',
date:new Date()
}]
}
使用findAndModify()方法在一个文档中确保多个字段同步更新。
db.book.findAndModify({
query:{
title:'MongoDB教程'
},
update:{
$set:{title:'MongoDB学习'},
$push:{checkout:{by:'hedanning',date:new Date()}}
}
})
原子操作常用命令:
- $set:用来指定一个键并更新键值,若键不存在就创建 —— { $set : { field : value } }
db.book.findAndModify({
query:{
title:'MongoDB学习'
},
update:{
$set:{price:99.99}
}
})
- $unset:用来删除一个键 —— { $unset : { field : 1} }
将price删除
db.book.findAndModify({
query:{
title:'MongoDB学习'
},
update:{
$unset:{price:1}
}
})
- inc:可以对文档的某个数字类型值的键进行增减的操作 —— { $inc : { field : value } }
db.book.findAndModify({
query:{
title:'MongoDB学习'
},
update:{
$inc:{price:1}
}
})
- push:把value追加到field里面去,field一定要是数组类型,如果field不存在,会新增一个数组类型加进去 ——
{ $push : { field : value } }
db.book.findAndModify({
query:{
title:'MongoDB学习'
},
update:{
$push:{author:{name:'AAA',city:'china'}}
}
})
- $pull:从数组field内删除一个等于value值 —— { $pull : { field : _value } }
db.book.findAndModify({
query:{
title:'MongoDB学习'
},
update:{
$pull:{checkout:{by:'hdn'}}
}
})
- $addToSet:增加一个值到数组内,而且只有当这个值不在数组内才增加。否则,不会增加 —— {$addToSet:{key:value}}
区别:$push和$addToSet的区别?
$push:不论要添加的值数组中是否存在,始终都会进行添加。
$addToSet:只有当数组中不存在将要添加的值,该值才会添加到数组中,否则,不进行添加。
- $pop:删除数组中的第一个值和最后一个值。
删除最后一个元素:{$pop:{field:1}} 删除第一个元素:{$pop:{field:-1}}
- $rename:修改字段名称 —— {$bit : { field : {and : 5}}}
db.book.findAndModify({
query:{
title:'MongoDB学习'
},
update:{
$rename:{email:'Email'}
}
})