MongoDB学习(一)一一MongoDB关系

MongoDB

关于MongoDB的安装以及基本操作我就不介绍了,网上也有很多的教程,今天我们来看MongoDB的高级操作。
这是我的版本:

1
2
3
4
5
6
7
8
kelele67@liuqideMacBook-Pro /usr/local/mongodb/bin ./mongo --version
MongoDB bash version v3.4.2
git version: 3f76e40c105fc223b3e5aac3e20dcd026b83b38b
allocator: system
modules: none
build environment:
distarch: x86_64
target_arch: x86_64

MongoDB 关系

作为一个NoSQL数据库,MongoDB 的关系表示多个文档之间在逻辑上的相互联系。

文档间可以通过嵌入和引用来建立联系。

MongoDB 中的关系有:

  • 1:1 (1对1)
  • 1: N (1对多)
  • N: 1 (多对1)
  • N: N (多对多)

这里我们用 User 文档和 Address 文档来作为例子

首先我们使用自己的数据库kelele67,并插入两个文档在users和address集合中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
> use kelele67
switched to db kelele67
> db.users.insert({
... "name": "kelele67",
... "sex": "male",
... "tel": "666666"
... })
WriteResult({ "nInserted" : 1 })
> db.address.insert({
... "city": "ChongQing",
... "university": "CQU",
... "dorm_no": "636"
... })
WriteResult({ "nInserted" : 1 })

插入了两个文档:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
> db.users.find().pretty()
{
"_id" : ObjectId("590858b164d6ed405ce2ad7d"),
"name" : "kelele67",
"sex" : "male",
"tel" : "666666"
}
> db.address.find().pretty()
{
"_id" : ObjectId("590858d464d6ed405ce2ad7e"),
"city" : "ChongQing",
"university" : "CQU",
"dorm_no" : "636"
}

嵌入式关系

第一个我们要实现的是嵌入式关系

使用嵌入式方法,我们可以把用户地址嵌入到用户的文档中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
> db.users.save({
... "_id" : ObjectId("590858b164d6ed405ce2ad7d"),
... "name" : "kelele67",
... "sex" : "male",
... "tel" : "666666",
... "address": [
... {
... "city" : "ChongQing",
... "university" : "CQU",
... "dorm_no" : "636"
... },
... {
... "city" : "BeiJing",
... "university" : "PKU",
... "dorm_no" : "123"
... }]
... })
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

可以看到我们更新了文档为嵌入式关系:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
> db.users.find().pretty()
{
"_id" : ObjectId("590858b164d6ed405ce2ad7d"),
"name" : "kelele67",
"sex" : "male",
"tel" : "666666",
"address" : [
{
"city" : "ChongQing",
"university" : "CQU",
"dorm_no" : "636"
},
{
"city" : "BeiJing",
"university" : "PKU",
"dorm_no" : "123"
}
]
}

这样做的好处是比较容易的获取和维护数据,因为你的数据都保存在了一个文档中

例如,你可以这样查询用户的地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
> db.learn.findOne({"name":"kelele67"},{"address":1})
{
"_id" : ObjectId("59084f1a64d6ed405ce2ad78"),
"address" : [
{
"city" : "ChongQing",
"university" : "CQU",
"dorm_no" : "636"
},
{
"city" : "BeiJing",
"university" : "PKU",
"dorm_no" : "123"
}
]
}

这种结构的缺点是,如果用户和用户地址在不断增加,数据量不断变大,会影响读写性能

引用式关系

这种是常用的方法,通过引用文档的 id 字段来建立关系:

由于我们刚才用的save操作,所以覆盖了原来的文档,
我们先drop掉users,再像上面一样重新insert一遍,

1
2
3
4
5
6
7
8
9
> db.users.drop()
true
> db.users.insert({
... "name": "kelele67",
... "sex": "male",
... "tel": "666666"
... })
> db.users.find()
{ "_id" : ObjectId("59085a2364d6ed405ce2ad7f"), "name" : "kelele67", "sex" : "male", "tel" : "666666" }

接下来我们建立引用式关系:

1
2
3
4
5
6
7
8
9
10
11
> db.users.save({
... "_id" : ObjectId("59085a2364d6ed405ce2ad7f"),
... "name" : "kelele67",
... "sex" : "male",
... "tel" : "666666",
... "address_ids": [
... ObjectId("590858d464d6ed405ce2ad7e"),
... ObjectId("59085a2364d6ed405ce2ad7f"),
... ]
... })
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

可以看到我们更新了文档为引用式关系:

1
2
3
4
5
6
7
8
9
10
11
> db.users.find().pretty()
{
"_id" : ObjectId("59085a2364d6ed405ce2ad7f"),
"name" : "kelele67",
"sex" : "male",
"tel" : "666666",
"address_ids" : [
ObjectId("590858d464d6ed405ce2ad7e"),
ObjectId("59085a2364d6ed405ce2ad7f")
]
}


以上例子中,用户文档的 address_ids 字段包含用户地址的对象id(ObjectId)数组。
我们可以读取这些用户地址的对象id(ObjectId)来获取用户的详细地址信息。

这种方法需要两次查询,第一次查询用户地址的对象id(ObjectId),第二次通过查询的id获取用户的详细地址信息:

1
2
3
4
5
6
7
8
9
10
11
12
>var result = db.users.findOne({"name":"kelele67"},{"address_ids":1})
>var addresses = db.address.find({"_id":{"$in":result["address_ids"]}})
> result
{
"_id" : ObjectId("59085a2364d6ed405ce2ad7f"),
"address_ids" : [
ObjectId("590858d464d6ed405ce2ad7e"),
ObjectId("59085a2364d6ed405ce2ad7f")
]
}
> addresses
{ "_id" : ObjectId("590858d464d6ed405ce2ad7e"), "city" : "ChongQing", "university" : "CQU", "dorm_no" : "636" }

MongoDB 数据库引用

上面我们学习了使用MongoDB的引用来规范数据结构文档

引用一共有两种:

  • 手动引用(Manual References)
  • DBRefs

当我们在不同的集合中 (例如address_home, address_office, address_mailing, 等)存储不同的地址。
这样,我们在调用不同地址时,也需要指定集合,一个文档从多个集合引用文档,我们应该使用 DBRefs。

使用DBRefs

基本格式:

1
{ $ref : , $id : , $db : }

  • $ref:集合名称

  • $id:引用的id

  • $db:数据库名称,可选参数

举个栗子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
db.users.save({
... "_id" : ObjectId("59085a2364d6ed405ce2ad7f"),
... "name" : "kelele67",
... "sex" : "male",
... "tel" : "666666",
<!-- ... "address_ids": [
... ObjectId("590858d464d6ed405ce2ad7e"),
... ObjectId("59085a2364d6ed405ce2ad7f"),
... ] -->
... "address": {
"$ref": "address"
"$id": "590858d464d6ed405ce2ad7e"
"$db": "kelele67"
}
... })