【笔记】MongoDB 一些较为复杂的操作
MongoDB
Mukai Music的用户自建歌单(可以整合其他平台)打算用MongoDB来存储。
使用单表设计,每一个用户为一个Document,包含用户Id int64类型,三个数组分别是:
- 用户自建歌单playlists,包含歌单封面,名字,是否公开,以及内部还包含一个tracks数组,记录所有歌单中的歌曲
- 用户收藏的歌单,记录目标歌单Id 和来源,如果为用户自建歌单,Id则为ObjectId,否则为网易云歌单Id
- 用户喜欢的歌曲,记录所有用户喜欢的单曲。以DataSource_Id形式存储
可以看出,主要遇到的问题,还是在于子数组中,要想办法实现对子数组的各种操作。
创建一个歌单(向sub-array插入元素)
这个遇到的问题在于,用户数据和用户的音乐数据是分开存储的,用户数据完全属于另一个模块。所以如果一个用户刚刚注册完成,MongoDB中还没有包含这个用户的Id的Document,那么他的playlists肯定也无从谈起。
这个可以用MongoDB的setOnInsert进行操作,即用户Id存在的话,就往对应的playlists插入一个歌单,插入用到的是$addToSet操作符,用于向数组插入元素,不然的话,就创建一个用户Document:
用C# 可以这样实现
在Shell中可以如此:
db.user_musics.update(
{ "_id": 100 },
{
$addToSet: { "playlists": playlist0 },
$setOnInsert: { "_id": 100 }
},
{ upsert: true } //upsert是必要的
)
更新歌单信息(更改sub-array中的元素)
例如想要修改歌单的名字,是否公开等等.查阅mongoDB官方文档可以得知,可以用$美刀符号来实现。它的作用:
MongoDB Manual
The positional $ operator identifies an element in an array to update without explicitly specifying the position of the element in the array.
大概就是它可以标识要更新哪个元素,而不需要知道元素在数组哪个位置。
C# :
用Shell:
db.user_musics.update(
{"playlists":
$elemMatch:{"_id": "5edd033ed261111ef49ec6a6" }
},
{
$set:{ "playlists.$.picUrl":"http://pic.mypic.png" }
}
)
查询用户歌单数量(聚合查询sub-array元素个数)
这个稍显复杂,要用到MongoDB的AggregatePipeline,聚合管道,我的理解是这个管道就是类似一个漏斗的东西,从上下一层一层筛选,在最后根据聚合操作符,输出对应的结果。
C# 操作:
采用的是Json的方式来聚会,整个管道有两个部分,第一部分作用是筛选出对应的用户Id。第二部分是通过 $size操作符,得到playlists数组的长度,并且赋值给“count”,同时还排除了“_id”字段,让它不返回,那么最后的查询得到的一个Document,其中就会包含一个Count字段,里面的值就是playlists的长度。
这两个stage0,stage1的两个泛型,就分别是输入输出类型,第一个管道,输入UserMusic,输出UserMusic,因为这里做的是筛选操作。第二个管道,输入的UserMusic,输出一个包含Count字段的内部类(自定义的),因为这里使用了聚合操作。最后我们得到的结构就是一个CountInfo这个内部类对象实例。
Shell写法:
db.user_musics.aggregate(
[
{$match:{"_id": 1}},
{$project :{"count": {$size:"$playlists" }, "_id":0 } }
])
删除歌单 (删除sub-array元素)
主要是用到$Pull操作符,作用是删除array中的元素,这个操作符的介绍在MongoDB Manual里也有,这里不详细说了,直接上代码:
C#
Shell:
db.user_musics.update(
{"_id":1},
{$pull:{"playlists":{ "_id": "5edd033ed261111ef49ec6a6" } }}
)
向歌单中添加歌曲 (向sub-array的sub-array中添加元素)
这个比直接创建歌单麻烦更多。好在,也成功解决了。用到的是也是$addToSet
C#:
和更新一样,需要用$. C#的AddToSetEach可以插入多个元素
Shell:
db.user_musics.update(
{
"_id":1,
"playlists":{$elemMatch:{"_id":"5edd033ed261111ef49ec6a6"}}
},
{
$addToset:{"playlists.$.tracks":{$each:[music1,music2]}}
}
)
从歌单中移除一些歌曲(从sub-array的sub-array中移除元素)
妈的,这个困扰我好久。我一直尝试用“playlists.$.tracks”,但是没有卵用。
我跑去Stack overflow问了下外国小哥才解决。
C#:
可以看到,它用了一个“playlists.0.tracks”.这个0其实是playlists对应的在数组中的位置,第一个歌单,就是0。而不是要删除的元素对应的位置。
那么我们不知道位置咋办,没有关系,我么可以通过聚合进行一次查询。
这个聚合的结果也是一个内部类,包含了index字段。然后将上面的"playlists.0.tracks"中的0换成查询到的index结果就行了。
聚合查询的Shell:
db.user_musics.aggregate(
[
{$match :{"_id":46}},
{$project :{
"index":{ $indexOfArray:["$playlists._id",ObjectId("5edb5734e55ff4378857d2f4")]},
"_id":0
}}
])
删除歌曲的Shell
db.user_musics.update(
{ "_id" : 2 },
{ $pull : { "playlists.0.tracks" : { "sid": "NetEase_1998" } } }
);
- 0
- 0
-
分享