MongoDB学习系列 -- 数组修改器

数组算得上是MongoDB中最重要的数据类型了,对于数组的常见操作有添加、修改、删除等。

上一节我们已经学习了$inc、$set 、$unset等修改器,另外还有$pop、$push、$addToSet等数组修改器。今天我们就来重点学习一下数组修改器的内容。

沿袭上一篇的风格,对于每一个修改器,我们尽量使用shell来实践一下。

一、$push

如果指定的key已经存在,则向数组末尾加入一个元素,如果不存在,则会创建一个新的数组

db.student.findOne({"sex":1,'name':'lily'})
{
	"_id" : ObjectId("5b3f79a5d16c252df9d143fc"),
	"name" : "lily",
	"age" : 31,
	"sex" : 1,
	"address" : "bj"
}
> db.student.update({"sex":1,'name':'lily'},
... {$push:{
... 'comment':[
... {'name':'zhangsan','content':'nice','time':new Date()},
... {'name':'lisi','content':'not bad','time':new Date()}]}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.student.findOne({"sex":1,'name':'lily'})
{
	"_id" : ObjectId("5b3f79a5d16c252df9d143fc"),
	"name" : "lily",
	"age" : 31,
	"sex" : 1,
	"address" : "bj",
	"comment" : [
		[
			{
				"name" : "zhangsan",
				"content" : "nice",
				"time" : ISODate("2018-07-07T07:48:39.173Z")
			},
			{
				"name" : "lisi",
				"content" : "not bad",
				"time" : ISODate("2018-07-07T07:48:39.173Z")
			}
		]
	]
}

自动创建了一个comment数组并插入了两个评论元素。 

注:个人在进行代码测试的时候发现数组中可以插入重复的元素,下面的代码就在comment数组中插入了{'name':'demo'}的重复文档。

db.student.find()
{ "_id" : ObjectId("5b3f782598781e88f5e3462f"), "name" : "xiaoming", "sex" : 1, "age" : 2 }
{ "_id" : ObjectId("5b401a87d3c72e6c8d190cf7"), "date" : ISODate("2018-07-07T01:42:31.010Z") }
{ "_id" : ObjectId("5b40d1ea5c99dd00d4d1585a"), "name" : "lucy", "age" : 1 }
{ "_id" : ObjectId("5b40d2275c99dd00d4d1585b"), "name" : "wangwu", "age" : 1 }
{ "_id" : ObjectId("5b3f79a5d16c252df9d143fc"), "age" : 31, "name" : "lily", "sex" : 1, "address" : "bj", "comment" : [ [ { "name" : "zhangsan", "content" : "nice", "time" : ISODate("2018-07-08T02:54:38.459Z") }, { "name" : "lisi", "content" : "not bad", "time" : ISODate("2018-07-08T02:54:38.459Z") } ], { "name" : "test" } ] }
> db.student.update({'name':'lily'},{$push:{'comment':{'name':'test'}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.student.find()
{ "_id" : ObjectId("5b3f782598781e88f5e3462f"), "name" : "xiaoming", "sex" : 1, "age" : 2 }
{ "_id" : ObjectId("5b401a87d3c72e6c8d190cf7"), "date" : ISODate("2018-07-07T01:42:31.010Z") }
{ "_id" : ObjectId("5b40d1ea5c99dd00d4d1585a"), "name" : "lucy", "age" : 1 }
{ "_id" : ObjectId("5b40d2275c99dd00d4d1585b"), "name" : "wangwu", "age" : 1 }
{ "_id" : ObjectId("5b3f79a5d16c252df9d143fc"), "age" : 31, "name" : "lily", "sex" : 1, "address" : "bj", "comment" : [ [ { "name" : "zhangsan", "content" : "nice", "time" : ISODate("2018-07-08T02:54:38.459Z") }, { "name" : "lisi", "content" : "not bad", "time" : ISODate("2018-07-08T02:54:38.459Z") } ], { "name" : "test" }, { "name" : "test" } ] }

而在实际使用过程中我们是不希望在数组中出现重复的元素的。那如果我们想要实现这部分功能又要如何做呢?你可能想到了,插入之前先查询一下,没有的话再进行插入。那问题来了,查询一个文档是不是在数组里面又要如何操作?流程是不是有点繁琐,有没有现成的命令呢?

答案是可能的,$addToSet就是用来完成这部分功能的。

下面我们使用$addToSet来再执行上面的插入操作

db.student.update({'name':'lily'},{$addToSet:{'comment':{'name':'test'}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })
> db.student.findOne({'name':'lily'})
{
        "_id" : ObjectId("5b3f79a5d16c252df9d143fc"),
        "age" : 31,
        "name" : "lily",
        "sex" : 1,
        "address" : "bj",
        "comment" : [
                [
                        {
                                "name" : "zhangsan",
                                "content" : "nice",
                                "time" : ISODate("2018-07-08T02:54:38.459Z")
                        },
                        {
                                "name" : "lisi",
                                "content" : "not bad",
                                "time" : ISODate("2018-07-08T02:54:38.459Z")
                        }
                ],
                {
                        "name" : "test"
                },
                {
                        "name" : "test"
                }
        ]
}

这个时候你可能又有需求了,能不能批量插入元素到数组中呢?答案也是肯定的,这个时候就需要请出$each了,$each和$addToSet就是一个黄金搭档。

现在,我们想数组中批量插入几条评论

扫描二维码关注公众号,回复: 2525176 查看本文章
db.student.update({'name':'lily'},
... {$addToSet:{
... 'comment':{$each:[{"name":'test1'},{'name':'test2'},{'name':'test3'}]}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
db.student.findOne({'name':'lily'})
{
        "_id" : ObjectId("5b3f79a5d16c252df9d143fc"),
        "age" : 31,
        "name" : "lily",
        "sex" : 1,
        "address" : "bj",
        "comment" : [
                [
                        {
                                "name" : "zhangsan",
                                "content" : "nice",
                                "time" : ISODate("2018-07-08T02:54:38.459Z")
                        },
                        {
                                "name" : "lisi",
                                "content" : "not bad",
                                "time" : ISODate("2018-07-08T02:54:38.459Z")
                        }
                ],
                {
                        "name" : "test"
                },
                {
                        "name" : "test"
                },
                {
                        "name" : "test1"
                },
                {
                        "name" : "test2"
                },
                {
                        "name" : "test3"
                }
        ]
}

OK,上面讲得都是插入文档,如果我想弹出数组头尾部的一些文档呢?答案是使用$pop

二、$pop

语法:{$pop:{key:1}}弹出尾部元素  {$pop:{key:1}}弹出头部元素

1、弹出尾部一个元素

db.student.update({'name':'lily'},{$pop:{'comment':1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.student.findOne({'name':'lily'})
{
        "_id" : ObjectId("5b3f79a5d16c252df9d143fc"),
        "age" : 31,
        "name" : "lily",
        "sex" : 1,
        "address" : "bj",
        "comment" : [
                [
                        {
                                "name" : "zhangsan",
                                "content" : "nice",
                                "time" : ISODate("2018-07-08T02:54:38.459Z")
                        },
                        {
                                "name" : "lisi",
                                "content" : "not bad",
                                "time" : ISODate("2018-07-08T02:54:38.459Z")
                        }
                ],
                {
                        "name" : "test"
                },
                {
                        "name" : "test"
                },
                {
                        "name" : "test1"
                },
                {
                        "name" : "test2"
                }
        ]
}

2、弹出头部一个元素

db.student.update({'name':'lily'},{$pop:{'comment':-1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.student.findOne({'name':'lily'})
{
        "_id" : ObjectId("5b3f79a5d16c252df9d143fc"),
        "age" : 31,
        "name" : "lily",
        "sex" : 1,
        "address" : "bj",
        "comment" : [
                {
                        "name" : "test"
                },
                {
                        "name" : "test"
                },
                {
                        "name" : "test1"
                },
                {
                        "name" : "test2"
                }
        ]
}

注:使用$pop时有一个局限:只能弹出头尾部的单个元素。-1/1只是用来标识弹出头尾而言的,使用其他正负数也可以,但是只会弹出一个。

db.student.update({'name':'lily'},{$pop:{'comment':-2}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.student.findOne({'name':'lily'})
{
        "_id" : ObjectId("5b3f79a5d16c252df9d143fc"),
        "age" : 31,
        "name" : "lily",
        "sex" : 1,
        "address" : "bj",
        "comment" : [
                {
                        "name" : "test"
                },
                {
                        "name" : "test1"
                },
                {
                        "name" : "test2"
                }
        ]
}
> db.student.update({'name':'lily'},{$pop:{'comment':-5}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.student.findOne({'name':'lily'})
{
        "_id" : ObjectId("5b3f79a5d16c252df9d143fc"),
        "age" : 31,
        "name" : "lily",
        "sex" : 1,
        "address" : "bj",
        "comment" : [
                {
                        "name" : "test1"
                },
                {
                        "name" : "test2"
                }
        ]
}

如果我们想要弹出某些某条件的元素呢?没错,就是用下面的$pull


三、$pull

弹出数组中满足某条件的所有元素

db.student.update({'name':'lily'},{$pull:{'comment':{'name':'test1'}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.student.findOne({'name':'lily'})
{
        "_id" : ObjectId("5b3f79a5d16c252df9d143fc"),
        "age" : 31,
        "name" : "lily",
        "sex" : 1,
        "address" : "bj",
        "comment" : [
                {
                        "name" : "test2"
                }
        ]
}

讲到这里,你可能会说,如果我想要修改某个位置的元素,有没有简单的方法呢?答案也是肯定的。

四、定位操作符$

定位操作符经常和$set结合起来使用,下面我们给comment数组中{'name':'test2'}新增一个评论数量字段。

db.student.update({'comment.name':'test2'},{$inc:{'comment.$.count':1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.student.findOne({'name':'lily'})
{
        "_id" : ObjectId("5b3f79a5d16c252df9d143fc"),
        "age" : 31,
        "name" : "lily",
        "sex" : 1,
        "address" : "bj",
        "comment" : [
                {
                        "name" : "test2",
                        "count" : 1
                }
        ]
}

当然,$也有局限性:只更新第一个匹配的元素,关于这点的改进我们后续会再学习。

OK,相信上面的一些修改器已经能cover我们大部分的需求了。

总结:通过上篇文章以及本片文章的学习,我们会发现MongoDB修改器的一个巨大优势:会在key、文档信息不存在时自动进行一个新增操作,这个优势在实际使用中会节约大量的代码提升不小的性能,而且还能解决部分竞态问题。

MongoDB学习系列 -- 基础知识了解

MongoDB学习系列 -- 常见的限制

MongoDB学习系列 -- 数据库、集合、文档的CURD

MongoDB学习系列 -- 文档查询

猜你喜欢

转载自blog.csdn.net/ydm19891101/article/details/80956038
今日推荐