背景

最近公司系统还原用户时偶尔会出现部分用户信息未还原成功的问题,作为开发人员,最头疼的不是代码存在bug,而是测试发现了bug,但一旦我去重现,它就不见了。Are you kidding me?

经过漫长的沟通与尝试,终于发现了端倪,这个问题只有在多人同时操作修改同一用户信息时才会出现。

哦,那你死定了,小bug。

分析

经过短暂的代码review,发现还原用户时,代码中会先把用户获取出来,然后修改用户信息,最后再将修改后的用户更新至数据库中。

var user1 = collection.Find(x => x.Id == "user1").FirstOrDefault();
user1.Name = "B";
collection.ReplaceOneAsync(x => x.Id == "user1", user1);

这就导致代码并发运行时,后面的可能覆盖前方的操作。

如下图操作1及操作2执行结束后,数据库中用户1的名称为A,年龄为18;操作1的修改用户名称为B被覆盖.

所以我们需要采用原子操作来修改用户信息,我们调整代码如下

UpdateDefinition<Persion> update = Builders<Persion>.Update.Set(y => y.Name, "B");
collection.UpdateOne(x => x.Id == "user1", update);

这样就把修改操作交给数据库来执行,仅修改要修改的属性,避免操作互相影响;

看到这里有的大神就会吐槽,这么简单的东西也好意思拿来说,请在耐心往下看,重点在下边。

重点

如果我们的数据结构类似这样:

/// <summary>
/// 人
/// </summary>
[BsonIgnoreExtraElements]
public class Persion : BaseEntity
{
/// <summary>
/// 名称
/// </summary>
[BsonElement("name")]
public string Name { get; set; } /// <summary>
/// 年龄
/// </summary>
[BsonElement("age")]
public int Age { get; set; } /// <summary>
/// 亲戚
/// </summary>
[BsonElement("relatives")]
public List<Relative> Relatives { get; set; } }
/// <summary>
/// 亲属
/// </summary>
public class Relative
{
/// <summary>
/// 名称
/// </summary>
[BsonElement("name")]
public string Name { get; set; } /// <summary>
/// 与本人关系
/// </summary>
[BsonElement("relationship")]
public Relationship Relationship { get; set; }
}
/// <summary>
/// 与本人关系
/// </summary>
public enum Relationship
{
/// <summary>
/// 爸爸
/// </summary>
father = ,
/// <summary>
/// 妈妈
/// </summary>
mother = ,
/// <summary>
/// 儿子
/// </summary>
son = ,
/// <summary>
/// 女儿
/// </summary>
daughter = ,
/// <summary>
/// 不明
/// </summary>
unknow =
}

如果我们想更新名称为“赵小明”的人的名称为“赵刚”的亲戚与本人关系为“爸爸”,请考虑,应该怎么处理。

注意:人的亲戚可以有多个,所以是List。

这时我们就用到了ArrayFilters对象,其存在于UpdateOptions中,如果没有系统的看过MongoDB的接口,我相信大部分人都会忽略它。

好了,废话不多说,让我们来看看它的用法吧

FilterDefinition<Persion> filter = Builders<Persion>.Filter.Where(x => x.Name == "赵小明" && x.Relatives != null && x.Relatives.Count > );

UpdateDefinition<Persion> update = Builders<Persion>.Update.Set("relatives.$[i].relationship", Relationship.father);

var option = new UpdateOptions()
{
ArrayFilters = new List<ArrayFilterDefinition> {
new JsonArrayFilterDefinition<Relationship>("{'i.name': '赵刚'}")
}
}; collection.UpdateMany(filter, update, option);

可以看到,我们先生成一个查询条件,名称为“赵小明”,存在亲戚的人;

然后更新其"relatives.$[i].relationship"属性,为Relationship.father,其中的$[i]为占位符;

再生成一个决定$[i]值的JsonArrayFilterDefinition<Relationship>("{'i.name': '赵刚'}");

最后用这些条件来更新数据库。

引申

好了,更新是实现了,那有求知欲的小伙伴就会想查询怎么办呢?

这还不简单,一行语句就搞定了

var user = collection.Find(x => x.Name == "赵小明" && x.Relatives != null && x.Relatives.Count > 0 && x.Relatives.Exists(y => y.Name == "赵刚")).FirstOrDefault();

没错,但如果此人存在几千万个亲戚(现实生活中怎么可能,笑),我只需要其与一个名为“赵刚”的亲戚的关系,不想把整个对象都加载到内存中怎么办?

这时我们就需要用到ProjectionDefinitionBuilder对象了,

FilterDefinition<Persion> filter = Builders<Persion>.Filter.Where(x => x.Name == "赵小明" && x.Relatives != null && x.Relatives.Count > );

var findOptions = new FindOptions<Persion, Relative>()
{
Projection = new ProjectionDefinitionBuilder<Persion>().Expression(x => x.Relatives.FirstOrDefault(r => r.Name != "赵刚"))
}; Relative cursor = collection.FindSync(filter, findOptions).FirstOrDefault();

我们就得到了我们想要的亲戚对象,而不是包含几千万亲戚信息的完整Persion对象了。

结语

作为一名博客萌新,我只是将我遇到的问题总结下来并分享给大家,有不对的地方,务必帮忙指正。

当然上面的内容对于大佬来说可能是常规操作,但如果对你有一点点用处,请点赞,评论,并关注下。

后面我会将我在工作学习中遇到的有趣的问题分享给大家,谢谢!!!

这些MongoDB的隐藏操作你真的都掌握了吗?反正我是刚知道的更多相关文章

  1. MongoDB数据库简单操作

    之前学过的有mysql数据库,现在我们学习一种非关系型数据库 一.简介 MongoDB是一款强大.灵活.且易于扩展的通用型数据库 MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数 ...

  2. 【翻译】MongoDB指南/CRUD操作(二)

    [原文地址]https://docs.mongodb.com/manual/ MongoDB CRUD操作(二) 主要内容: 更新文档,删除文档,批量写操作,SQL与MongoDB映射图,读隔离(读关 ...

  3. 【翻译】MongoDB指南/CRUD操作(一)

    [原文地址]https://docs.mongodb.com/manual/ MongoDB CRUD操作(一) 主要内容:CRUD操作简介,插入文档,查询文档. CRUD操作包括创建.读取.更新和删 ...

  4. MongoDB的CRUD操作

    1. 前言 在上一篇文章中,我们介绍了MongoDB.现在,我们来看下如何在MongoDB中进行常规的CRUD操作.毕竟,作为一个存储系统,它的基本功能就是对数据进行增删改查操作. MongoDB中的 ...

  5. MongoDB各种查询操作详解

    这篇文章主要介绍了MongoDB各种查询操作详解,包括比较查询.关联查询.数组查询等,需要的朋友可以参考下   一.find操作 MongoDB中使用find来进行查询,通过指定find的第一个参数可 ...

  6. Javascript的DOM操作 - 你真的了解吗?

    摘要 想稍微系统的说说对于DOM的操作,把Javascript和jQuery常用操作DOM的内容归纳成思维导图方便阅读,同时加入性能上的一些问题. 前言 在前端开发的过程中,javascript极为重 ...

  7. mongodb学习03 操作详解

    插入文档 db.test.insert({"name":"jinks"}); 批量插入 db.test.insert([{}, {}, {}]); 一次批量插入 ...

  8. mongodb的常用操作

    对于nosql之前工作中有用到bekerlydb,最近开始了解mongodb,先简单写下mongodb的一些常用操作,当是个总结: 1.mongodb使用数据库(database)和集合(collec ...

  9. openerp 经典收藏 通过view实现字段的只读、隐藏操作(转载)

    通过view实现字段的只读.隐藏操作 原文地址:http://cn.openerp.cn/view_groups/ 在OpenERP V7视图(ir.ui.view)多了一个非常有用的字段(group ...

随机推荐

  1. JavaScript函数创建方式

    1.工厂模式 function createPerson(name, job) { var o = new Object() o.name = name o.job = job o.sayName = ...

  2. django框架基础-ORM进阶-长期维护

    ###############    ORM进阶---contenttype    ################ 设计思路: """ 路飞有两种课,专题课和学位课, ...

  3. C# 怎样判断 datagridview 中的checkbox列是否被选中

    private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e){ for (int i  ...

  4. Java IO: Buffered和Data

    作者:Jakob Jenkov  译者: 李璟(jlee381344197@gmail.com) 本小节会简要概括Java IO中Buffered和data的输入输出流,主要涉及以下4个类型的流:Bu ...

  5. Golang Middleware Part 1 · To Be A Better Man

    如何在Golang中实现中间件-Part 1 当使用net/http包实现服务的时候,一般使用的是如下的两中处理方式: http.HandleFunc http.Handle http.HandleF ...

  6. cas单点登录打包时下载慢!

    环境:win10 百度网盘下载 版本:  cas-overlay-template-5.3  提取码    d1b6 添加阿里的到pom.xml    注意添加到第一列 <!--阿里云仓库--& ...

  7. Docker For Mac 下安装 Rancher

    https://www.jianshu.com/p/5fb3e1a998d6 Docker For Mac 下安装 Rancher 原文:如何在 OS X 上安装 Rancher Rancher 是 ...

  8. 吴裕雄--天生自然 PHP开发学习:本地PHPSTORM在线连接、编辑、上传文件到虚拟机,并在本地浏览器运行(前提是虚拟机与本机已桥连成功)

  9. MyBatis之ResultMap的association和collection标签(一)

    1.先说resultMap比较容易混淆的点, 2. Map结尾是映射,Type是类型  resultType 和restltMap restulyType: 1.对应的是java对象中的属性,大小写不 ...

  10. TensorFlow_Faster_RCNN中demo.py的运行(CPU Only)

    GitHub项目地址,https://github.com/endernewton/tf-faster-rcnnTensorflow Faster RCNN for Object Detection. ...