当前位置: 凤凰彩票官方app > 编程知识 > 正文

内嵌数组,MongoDB学习笔记三

时间:2019-11-21 02:48来源:编程知识
目录 插入insert 一、前言 (生龙活虎)运营情状 二、后期希图干活 (豆蔻梢头) 成立 MongoDBContextMongoDb操作上下文类 (二卡塔 尔(阿拉伯语:قطر‎创设测量试验类 (三卡塔尔创设测量检

目录

插入insert

  • 一、前言
    • (生龙活虎) 运营情状
  • 二、后期希图干活
    • (豆蔻梢头) 成立 MongoDBContext MongoDb操作上下文类
    • (二卡塔 尔(阿拉伯语:قطر‎创设测量试验类
    • (三卡塔尔创设测量检验代码
  • 三、内嵌数组增日币素操作
    • (风姿罗曼蒂克卡塔 尔(英语:State of Qatar) Update.Set()方法 替换内嵌数组(不推荐使用卡塔 尔(阿拉伯语:قطر‎
    • (二卡塔尔Update.Push()方法 直接将成分压入内嵌数组(推荐卡塔尔
    • (三) Update.PushEach()方法 将多少个因素压入内嵌数组(推荐卡塔 尔(英语:State of Qatar)
  • 四、内嵌数组删除成分操作
    • (生机勃勃) Update.Set()方法 替换内嵌数组(不推荐使用卡塔尔
    • (二) Update.Pull()方法 Pull删除贰个要素(推荐卡塔尔国
    • (三卡塔尔Update.PullFilter()方法 删除过滤器删除成分(推荐卡塔 尔(英语:State of Qatar)
  • 五、内嵌数组修正成分操作
    • (意气风发卡塔尔国 Update.Set() Set先查询后更正(不推荐卡塔尔国
    • (二卡塔 尔(英语:State of Qatar) Update.Set() 协作过滤器改正(推荐卡塔尔
  • 六、内嵌数组查找成分操作(Linq)
    • (意气风发卡塔尔Linq查询一条记下(推荐卡塔 尔(英语:State of Qatar)
    • (二卡塔尔国Linq查询分页 (推荐)
  • 七、总结

单条插入

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

一、前言

本教程是入门基础教程,主要是笔者在项目中使用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 }
> 

(朝气蓬勃) 运市场价格况

.net版本

.Net Framwork 4.6.2 x64

MongoDb数据库版本

MongoDb 3.6.2 x64

使得版本

MongoDb Driver 2.5

作者使用的MongoDB驱动是合法2.5本子驱动,大家能够由此下载GitHub源码自行编写翻译,只怕通过NuGet包管理工科具安装。
GitHub地址:戳风度翩翩戳查看、Link
NuGet地址:戳风华正茂戳查看、Link

本例源码已上传至Gitee

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

删去文书档案

二、早先时期计划干活

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 }
> 

(黄金时代) 成立 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
>

(二卡塔尔国创制测量检验类

如若当前是一个物联网数据搜罗项目,在那之中有那多少个个传感器节点(SensorNode),各样节点都会发出记录(Records),一条记下(Record)由数据值(Data)和记录时间(RecordDateTime)组成。须求对其Recods内嵌数组进行增加和删除改查操作。

那么由上必要能够抽象出如下目的关联,在骨子里行使中,不建议将记录第一手放在SensorNode下,因为Mongodb数据库对单个文书档案有16MB的尺寸节制

    /// <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" }
> 

(三卡塔 尔(英语:State of Qatar) 创设测量检验代码

新建一个控制台程序,加入以下代码。对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 } }
> 

广大的失实是询问条件极其到了八个文书档案,然后更新时由于第三个参数的留存就爆发重复的 _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')}."
        }
})
> 

三、内嵌数组增比索素操作

选用改革器

动用原子性的校勘纠正器,钦点对文书档案的某个字段进行立异。更新改过器是种至极的键,用来钦点复杂的翻新操作,举个例子改革、增多或然删除键,还恐怕是操作数组或许内嵌文书档案。

> 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 }
> 

明明相配3条,却只改了一条。原本MongoDB暗中同意只会更新相配的第一条,如若要立异多条,还得钦定参数。

选择修正器时,_id的值不能够改造。(整个文书档案替换时可以转移“_id”)

(风度翩翩卡塔 尔(英语:State of Qatar) Update.Set()方法 替换内嵌数组(不引入使用卡塔 尔(英语:State of Qatar)

此措施首要行使操作符[$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"
        }
}

追加、删除、改正键时,应该选取$起头的校正器,不然大概会将全部文书档案替换掉。

(二卡塔 尔(英语:State of Qatar)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 用来充实已有键的值,恐怕该键空头支票这里就创建一个。对于创新解析数据、因果关系、投票大概别的有生成数值的地点很有利。

> 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"}"
        }
})
> 

(三) Update.PushEach()方法 将四个因素压入内嵌数组(推荐卡塔 尔(英语:State of Qatar)

此格局运用的也是[$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

数组校订器

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

$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()方法 替换内嵌数组(不引入应用卡塔尔国

此措施思路和充实成分的笔触少年老成致,品质十分低,不推荐使用。

#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

能够将它使用在一部分相比较复杂的数组操作中。使用 $each 子操作符,能够经过叁回 $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 ] ] }

 

(二) Update.Pull()方法 Pull删除贰个成分(推荐卡塔 尔(阿拉伯语:قطر‎

// 假设需要删除的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);

(三卡塔尔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

五、内嵌数组匡正成分操作

(风姿罗曼蒂克卡塔尔国 Update.Set() Set先查询后更改(不引入卡塔 尔(英语:State of Qatar)

此方法品质相当低,不推荐。

#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

(二卡塔尔 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

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

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

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

(后生可畏卡塔 尔(英语:State of Qatar)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

(二卡塔尔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方法二义性解决方案:

七、总结

  本文简单的介绍了MongoDB C#驱动对内嵌文档的增删改查操作,笔者水平有限,如有错误请批评指正!
  欢迎转载,请注明来源!

编辑:编程知识 本文来源:内嵌数组,MongoDB学习笔记三

关键词:

  • 上一篇:没有了
  • 下一篇:没有了