背景

最近公司系统还原用户时偶尔会出现部分用户信息未还原成功的问题,作为开发人员,最头疼的不是代码存在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. Windows 10操作系统针对不同环境下的安装方法

    一.电脑系统能正常运行 1.解压win10镜像文件 到电脑的非系统分区,运行setup安装文件 2.点击setup应用程序,准备安装 3.准备安装 4.等待安装过程结束,重启即可. 二.光盘安装 1. ...

  2. [LC] 82. Remove Duplicates from Sorted List II

    Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numb ...

  3. kafka运行找不到或无法加载主类 Files\Java\jdk1.8.0_131\lib\dt.jar;C:\Program

    最近在研究Flink+kafka解决方案, kafka的安装首先需要安装zookeeper,在安装zookeeper是报错找不到或无法加载主类 Files\Java\jdk1.8.0_131\lib\ ...

  4. 解决centos6系统上python3—flask模块的安装问题

    Flask 是一个使用 Python 编写的轻量级 Web 框架(所以我们前面花了那么多时间安装 Python3 呀).它被称为微型架构,因为其使用非常简单的核心以及功能丰富的扩展.虽然 Flask ...

  5. <JZOJ5943>树

    一开始t了五个点我就一脸懵逼 然后 发现高级操作... 就是那个tor的数组2333 可以让一些不需要改的不再去改啦 位运算果然是神奇的东西XD 魔性哈哈哈 #include<cstdio> ...

  6. mysql-5.7.14-winx64解压版配置

    1.下载最新的MySQL文件并且解压 我的位置是 F:\mysql-5.7.14-winx64 2.F:\mysql-5.7.14-winx64\bin; 添加到环境变量-系统变量-PATH下 3.复 ...

  7. [VUE]关于路由哪些事儿

    什么是路由 之前有个小伙伴面试被问到:面试官:不用vue能不能写单页面应用?答:用angular啊(咳咳,开个玩笑),答案确实是可以的,原生js中有个事件叫做onhashchange,可以在windo ...

  8. AI能帮我们造出一个无肉的世界吗?

    AI听起来很遥远,其实已经渗透到我们的日常工作和生活中.在不远的未来,互联网.大数据.硬件的发展和软件的优化,乃至全社会的参与,人工智能将真正从实验室走进生活,它将成为改变我们生活的一部分.我们吃的肉 ...

  9. API网关Kong

    官网:https://konghq.com/ 各种方式安装汇总:https://konghq.com/install/ 命令列表:https://docs.konghq.com/0.14.x/admi ...

  10. Yuur persistent XSS

    XSS发生在评论处/帖子正文处 index.php:37-38行 $sql="insert into topic set tid='$tid',title='$title',nickname ...