威尼斯人线上娱乐

【威尼斯人线上娱乐】MongoDB学习笔记3,增加和删除改文书档案上

5 4月 , 2019  

目录

插入insert

插入insert

MongoDB是文书档案型数据库,种种文书档案(doc)表示数据的1项记录。比较关系型DB的row只好使用简单的数据类型,doc能够使用复杂的数据类型:内嵌doc,数组。MongoDB的数组是一星罗棋布成分的集聚,使用中括号
[] 表示数组,例如:[1,2,3]的要素是整数值,[{name:”t5″},
{name:”t7″}],[【威尼斯人线上娱乐】MongoDB学习笔记3,增加和删除改文书档案上。 {name:”t5″, age:21}, {name:”t7″, age:22}
]的因素是doc。

  • 一、前言
    • (1)
      运转条件
  • 贰、早先时代准备干活
    • (1) 创造 MongoDBContext
      MongoDb操作上下文类
    • (二)成立测试类
    • (叁)
      创制测试代码
  • 三、内嵌数组增新币素操作
    • (1) Update.Set()方法
      替换内嵌数组(不推荐使用)
    • (二)Update.Push()方法
      直接将成分压入内嵌数组(推荐)
    • (三) Update.PushEach()方法
      将五个因素压入内嵌数组(推荐)
  • 四、内嵌数组删除成分操作
    • (一) Update.Set()方法
      替换内嵌数组(不推荐使用)
    • (二) Update.Pull()方法
      Pull删除一个因素(推荐)
    • (三)Update.PullFilter()方法
      删除过滤器删除成分(推荐)
  • 伍、内嵌数组修改元素操作
    • (一) Update.Set()
      Set先查询后修改(不推荐)
    • (贰) Update.Set()
      合营过滤器修改(推荐)
  • 陆、内嵌数组查找成分操作(Linq)
    • (1)Linq查询一条记下(推荐)
    • (二)Linq查询分页
      (推荐)
  • 七、总结

单条插入

> db.foo.insert({"bar":"baz"})
WriteResult({ "nInserted" : 1 })

单条插入

> db.foo.insert({"bar":"baz"})
WriteResult({ "nInserted" : 1 })

在MongoDB中,数组成分允许再一次,成分的职位是固定的。假如三个数组相等,那么那八个数组的元素和及其地点都平等。

一、前言

本教程是入门基础教程,主要是笔者在项目中使用MongoDB .Net官方驱动对MongoDB内嵌文档的操作时遇到了很多不方便的情况,踩了很多的坑,所以单独整理出来一篇文章,来讲一讲笔者踩坑的过程。

作者水平有限,如有错误还请批评指正!

批量插入

> db.foo.insert([{"_id":1},{"_id":2},{"_id":3}])
BulkWriteResult({
        "writeErrors" : [ ],
        "writeConcernErrors" : [ ],
        "nInserted" : 3,
        "nUpserted" : 0,
        "nMatched" : 0,
        "nModified" : 0,
        "nRemoved" : 0,
        "upserted" : [ ]
})
> db.foo.find()
{ "_id" : 1 }
{ "_id" : 2 }
{ "_id" : 3 }
> 

借使在推行批量布署的历程中有三个文书档案插入退步,那么在这几个文档此前的具有文书档案都会插入成功,之后的具有一切输球。

> db.foo.insert([{"_id":10},{"_id":11},{"_id":10},{"_id":12}])
BulkWriteResult({
        "writeErrors" : [
                {
                        "index" : 2,
                        "code" : 11000,
                        "errmsg" : "E11000 duplicate key error collection: test.foo index: _id_ dup key: { : 10.0 }",
                        "op" : {
                                "_id" : 10
                        }
                }
        ],
        "writeConcernErrors" : [ ],
        "nInserted" : 2,
        "nUpserted" : 0,
        "nMatched" : 0,
        "nModified" : 0,
        "nRemoved" : 0,
        "upserted" : [ ]
})
> db.foo.find()
{ "_id" : 10 }
{ "_id" : 11 }
> 

批量插入

> db.foo.insert([{"_id":1},{"_id":2},{"_id":3}])
BulkWriteResult({
        "writeErrors" : [ ],
        "writeConcernErrors" : [ ],
        "nInserted" : 3,
        "nUpserted" : 0,
        "nMatched" : 0,
        "nModified" : 0,
        "nRemoved" : 0,
        "upserted" : [ ]
})
> db.foo.find()
{ "_id" : 1 }
{ "_id" : 2 }
{ "_id" : 3 }
> 

若是在履行批量陈设的进度中有二个文书档案插入失败,那么在那几个文书档案从前的装有文书档案都会插入成功,之后的兼具1切曲折。

> db.foo.insert([{"_id":10},{"_id":11},{"_id":10},{"_id":12}])
BulkWriteResult({
        "writeErrors" : [
                {
                        "index" : 2,
                        "code" : 11000,
                        "errmsg" : "E11000 duplicate key error collection: test.foo index: _id_ dup key: { : 10.0 }",
                        "op" : {
                                "_id" : 10
                        }
                }
        ],
        "writeConcernErrors" : [ ],
        "nInserted" : 2,
        "nUpserted" : 0,
        "nMatched" : 0,
        "nModified" : 0,
        "nRemoved" : 0,
        "upserted" : [ ]
})
> db.foo.find()
{ "_id" : 10 }
{ "_id" : 11 }
> 

创制示范collection,使用db.collection.insert()函数和数组参数,贰次性向聚集中插入贰个doc。

(一) 运营环境

.net版本

.Net Framwork 4.6.2 x64

MongoDb数据库版本

MongoDb 3.6.2 x64

使得版本

MongoDb Driver 2.5

我利用的MongoDB驱动是合法二.伍版本驱动,大家可以经过下载GitHub源码自行编写翻译,恐怕经过NuGet包管理工科具安装。
GitHub地址:戳1戳查看、Link
NuGet地址:戳一戳查看、Link

本例源码已上传至Gitee

本例源码地址:戳一戳、Link

去除文书档案

除去文书档案

user1={ name:"t1", age:21}
user2={ name:"t2", age:22}
user3={ name:"t3", age:23}

db.users.insert([user1,user2,user3])

二、早先时代准备工作

remove

remove函数接受八个查询文书档案作为参数。符合条件的文书档案才被剔除。删除数据是永久性的,不可能撤销,也不能够恢复生机。

> db.foo.remove()
2016-12-15T19:50:31.721+0800 E QUERY    [thread1] Error: remove needs a query :
DBCollection.prototype._parseRemove@src/mongo/shell/collection.js:406:1
DBCollection.prototype.remove@src/mongo/shell/collection.js:433:18
@(shell):1:1

> db.foo.remove({"_id":10})
WriteResult({ "nRemoved" : 1 })
> db.foo.find()
{ "_id" : 11 }
> 

remove

remove函数接受三个查询文书档案作为参数。符合条件的文书档案才被删去。删除数据是永久性的,不能够收回,也不可能苏醒。

> db.foo.remove()
2016-12-15T19:50:31.721+0800 E QUERY    [thread1] Error: remove needs a query :
DBCollection.prototype._parseRemove@src/mongo/shell/collection.js:406:1
DBCollection.prototype.remove@src/mongo/shell/collection.js:433:18
@(shell):1:1

> db.foo.remove({"_id":10})
WriteResult({ "nRemoved" : 1 })
> db.foo.find()
{ "_id" : 11 }
> 

一,使用dot标记法(dot notation)访问数组成分

(一) 创制 MongoDBContext MongoDb操作上下文类

创建MongoDBContext操作上下文类,用于连接Mongodb数据库和管理Collection对象

using MongoDB.Bson;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Security.Authentication;
using System.Threading.Tasks;

namespace MongoDBEmbeddedOperation
{
    public class MongoDbContext
    {
        #region Fields
        /// <summary>
        /// 缓存Mongodb集合
        /// </summary>
        private Dictionary<string, object> _collectionsMongoDb;

        #endregion

        #region Properties
        /// <summary>
        /// 数据库连接字符串
        /// </summary>
        public string ConnectionString { get; }

        /// <summary>
        /// Mongo客户端设置
        /// </summary>
        public MongoClientSettings Settings { get; }

        /// <summary>
        /// 数据库名称
        /// </summary>
        public string DatabaseName { get; }

        /// <summary>
        /// Mongo上下文 
        /// </summary>
        public IMongoDatabase DbContext { get; }
        #endregion

        /// <summary>
        /// MongoDb数据上下文
        /// </summary>
        /// <param name="connectionString">
        /// 连接字符串,如:"mongodb://username:password@host:port/[DatabaseName]?ssl=true"
        /// 详情参见:http://www.runoob.com/mongodb/mongodb-connections.html
        /// </param>
        public MongoDbContext(string connectionString)
        {
            if (string.IsNullOrWhiteSpace(connectionString))
                throw new ArgumentException("connectionString 连接字符串不能为空!");

            try
            {
                var mongoUrl = new MongoUrl(connectionString);
                ConnectionString = connectionString;
                Settings = MongoClientSettings.FromUrl(mongoUrl);

                if (string.IsNullOrWhiteSpace(mongoUrl.DatabaseName))
                    throw new ArgumentException("数据库名称不能为空!");

                DatabaseName = mongoUrl.DatabaseName;
                // SSL加密
                if (Settings.UseSsl)
                {
                    Settings.SslSettings = new SslSettings
                    {
                        EnabledSslProtocols = SslProtocols.Tls12
                    };
                }

                var mongoClient = new MongoClient(Settings);
                DbContext = mongoClient.GetDatabase(DatabaseName);
            }
            catch (Exception e)
            {
                // TODO: 记录错误日志
                throw;
            }
        }

        /// <summary>
        /// 异步获取集合
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <returns></returns>
        public async Task<IMongoCollection<TEntity>> GetCollectionAsync<TEntity>() where TEntity : class
        {
            // 集合缓存如果为空,那么创建一个
            if (_collectionsMongoDb == null)
            {
                _collectionsMongoDb = new Dictionary<string, object>();
            }

            // 获取集合名称,使用的标准是在实体类型名后添加Set
            var collectionName = $"{typeof(TEntity).Name}Set";

            // 如果集合不存在,那么创建集合
            if (false == await IsCollectionExistsAsync<TEntity>())
            {
                await DbContext.CreateCollectionAsync(collectionName);
            }

            // 如果缓存中没有该集合,那么加入缓存
            if (!_collectionsMongoDb.ContainsKey(collectionName))
            {
                _collectionsMongoDb[collectionName] = DbContext.GetCollection<TEntity>(collectionName);
            }

            // 从缓存中取出集合返回
            return (IMongoCollection<TEntity>)_collectionsMongoDb[collectionName];
        }

        /// <summary>
        /// 集合是否存在
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <returns></returns>
        public async Task<bool> IsCollectionExistsAsync<TEntity>()
        {
            var filter = new BsonDocument("name", $"{typeof(TEntity).Name}Set");
            // 通过集合名称过滤
            var collections = await DbContext.ListCollectionsAsync(new ListCollectionsOptions { Filter = filter });
            // 检查是否存在
            return await collections.AnyAsync();
        }
    }
}

drop

要清空整个集合,那么使用drop直接删除集合会更加快。代价是:无法钦点其余限制标准。整个集合都被删去,全部元数据都不见了。

> for(var i=0;i<1000000;i++){
... db.tester.insert({"foo":"bar","baz":i,"z":10-i})
... }
WriteResult({ "nInserted" : 1 })
> db.tester.find()
{ "_id" : ObjectId("58528543b049609a5fa74f7c"), "foo" : "bar", "baz" : 0, "z" : 10 }
......

Type "it" for more
> db.tester.drop()//插入一百万条数据,使用drop删除,只需1ms
true
>

drop

要清空整个集合,那么使用drop直接删除集合会更快。代价是:无法钦赐别的限制标准。整个集合都被剔除,全体元数据都遗落了。

> for(var i=0;i<1000000;i++){
... db.tester.insert({"foo":"bar","baz":i,"z":10-i})
... }
WriteResult({ "nInserted" : 1 })
> db.tester.find()
{ "_id" : ObjectId("58528543b049609a5fa74f7c"), "foo" : "bar", "baz" : 0, "z" : 10 }
......

Type "it" for more
> db.tester.drop()//插入一百万条数据,使用drop删除,只需1ms
true
>

MongoDB使用 dot 访问数组的因素,或内嵌doc字段。

(贰)成立测试类

若果当前是二个物联网数据收集项目,个中有好多少个传感器节点(SensorNode),各种节点都会生出记录(Records),一条记下(Record)由数据值(Data)和记录时间(RecordDateTime)组成。要求对其Recods内嵌数组举办增加和删除改查操作。

那么由上供给能够抽象出如下目的关系,在实际上利用中,不提出将记录第壹手放在SensorNode下,因为Mongodb数据库对单个文书档案有1陆MB的分寸限制

    /// <summary>
    /// 传感器节点
    /// </summary>
    public class SensorNode
    {
        /// <summary>
        /// ID
        /// </summary>
        public ObjectId Id { get; set; }

        /// <summary>
        /// 记录数
        /// </summary>
        public List<Record> Records { get; set; }
    }

    /// <summary>
    /// 数据记录
    /// </summary>
    public class Record
    {
        /// <summary>
        /// 数据值
        /// </summary>
        public double Data { get; set; }

        /// <summary>
        /// 记录时间
        /// </summary>
        public DateTime RecorDateTime { get; set; }
    }

测试类所对应的Bson结构,如下所示.

{
    "_id" : ObjectId("5aa877e4aa25752ab4d4cae3"),
    "Records" : [ 
        {
            "Data" : 1962163552.0,
            "RecorDateTime" : ISODate("2018-03-16T10:01:14.931Z")
        }, 
        {
            "Data" : 1111405346.0,
            "RecorDateTime" : ISODate("2018-03-16T08:53:14.931Z")
        },
        ......
    ]
}

履新文书档案update

Update有四个必须参数:

一是询问文书档案,用于固定供给立异的指标文书档案

二是修改器文档,用于表明要找到的文书档案实行怎么着修改

革新操作是不可分割的:要是七个更新还要发出,先抵达服务器的先实施,接着执行另八个。

db.foo.insert({
... "name":"yyb",
... "friends":32,
... "enemies":2
... })
WriteResult({ "nInserted" : 1 })
> db.foo.update({"name":"yyb"},{"name":"joe"})//将yyb这个文档修改成{“name”:“joe”}
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.foo.find()
{ "_id" : ObjectId("58528a2bb049609a5fb691bc"), "name" : "joe" }
> 

履新文书档案update

Update有四个必须参数:

1是询问文档,用于固定必要更新的目的文档

二是修改器文书档案,用于注明要找到的文书档案举行什么样修改

革新操作是不可分割的:即使四个立异还要发出,先抵达服务器的先实施,接着执行另八个。

db.foo.insert({
... "name":"yyb",
... "friends":32,
... "enemies":2
... })
WriteResult({ "nInserted" : 1 })
> db.foo.update({"name":"yyb"},{"name":"joe"})//将yyb这个文档修改成{“name”:“joe”}
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.foo.find()
{ "_id" : ObjectId("58528a2bb049609a5fb691bc"), "name" : "joe" }
> 

MongoDB uses the dot notation to access
the elements of an array and to access the fields of an embedded
document.

(3) 成立测试代码

新建一个控制台程序,加入以下代码。对MongodbContext进行初始化,插入用于测试的SensorNode.

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Driver.Linq; // <---- 此引用非常重要
using MongoDB.Driver;

......

private static ObjectId _sensorNodeId;
private static MongoDbContext _dbContext;
private static IMongoCollection<SensorNode> _sensorNodes;
static void Main(string[] args)
{
    // 使用链接字符串创建dbContext对象
    _dbContext = new MongoDbContext("mongodb://hynuAdmin:123456@localhost:27017/HynuIOTA");
    // 创建_Sensors数据库,入口方法不能使用异步,我们等待结果返回
    _sensorNodes = _dbContext.GetCollectionAsync<SensorNode>().Result;
    // 创建一个SensorNode 存入MongoDb数据库中
    _sensorNodeId = ObjectId.Parse("5aa877e4aa25752ab4d4cae3");
    #region 创建测试用例
    var sensorNode = new SensorNode()
    {
        Id = _sensorNodeId,
        Records = new List<Record>()
    };
    // 插入数据库中
    _sensorNodes.InsertOne(sensorNode);
    #endregion
}

添加一个方法,用于随机生成Record对象。

/// <summary>
/// 生成count个Rocred对象
/// </summary>
/// <param name="count">生成数量</param>
/// <returns></returns>
static List<Record> GetRandomRecord(int count)
{
    var records = new List<Record>();
    var rd = new Random();
    for (int i = 0; i < count; i++)
    {
        records.Add(new Record()
        {
            Data = rd.Next(),
            RecorDateTime = DateTime.Now.AddMinutes(rd.Next() % 100)
        });
    }
    return records;
}

文档替换

用三个新文书档案完全替换相配的文书档案,那符合广大形式迁移的图景。

  db.user.insert({
... ... "name":"joe",
... ... "friends":32,
... ... "enemies":2
... ... })
WriteResult({ "nInserted" : 1 })
//将上面这个文档的后两个字段移到子文档为realtionships中
> var joe=db.user.findOne({"name":"joe"})
> joe.relationships={"friends":joe.friends,"enemies":joe.enemies};
{ "friends" : 32, "enemies" : 2 }
> delete joe.friends
true
> delete joe.enemies
true
> db.user.update({"name":"joe"},joe);
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find()
{ "_id" : ObjectId("58529188b049609a5fb691bf"), "name" : "joe", "relationships" : { "friends" : 32, "enemies" : 2 } }
> 

广泛的不当是查询条件同盟到了多少个文书档案,然后更新时出于第3个参数的留存就时有产生重复的 _id 值。数据库会抛出尤其。

> db.user.find()
{ "_id" : ObjectId("585295e3b049609a5fb691c5"), "name" : "yyb", "age" : 21 }
{ "_id" : ObjectId("585295e3b049609a5fb691c6"), "name" : "yyb", "age" : 30 }
{ "_id" : ObjectId("585295e3b049609a5fb691c7"), "name" : "yyb", "age" : 40 }
> joe=db.user.findOne({"name":"yyb","age":30})
{ "_id" : ObjectId("585295e3b049609a5fb691c6"), "name" : "yyb", "age" : 30 }
> joe.age++;
30
> db.user.update({"name":"yyb"},joe)
WriteResult({
        "nMatched" : 0,
        "nUpserted" : 0,
        "nModified" : 0,
        "writeError" : {
                "code" : 16837,
                "errmsg" : "The _id field cannot be changed from {_id: ObjectId('585295e3b049609a5fb691c5')} to {_id: ObjectId('585295e3b049609a5fb691c6')}."
        }
})
> 

文书档案替换

用3个新文书档案完全替换相称的文书档案,这符合广大方式迁移的气象。

  db.user.insert({
... ... "name":"joe",
... ... "friends":32,
... ... "enemies":2
... ... })
WriteResult({ "nInserted" : 1 })
//将上面这个文档的后两个字段移到子文档为realtionships中
> var joe=db.user.findOne({"name":"joe"})
> joe.relationships={"friends":joe.friends,"enemies":joe.enemies};
{ "friends" : 32, "enemies" : 2 }
> delete joe.friends
true
> delete joe.enemies
true
> db.user.update({"name":"joe"},joe);
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find()
{ "_id" : ObjectId("58529188b049609a5fb691bf"), "name" : "joe", "relationships" : { "friends" : 32, "enemies" : 2 } }
> 

左近的错误是查询条件合营到了八个文书档案,然后更新时出于第二个参数的存在就产生重复的 _id 值。数据库会抛出十一分。

> db.user.find()
{ "_id" : ObjectId("585295e3b049609a5fb691c5"), "name" : "yyb", "age" : 21 }
{ "_id" : ObjectId("585295e3b049609a5fb691c6"), "name" : "yyb", "age" : 30 }
{ "_id" : ObjectId("585295e3b049609a5fb691c7"), "name" : "yyb", "age" : 40 }
> joe=db.user.findOne({"name":"yyb","age":30})
{ "_id" : ObjectId("585295e3b049609a5fb691c6"), "name" : "yyb", "age" : 30 }
> joe.age++;
30
> db.user.update({"name":"yyb"},joe)
WriteResult({
        "nMatched" : 0,
        "nUpserted" : 0,
        "nModified" : 0,
        "writeError" : {
                "code" : 16837,
                "errmsg" : "The _id field cannot be changed from {_id: ObjectId('585295e3b049609a5fb691c5')} to {_id: ObjectId('585295e3b049609a5fb691c6')}."
        }
})
> 
users=
{
name:"t1",
age:21,
address: {phone:123,email:"xxx@163.com"},
followers:[{name:"a"},{name:"b"},{name:"c"}]
}

三、内嵌数组增澳元素操作

动用修改器

应用原子性的翻新修改器,钦赐对文书档案的1些字段实行翻新。更新修改器是种独特的键,用来钦点复杂的更新操作,比如修改、添加可能删除键,还大概是操作数组或许内嵌文书档案。

> db.user.find()
{ "_id" : ObjectId("585295e3b049609a5fb691c5"), "name" : "yyb", "age" : 21 }
{ "_id" : ObjectId("585295e3b049609a5fb691c6"), "name" : "yyb", "age" : 30 }
{ "_id" : ObjectId("585295e3b049609a5fb691c7"), "name" : "yyb", "age" : 40 }
> db.user.update({"name":"yyb"},{$inc:{"age":5}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })> db.user.find({"name":"yyb"})
{ "_id" : ObjectId("585295e3b049609a5fb691c5"), "name" : "yyb", "age" : 26 }
{ "_id" : ObjectId("585295e3b049609a5fb691c6"), "name" : "yyb", "age" : 30 }
{ "_id" : ObjectId("585295e3b049609a5fb691c7"), "name" : "yyb", "age" : 40 }
> 

明明相称三条,却只改了一条。原来MongoDB暗中认可只会更新相称的首先条,假若要更新多条,还得钦赐参数。

行使修改器时,_id的值无法更改。(整个文书档案替换时能够改变“_id”)

应用修改器

运用原子性的翻新修改器,钦赐对文档的一点字段进行革新。更新修改器是种新鲜的键,用来钦定复杂的换代操作,比如修改、添加或许删除键,还可能是操作数组大概内嵌文书档案。

> db.user.find()
{ "_id" : ObjectId("585295e3b049609a5fb691c5"), "name" : "yyb", "age" : 21 }
{ "_id" : ObjectId("585295e3b049609a5fb691c6"), "name" : "yyb", "age" : 30 }
{ "_id" : ObjectId("585295e3b049609a5fb691c7"), "name" : "yyb", "age" : 40 }
> db.user.update({"name":"yyb"},{$inc:{"age":5}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })> db.user.find({"name":"yyb"})
{ "_id" : ObjectId("585295e3b049609a5fb691c5"), "name" : "yyb", "age" : 26 }
{ "_id" : ObjectId("585295e3b049609a5fb691c6"), "name" : "yyb", "age" : 30 }
{ "_id" : ObjectId("585295e3b049609a5fb691c7"), "name" : "yyb", "age" : 40 }
> 

明明相配三条,却只改了一条。原来MongoDB默许只会更新相配的率先条,倘诺要翻新多条,还得钦赐参数。

使用修改器时,_id的值无法改变。(整个文书档案替换时得以更改“_id”)

address 字段是内嵌文书档案,查找phone是12叁的doc

(一) Update.Set()方法 替换内嵌数组(不推荐使用)

此措施主要接纳操作符[$set],它的笔触是先将内嵌文书档案的父成分查找出来,然后对子文档数组进行增加和删除改操作,最终将子文书档案数组重新沟通。此措施质量较低,除非涉及到大量改成,不然不引入应用。

    #region 方法一. 查出sensor再更新Records, $set操作 - 随机插入100个元素(不推荐)
    // 查询需修改的sensorNode对象
    var sensor = await _sensorNodes.Find(s => s.Id == _sensorNodeId).FirstOrDefaultAsync();
    // 往Records里面增加随机元素
    sensor.Records.AddRange(GetRandomRecord(100));
    // 构建update Bson, 将原有Records替换为新的Records
    var update = Builders<SensorNode>.Update.Set(d => d.Records, sensor.Records);
    // 使用Update方法
    await _sensorNodes.UpdateOneAsync(s => s.Id == _sensorNodeId, update);
    #endregion

$set与$unset

用来钦点一个字段的值。就算那几个字段不存在,则创建它。

> db.user.insert({
... "name":"yyb",
... "age":20,
... "sex":"male",
... "location":"cd"})
WriteResult({ "nInserted" : 1 })
>> db.user.update({"name":"yyb"},{"$set":{"email":"123@qq.com"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find().pretty()
{
        "_id" : ObjectId("58529e66b049609a5fb691c9"),
        "name" : "yyb",
        "age" : 20,
        "sex" : "male",
        "location" : "cd",
        "email" : "123@qq.com"
}
> 

用 $set 甚至足以修改键的档次。用 $unset 能够将键完全除去。

>  db.user.update(
... ... {"name":"yyb"},
... ... {"$set":{"email":["xx@qq.com","xl@sina.com"]}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find().pretty()
{
        "_id" : ObjectId("58529e66b049609a5fb691c9"),
        "name" : "yyb",
        "age" : 20,
        "sex" : "male",
        "location" : "cd",
        "email" : [
                "xx@qq.com",
                "xl@sina.com"
        ]
}
> db.user.update({"name":"yyb"},{"$unset":{"email":1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find().pretty()
{
        "_id" : ObjectId("58529e66b049609a5fb691c9"),
        "name" : "yyb",
        "age" : 20,
        "sex" : "male",
        "location" : "cd"
}

也足以用 $set 修改内嵌文书档案:

{
        "_id" : ObjectId("5853e17ff7720722b4ded850"),
        "title" : "a blog post",
        "content" : "...",
        "author" : {
                "name" : "yyb",
                "email" : "aaa@sina.com"
        }
}
> db.blog.update(
... {"author.name":"yyb"},
... {"$set":{"author.name":"joe"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.findOne()
{
        "_id" : ObjectId("5853e17ff7720722b4ded850"),
        "title" : "a blog post",
        "content" : "...",
        "author" : {
                "name" : "joe",
                "email" : "aaa@sina.com"
        }
}

日增、删除、修改键时,应该使用$起头的修改器,不然大概会将总体文书档案替换掉。

$set与$unset

用来钦点1个字段的值。借使那么些字段不存在,则开创它。

> db.user.insert({
... "name":"yyb",
... "age":20,
... "sex":"male",
... "location":"cd"})
WriteResult({ "nInserted" : 1 })
>> db.user.update({"name":"yyb"},{"$set":{"email":"123@qq.com"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find().pretty()
{
        "_id" : ObjectId("58529e66b049609a5fb691c9"),
        "name" : "yyb",
        "age" : 20,
        "sex" : "male",
        "location" : "cd",
        "email" : "123@qq.com"
}
> 

用 $set 甚至足以修改键的门类。用 $unset 可以将键完全除去。

>  db.user.update(
... ... {"name":"yyb"},
... ... {"$set":{"email":["xx@qq.com","xl@sina.com"]}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find().pretty()
{
        "_id" : ObjectId("58529e66b049609a5fb691c9"),
        "name" : "yyb",
        "age" : 20,
        "sex" : "male",
        "location" : "cd",
        "email" : [
                "xx@qq.com",
                "xl@sina.com"
        ]
}
> db.user.update({"name":"yyb"},{"$unset":{"email":1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find().pretty()
{
        "_id" : ObjectId("58529e66b049609a5fb691c9"),
        "name" : "yyb",
        "age" : 20,
        "sex" : "male",
        "location" : "cd"
}

也得以用 $set 修改内嵌文书档案:

{
        "_id" : ObjectId("5853e17ff7720722b4ded850"),
        "title" : "a blog post",
        "content" : "...",
        "author" : {
                "name" : "yyb",
                "email" : "aaa@sina.com"
        }
}
> db.blog.update(
... {"author.name":"yyb"},
... {"$set":{"author.name":"joe"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.findOne()
{
        "_id" : ObjectId("5853e17ff7720722b4ded850"),
        "title" : "a blog post",
        "content" : "...",
        "author" : {
                "name" : "joe",
                "email" : "aaa@sina.com"
        }
}

扩充、删除、修改键时,应该利用$初始的修改器,不然或者会将整个文书档案替换掉。

db.users.find({"addr.phone":123})

(2)Update.Push()方法 直接将成分压入内嵌数组(推荐)

此形式首要利用操作符[$push]来充实成分到子文书档案数组,品质好,推荐使用。

// 构建update Bson ,添加一条记录使用Push方法
var updateOne = Builders<SensorNode>.Update.Push(d => d.Records, GetRandomRecord(1).First());
// 更新
await _sensorNodes.UpdateOneAsync(s => s.Id == _sensorNodeId, updateMany);

$inc

 $inc 用来充实已有键的值,恐怕该键不存在那就创办3个。对于创新分析数据、因果关系、投票只怕其余有转移数值的地点很便宜。

> db.games.insert({"games":"pinball","user":"joe"})
WriteResult({ "nInserted" : 1 })
> db.games.update({"games":"pinball"},{"$inc":{"score":50}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.games.findOne()
{
        "_id" : ObjectId("5853e517f7720722b4ded851"),
        "games" : "pinball",
        "user" : "joe",
        "score" : 50
}
> db.games.update({"games":"pinball"},{"$inc":{"score":10000}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.games.findOne()
{
"_id" : ObjectId("5853e517f7720722b4ded851"),
"games" : "pinball",
"user" : "joe",
"score" : 10050
}
>

 $inc 正是特意用来增减数字的。且只可以用来整型、长整型只怕双精度浮点型的值。其余品类的数据会操作败北。

> db.foo.insert({"count":"1"})
WriteResult({ "nInserted" : 1 })

> db.foo.update({},{"$inc":{"count":1}})
WriteResult({
        "nMatched" : 0,
        "nUpserted" : 0,
        "nModified" : 0,
        "writeError" : {
                "code" : 16837,
                "errmsg" : "Cannot apply $inc to a value of non-numeric type. {_id: ObjectId('5853e73df7720722b4ded853')} has the field 'count' of non-numeric type String"
        }
})
> 

 $inc 键的值必须为数字”,不能够使用字符串、数组也许其余非数字的值。要修改其余种类,应该使用 $set 或然数字修改器。

> db.foo.insert({"count":1})
WriteResult({ "nInserted" : 1 })

> db.foo.update({},{"$inc":{"count":"5"}})
WriteResult({
        "nMatched" : 0,
        "nUpserted" : 0,
        "nModified" : 0,
        "writeError" : {
                "code" : 14,
                "errmsg" : "Cannot increment with non-numeric argument: {count: \"5\"}"
        }
})
> 

$inc

 $inc 用来充实已有键的值,大概该键不存在那就创办贰个。对于创新分析数据、因果关系、投票大概其余有转移数值的地点很有益于。

> db.games.insert({"games":"pinball","user":"joe"})
WriteResult({ "nInserted" : 1 })
> db.games.update({"games":"pinball"},{"$inc":{"score":50}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.games.findOne()
{
        "_id" : ObjectId("5853e517f7720722b4ded851"),
        "games" : "pinball",
        "user" : "joe",
        "score" : 50
}
> db.games.update({"games":"pinball"},{"$inc":{"score":10000}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.games.findOne()
{
"_id" : ObjectId("5853e517f7720722b4ded851"),
"games" : "pinball",
"user" : "joe",
"score" : 10050
}
>

 $inc 正是专门用来增减数字的。且只可以用来整型、长整型也许双精度浮点型的值。别的种类的数据会操作失败。

> db.foo.insert({"count":"1"})
WriteResult({ "nInserted" : 1 })

> db.foo.update({},{"$inc":{"count":1}})
WriteResult({
        "nMatched" : 0,
        "nUpserted" : 0,
        "nModified" : 0,
        "writeError" : {
                "code" : 16837,
                "errmsg" : "Cannot apply $inc to a value of non-numeric type. {_id: ObjectId('5853e73df7720722b4ded853')} has the field 'count' of non-numeric type String"
        }
})
> 

 $inc 键的值必须为数字”,不可能运用字符串、数组只怕别的非数字的值。要修改别的项目,应该利用 $set 只怕数字修改器。

> db.foo.insert({"count":1})
WriteResult({ "nInserted" : 1 })

> db.foo.update({},{"$inc":{"count":"5"}})
WriteResult({
        "nMatched" : 0,
        "nUpserted" : 0,
        "nModified" : 0,
        "writeError" : {
                "code" : 14,
                "errmsg" : "Cannot increment with non-numeric argument: {count: \"5\"}"
        }
})
> 

followers 字段是数组,查询followers中留存name=“b”的doc

(叁) Update.PushEach()方法 将多少个要素压入内嵌数组(推荐)

此措施应用的也是[$push]操作符,只是在生成BsonDocument时允许收取数组。

#region 方法二. $push操作 - 随机插入100个元素(推荐)
// 构建update Bson 添加多条记录使用PushEach方法
var updateMany = Builders<SensorNode>.Update.PushEach(d => d.Records, GetRandomRecord(100));
// 更新
await _sensorNodes.UpdateOneAsync(s => s.Id == _sensorNodeId, updateMany);
#endregion

数组修改器

数组修改器

db.users.find({"followers.name":"b"})

4、内嵌数组删除成分操作

$push

$push 添日币素。假使数组已经存在,会向已有些数组末尾加入3个成分,如果未有就成立二个新的数组。

> db.blog.post.findOne()
{
        "_id" : ObjectId("5853ea01f7720722b4ded855"),
        "title" : "a blog post",
        "content" : "..."
}
> db.blog.post.update(
... {"title":"a blog post"},
... {"$push":{"comments":{"name":"joe","email":"joe@qq.com","content":"nice post"}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.post.findOne()
{
        "_id" : ObjectId("5853ea01f7720722b4ded855"),
        "title" : "a blog post",
        "content" : "...",
        "comments" : [
                {
                        "name" : "joe",
                        "email" : "joe@qq.com",
                        "content" : "nice post"
                }
        ]
}
> 

> db.blog.post.update(
... {"title":"a blog post"},
... {"$push":{"comments":{"name":"bob","email":"bob@sina.com","content":"good post."}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.post.findOne()
{
        "_id" : ObjectId("5853ea01f7720722b4ded855"),
        "title" : "a blog post",
        "content" : "...",
        "comments" : [
                {
                        "name" : "joe",
                        "email" : "joe@qq.com",
                        "content" : "nice post"
                },
                {
                        "name" : "bob",
                        "email" : "bob@sina.com",
                        "content" : "good post."
                }
        ]
}
> 

$push

$push 添英镑素。假诺数组已经存在,会向已部分数组末尾参预三个要素,假使未有就创办三个新的数组。

> db.blog.post.findOne()
{
        "_id" : ObjectId("5853ea01f7720722b4ded855"),
        "title" : "a blog post",
        "content" : "..."
}
> db.blog.post.update(
... {"title":"a blog post"},
... {"$push":{"comments":{"name":"joe","email":"joe@qq.com","content":"nice post"}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.post.findOne()
{
        "_id" : ObjectId("5853ea01f7720722b4ded855"),
        "title" : "a blog post",
        "content" : "...",
        "comments" : [
                {
                        "name" : "joe",
                        "email" : "joe@qq.com",
                        "content" : "nice post"
                }
        ]
}
> 

> db.blog.post.update(
... {"title":"a blog post"},
... {"$push":{"comments":{"name":"bob","email":"bob@sina.com","content":"good post."}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.post.findOne()
{
        "_id" : ObjectId("5853ea01f7720722b4ded855"),
        "title" : "a blog post",
        "content" : "...",
        "comments" : [
                {
                        "name" : "joe",
                        "email" : "joe@qq.com",
                        "content" : "nice post"
                },
                {
                        "name" : "bob",
                        "email" : "bob@sina.com",
                        "content" : "good post."
                }
        ]
}
> 

二,修改字段的值

(一) Update.Set()方法 替换内嵌数组(不推荐使用)

此措施思路和增美金素的思绪1致,质量较低,不引入使用。

#region 方法一 . $set方式删除 - 删除前5个元素(不推荐)
// 查询需修改的sensorNode对象
var sensor = await _sensorNodes.Find(s => s.Id == _sensorNodeId).FirstOrDefaultAsync();
// 删除Record
for (int i = 0; i < 5; i++)
{
    sensor.Records.RemoveAt(i);
}
// 构建update Bson, 将原有Records替换为新的Records
var update = Builders<SensorNode>.Update.Set(d => d.Records, sensor.Records);
// 使用Update方法
await _sensorNodes.UpdateOneAsync(s => s.Id == _sensorNodeId, update);
#endregion

$each

能够将它选取在1些相比较复杂的数组操作中。使用 $each 子操作符,能够通过3遍 $push 操作添加八个值。

比如说:下边将多个新因素添加到数组中。倘诺钦命的数组中只包括四个因素,那么相同和没有选取“$each”的常备的“$push”操作。

db.stock.ticket.insert({"_id":"goog"})
WriteResult({ "nInserted" : 1 })
>  db.stock.ticket.update(
... ... ... {"_id":"goog"},
... ... ... {"$push":{"hourly":{"$each":[562.776,562.790,559.123]}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.stock.ticket.findOne()
{ "_id" : "goog", "hourly" : [ 562.776, 562.79, 559.123 ] }

要促成地点的操作,上面方法也足以。

db.stock.ticket.update(
... ... ... ... ... {"_id":"goog"},
... ... ... ... ... {"$set":{"hourly":[562.776,562.790,559.123]}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.stock.ticket.findOne()
{ "_id" : "goog", "hourly" : [ 562.776, 562.79, 559.123 ] }

而下边那样是那叁个的

 db.stock.ticket.update(
... ... ... ... {"_id":"goog"},
... ... ... ... {"$push":{"hourly":[562.776,562.790,559.123]}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.stock.ticket.findOne()
{ "_id" : "goog", "hourly" : [ [ 562.776, 562.79, 559.123 ] ] }

 

$each

能够将它选拔在局地相比复杂的数组操作中。使用 $each 子操作符,能够通过3遍 $push 操作添加三个值。

譬如:上边将四个新因素添加到数组中。倘诺钦赐的数组中只包涵1个要素,那么同样和尚未选拔“$each”的家常便饭的“$push”操作。

db.stock.ticket.insert({"_id":"goog"})
WriteResult({ "nInserted" : 1 })
>  db.stock.ticket.update(
... ... ... {"_id":"goog"},
... ... ... {"$push":{"hourly":{"$each":[562.776,562.790,559.123]}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.stock.ticket.findOne()
{ "_id" : "goog", "hourly" : [ 562.776, 562.79, 559.123 ] }

要实现地点的操作,上面方法也能够。

db.stock.ticket.update(
... ... ... ... ... {"_id":"goog"},
... ... ... ... ... {"$set":{"hourly":[562.776,562.790,559.123]}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.stock.ticket.findOne()
{ "_id" : "goog", "hourly" : [ 562.776, 562.79, 559.123 ] }

而上面那样是极度的

 db.stock.ticket.update(
... ... ... ... {"_id":"goog"},
... ... ... ... {"$push":{"hourly":[562.776,562.790,559.123]}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.stock.ticket.findOne()
{ "_id" : "goog", "hourly" : [ [ 562.776, 562.79, 559.123 ] ] }

 

在MongoDB中,修改操作首要行使八个修改器:$set 和
$inc,那多个修改器具有upsert性情:假如doc中存在对应的字段,那么修改该字段的值;假使doc中不设有对应的字段,那么在doc中开立异的字段。$inc用于整数型字段,扩充或调减字段的值,$set用于任意数据类型,替换字段的值。

(二) Update.Pull()方法 Pull删除3个要素(推荐)

// 假设需要删除的Record已经被查询转换为对象,那么使用Pull方式删除一个元素
var record = new Record() 
{ Data = 1670941112.0, RecorDateTime = DateTime.Parse("2018-03-16T09:36:14.930Z") };
// 构建Pull删除Bson
var update = Builders<SensorNode>.Update.Pull(s => s.Records, record);
// 使用Update方法
await _sensorNodes.UpdateOneAsync(s => s.Id == _sensorNodeId, update);

一,使用$set修改器,扩充followers数组,使用empty
filter:{},表示更新集合中的全部doc

(三)Update.PullFilter()方法 删除过滤器删除成分(推荐)

#region 方法三. PullFilter方式删除 - 删除符合Filter条件的元素(推荐)
// 如果r.Data == 339119843.0,那么从数组中删除
var update = Builders<SensorNode>.Update.PullFilter(s => s.Records, r => r.Data == 339119843.0);
await _sensorNodes.UpdateOneAsync(s => s.Id == _sensorNodeId, update);
#endregion
db.users.updateMany(
{},
{$set:
   {
    followers:[ {name:"t5"},{name:"t7"} ]
   }
}
)

5、内嵌数组修改成分操作

威尼斯人线上娱乐 1

(一) Update.Set() Set先查询后修改(不推荐)

此措施质量较低,不引入。

#region 方法一. $set方式修改(将某个元素记录时间改为最小值) - 先查询后修改(不推荐)
// 查询需修改的sensorNode对象
var sensor = await _sensorNodes.Find(s => s.Id == _sensorNodeId).FirstOrDefaultAsync();
// 修改Record值
sensor.Records[0].RecorDateTime = DateTime.MinValue;
// 构建update Bson, 将原有Records替换为新的Records
var update = Builders<SensorNode>.Update.Set(d => d.Records, sensor.Records);
// 使用Update方法
await _sensorNodes.UpdateOneAsync(s => s.Id == _sensorNodeId, update);
#endregion

二,使用$inc修改器,扩张age字段的值,各样doc的age字段的都增多一

(二) Update.Set() 协作过滤器修改(推荐)

在数据库内部形成过滤和改动,品质高,推荐使用。

#region 方法二. $set方式修改(将Data = 1835821478.0的元素记录时间改为最大值) - 直接过滤修改(推荐)
// 构造filter 
var filter = Builders<SensorNode>.Filter.Where(s => s.Id == _sensorNodeId) 
& Builders<SensorNode>.Filter.Where(d => d.Records.Any(r => r.Data == 1835821478.0));
// 执行更新
var update = Builders<SensorNode>.Update.Set(d => d.Records[-1].RecorDateTime, DateTime.MaxValue);
await _sensorNodes.UpdateOneAsync(filter, update);
#endregion
db.users.updateMany(
{},
{$inc:{age:1}}
)

6、内嵌数组查找成分操作(Linq)

对于内嵌数组/文档的查询,在.net里建议直接使用Linq或拼接BsonDocument。笔者着重使用Linq方式。

需要先using MongoDB.Driver.Linq,否则Linq不能使用。

威尼斯人线上娱乐 2

(1)Linq查询一条记下(推荐)

关键是SelectMany()方法的运用,此办法用于选取三个数组。

#region SelectMany查询内嵌数组(查询Data = 1340695206.0的第一条记录) - (推荐)
// 转换为Queryable
var result = await _sensorNodes.AsQueryable()
    // 查找对应的sensorNode
    .Where(s => s.Id == _sensorNodeId)
    // 选择Records内嵌数组
    .SelectMany(s => s.Records)
    // 查找Data == 1340695206.0的元素
    .Where(r => r.Data == 1340695206.0)
    // 取第一个
    .FirstOrDefaultAsync();
#endregion

三,$set 和
$inc的不一样之处在于:$set是替换现有字段的值,而$inc是在存活字段值的基础上,扩充或调整和收缩钦定的数值

(贰)Linq查询分页 (推荐)

重大区别使用SelectMany其余与EF Linq情势大多

#region SelectMany、Skip、Take内嵌数组排序分页 - (推荐)
// 页码
var index = 4;
// 页面大小
var size = 10;
// 转换为Queryable
var page = await _sensorNodes.AsQueryable()
    // 查找对应的sensorNode
    .Where(s => s.Id == _sensorNodeId)
    // 选择Records内嵌数组
    .SelectMany(s => s.Records)
    // 根据记录时间排序
    .OrderBy(r => r.RecorDateTime)
    // 跳过 index - 1页数据
    .Skip((index - 1) * size)
    // 选取一页数据
    .Take(size)
    // 转换为集合
    .ToListAsync();
#endregion

对内嵌数组的查询操作主要是通过聚合来实现,以上代码转换为Native方式后是以下命令。

aggregate(
[
    {"$match" : { "_id" : ObjectId("5aa877e4aa25752ab4d4cae3") } }, 
    { "$unwind" : "$Records" }, 
    { "$project" : { "Records" : "$Records", "_id" : 0 } }, 
    { "$sort" : { "Records.RecorDateTime" : 1 } }, { "$skip" : 30 }, { "$limit" : 10 }
]);

设若同时援引了System.Linq 和 MongoDB.Driver.Linq
调用Linq方法会发生艺术二义性,解决方案链接如下。

Linq方法2义性解决方案:

以身作则,使用$set修改age的值,更新截止后,每种doc的age字段都以一

七、总结

  本文简单的介绍了MongoDB C#驱动对内嵌文档的增删改查操作,笔者水平有限,如有错误请批评指正!
  欢迎转载,请注明来源!
db.users.updateMany(
{},
{$set:{age:1}}
)

三,修改数组

只要要向数组中追加或删除叁个成分,$set和$inc
都不可能很好的满意那种需求,MongoDB有专用的 Array
Operator,用于修改数组字段。

1,使用$push向doc中扩大数组,或插队新的要素

$push:要是doc中留存对应的数组字段,那么向数组的后面部分插入1个因素;如若doc中不存在对应的数组字段,那么向doc中成立2个数组字段,并起先化。

演示,第2回调用$push,由于doc中不存在comments字段,由此MongoDB向doc中新建comments
数组字段,并起首化数组

db.users.updateMany(
{},
{$push:{comments:{msg:"c1"}}}
)

示范,后续再一次调用$push,向已有个别数组字段的尾巴扩张八个要素

db.users.updateMany(
{},
{$push:{comments:{msg:"c2"}}}
)

图示,左侧是数组的第壹个成分,左侧是数组的末尾3个因素,使用$push每一次向数组的后面部分增添四个要素

威尼斯人线上娱乐 3

二,向数组字段插入八个因素

$push 修改器每一遍只可以向数组字段的尾巴插入二个要素,搭配使用$each
修改器,每一次能向数组字段中插入七个成分

db.users.updateMany(
{},
{$push:
   {
     comments:{ $each:[ {msg:"c3"}, {msg:"c4"} ] }
   }
}
)

图示,使用$each,2回将四个成分插入到数组的尾部

威尼斯人线上娱乐 4

三,从数组字段的一定岗位处早先插入成分

采取$push 修改器只好将成分插入到数组字段的尾巴,搭配使用$position
修改器,能够钦命成分插入的起第二个人置,$position
必须和$each搭配使用。数组的下标是从0起首的。

db.users.updateMany
(
{},
{$push:
    {comments:
        {
           $each:[ { msg:"c5"}, {msg:"c6"} ],
           $position:2
        }
    }
}
)

图示,使用$position
钦命成分插入的始发地方,将c五,c6依次插入都数组的贰,三职位处,由于数组的小标是从0开首的,c伍,c六是数组的第3,6个成分威尼斯人线上娱乐 5

假如不选择$position 修改器,那么 $push
每便都向数组的结尾写入成分;使用$position
修改器,内定$push插入成分的启幕地点。

$position :The $position modifier
specifies the location in the array at which the
$push
operator insert elements. Without the $position modifier, the
$push
operator inserts elements to the end of the array.

四,限制数组桐月素的数量

在$push
成分时,使用$slice=马克斯Num限制数组成分的最大数额。只要未有达到最大数据,就会向数组中插入新的要素,直到达到最大值。$slice必须和$each搭配使用,假设数组字段的成分的数量已经达到规定的标准最大值,依据马克斯Num值的不等,会有两样的作为:

  • 假如马克斯Num=0,表示将数组清空;
  • 假若马克斯Num是正整数,表示数组只保留后面的马克斯Num个要素;
  • 比方MaxNum是负整数,表示数组只保留前边的马克斯Num个要素;

示范,保留每一个comments的尾声多少个要素

db.users.updateMany(
{},
{$push:
   {comments:
      {
        $each:[ {msg:"c7"}, {msg:"c8"}, {msg:"c9"}],
        $slice:-5
      }
   }
}
)

图示,$slice:-五,MongoDB先将新的要素插入到数组中,将保留数组末尾的五个成分,将数组的别的因素删除

威尼斯人线上娱乐 6

伍,对数组字段的因素进行排序

在限制数组字段的要素数量事先,使用$sort
操作符对成分举办排序,是数组成分有序排列。在$sort操作之后选用$slice:马克斯Num
修改器,由于数组元素是依样画葫芦的,能够只保留体系前面或前边的一定数量的成分。

db.users.updateMany(
{},
{$push:
   {comments:
      {
        $each:[ {msg:"c7"}, {msg:"c8"}, {msg:"c9"}],
        $sort:{msg:-1},
        $slice:-5
      }
   }
}
)

图示,$each 向现有的数组的尾石嘴山插入多少个要素:c七,c捌,c九,$sort:{msg:-一}
对数组遵照msg的降序排序,$slice:-5操作符限制数组的因素数量,只保留数组尾巴部分的多少个要素。

威尼斯人线上娱乐 7

假定数组是[1,2,3]威尼斯人线上娱乐,那连串型,那么$sort:1,遵照一,二,三 升序排列;
$sort:-1,安装叁,2,一 降序排列。

6,使用$addToSet向数组插入无重复的要素

经过$push
插入元素,有一点都不小可能率插入重复的要素,MongoDB允许数组中的成分重复;假诺三个数组不可能插入重复值,能够选择$addToSet修改器,
$addToSet在向数组插入成分时,首先检查成分是还是不是曾经存在于数组中,要是不设有,那么$addToSet将元素插入到数组中;假若存在,那么$addToSet不会插入任何因素。$addToSet只会保障不会插入重复的要素,不应影响数组中早已存在的重新成分。

$addToSet 能够2次性向数组中插入三个因素。

$addToSet only ensures that there are
no duplicate items added to the set and does not affect existing
duplicate elements. $addToSet does not guarantee a particular ordering
of elements in the modified set.

演示,向comments 数组中插入多个messge

db.users.updateMany(
{},
{$addToSet:
   {comments:[ {msg:"c7"}, {msg:"c8"}, {msg:"c9"}] }
}
)

柒,使用$pop删除数组的首先个或尾数因素

把数组看作是队列,下标为0的元素是在队列底部,是数组的首先个因素,小标最大的因素是数组的结尾二个要素。使用$pop删除成分时,{$pop:{array:1}}
表示删除数组的尾声三个因素,{$pop:{array:-一}}
表示删除数组的率先个要素。

db.users.updateMany(
{},
{$pop:{comments:1}}
)

图示,删除数组的末尾一个要素

威尼斯人线上娱乐 8

八,遵照queyr filter删除数组成分

db.users.updateMany(
{},
{$pull:{comments:{msg:"c7"}}}
)

图示,删除数组中msg字段是”c七”的有着因素

威尼斯人线上娱乐 9

玖,遵照数组的下标修改成分,数组下标是从0初叶的

对于js的数组 arr,包蕴多少个element,修改第壹个因素的like
字段,将其值设置为贰.

var arr=[{name:"t1",like:1},{name:"t2",like:2}]
arr[0].like=2
print(tojoson(arr))

在MongoDB中,假设要修改doc中的数组,能够使用 dot notation,使用
arrary.index.田野 对数组中一定岗位的因素举行改动。

db.users.updateMany(
{},
{$inc:{"comments.0.likes":1}}
)

图示,向数组的首先个因素中扩展likes字段,并初步化为一

威尼斯人线上娱乐 10

假定不知晓数组成分的下标,MongoDB提供占位符
$,用于表示从数组中追寻满意query filter的率先个要素。占位符 $
须要对数组实行搜索,查找的query
filter必须显式提供,假设存在数量成分满意query filter,那么$
占位符表示第一个1二分的数组成分的position,如若未有数组成分满足query
filter,那么MongoDB不会对数据任何意义。

$占位符的选用格式,跟数组的因素类型有关:

  • 尽管数组成分是doc,那么使用 $ 占位符的格式是:arrary.$.田野先生。
  • 即使数组的因素类型是原子类型,例如,[1,2,3]等,那么使用那么使用
    $ 占用符的格式是:arrary.$。

$:
Acts as a placeholder to update the first element that matches the query
condition in an update.

示例1,使用empty filter作为query
filter

db.users.updateMany(
{},
{$inc:{"comments.$.unlikes":1}}
)

MongoDB抛出荒谬音讯:

“errmsg” : “The
positional operator did not find the match needed from the query.
Unexpanded update: comments.$.unlikes”

证实 query filter 不能够动用empty filter,必须显式提供query filter。

示例贰,对数组成分举办查询,只要存在别的三个因素的msg字段的值c四,就在该因素中追加一个unlikes字段,并最先化为一.

db.users.updateMany(
{comments:
    {$elemMatch:{msg:"c4"}}
},
{$inc:
    {"comments.$.unlikes":1}
}
)

图示,$ 占位符表示相称query filter的率先个要素

威尼斯人线上娱乐 11

肆,数组的询问

壹,成分相配符 $elemMatch,使用数组元素实行标准化同盟

$elemMatch
是对数组成分的字段进行相称,如若成分或因素的字段满意查询条件,那么重返该因素所在的doc。

格式是:{array:{$elemMatch:{field_query_filter,,,,}}}

The $elemMatch operator matches
documents that contain an array field with at least one element that
matches all the specified query criteria.

db.users.find({comments:{$elemMatch:{like:1}}})

示范1,数组成分是整数类型(原子类型)

{ _id: 1, results: [ 82, 85, 88 ] }
{ _id: 2, results: [ 75, 88, 89 ] }

db.scores.find(
   { results: { $elemMatch: { $gte: 80, $lt: 85 } } }
)

询问结果是:只有_id为一的doc的数组成分82满足query filter

{ "_id" : 1, "results" : [ 82, 85, 88 ] }

示例2,数组成分是doc

{ _id: 1, results: [ { product: "abc", score: 10 }, { product: "xyz", score: 5 } ] }
{ _id: 2, results: [ { product: "abc", score: 8 }, { product: "xyz", score: 7 } ] }
{ _id: 3, results: [ { product: "abc", score: 7 }, { product: "xyz", score: 8 } ] }

db.survey.find(
   { results: { $elemMatch: { product: "xyz", score: { $gte: 8 } } } }
)

询问结果是:

{ "_id" : 3, "results" : [ { "product" : "abc", "score" : 7 }, { "product" : "xyz", "score" : 8 } ] }

二,数组的可比,使用数组实行标准化同盟

利用数组举行比较时,只要数组中的任何因素满意query filter,就非凡成功。

只要有以下八个doc,每一个doc中都有二个grades 数组:

{ "_id" : 1, "grades" : [ 80, 85, 90 ] }
{ "_id" : 2, "grades" : [ 88, 90, 92 ] }
{ "_id" : 3, "grades" : [ 85, 100, 90 ] }

示范壹,对于query filter:{grades:{$gt:八伍,
$lt:十0}},分析那贰个数组是还是不是满足:

  • 第二个数组:成分 90 满意当先 捌伍,全体的成分都低于十0
  • 第三个数组:全部的要素满意条件
  • 第贰个数组:成分90,十0
    满足超过85的标准化,成分八伍,90满足小于拾0的规格 

于是,只要数组中有其余一个因素满足qeury filter,纵然满意qeury
filter,那二个数组都满意query filter。
示例2,query filter:{grades:90}

只要数组中有一个成分的值是90,就满意query
filter,因而,那三个数组都满意条件。

三,查询数组成分的数据

$size操作符,要是doc中设有数组,并且数组的成分满意$size钦赐的尺度,那么重返该doc。

The $size operator matches any array with
the number of elements specified by the argument.

db.collection.find( { array: { $size: n } } );

4,数组包括钦命的八个因素

The $all operator selects the documents
where the value of a field is an array that contains all the specified
elements.

{ array : { $all: [ <value1> , <value2> ... ] } }

$all
表示集合的含有关系,全集包涵子集的持有因素。假若数组A包括数组B,那么A是B的全集,B是A的子集。子集中的有着因素,都存在于全集;全集中的要素,不自然存在于子集。

借使array包罗内定的数组,那么知足$all条件,再次来到doc,表示钦赐数组的因素都存在于array。

示范1,假使多少个数组相等,那么那五个数组的要素和及其地方都平等,即在数组的平等地点上,其成分相同。

那三个数组大相径庭,arr① 和 arr2成分数量壹样,不过存在一样地点上(下标是:壹,二)的成分分歧;

arr1=[1,2,3] 
arr2=[1,3,2]
arr3=[1,2]

示例二,包蕴关系(数组成分是int,字符串等原子类型)

$all代表的是包涵关系,对于arr=[1,2,3],满足条件{arr:{$all:[2,1]}},arr存在成分一,2。

演示,查询数组中还要存在2,三的doc

{ "_id" : 1, "g" : [ 1, 2, 3 ] }
{ "_id" : 2, "g" : [ 4, 2, 3 ] }
{ "_id" : 3, "g" : [ 4, 2, 5 ] }

db.foo.find({g:{$all:[2,3]}})

查询结果是

{ "_id" : 1, "g" : [ 1, 2, 3 ] }
{ "_id" : 2, "g" : [ 4, 2, 3 ] }

示例三,包涵关系(数组成分是doc)

示范,对于以下集合,每种doc中都有三个数组字段qty,各样数组中含有多少个成分,每一种成分都以内嵌doc。

{_id:1,
   qty: [
          { size: "S", num: 10, color: "yellow" },
          { size: "M", num: 45, color: "blue" },
          { size: "L", num: 100, color: "green" }
        ]
}
{_id:2,
   qty: [
          { size: "S", num: 10, color: "blue" },
          { size: "M", num: 100, color: "red" },
          { size: "L", num: 100, color: "green" }
        ]
}

查询数组成分中color字段同时存在blue 和 green的doc,这五个doc都满足条件。

 db.foo.find({"qty.color":{$all:["blue","green"]}})

 

参考doc:

Update
Operators

Array Update
Operators

Query and Projection
Operators


相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图