这些MongoDB的隐藏操作你真的都掌握了吗?反正我是刚知道
背景
最近公司系统还原用户时偶尔会出现部分用户信息未还原成功的问题,作为开发人员,最头疼的不是代码存在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的隐藏操作你真的都掌握了吗?反正我是刚知道的更多相关文章
- MongoDB数据库简单操作
之前学过的有mysql数据库,现在我们学习一种非关系型数据库 一.简介 MongoDB是一款强大.灵活.且易于扩展的通用型数据库 MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数 ...
- 【翻译】MongoDB指南/CRUD操作(二)
[原文地址]https://docs.mongodb.com/manual/ MongoDB CRUD操作(二) 主要内容: 更新文档,删除文档,批量写操作,SQL与MongoDB映射图,读隔离(读关 ...
- 【翻译】MongoDB指南/CRUD操作(一)
[原文地址]https://docs.mongodb.com/manual/ MongoDB CRUD操作(一) 主要内容:CRUD操作简介,插入文档,查询文档. CRUD操作包括创建.读取.更新和删 ...
- MongoDB的CRUD操作
1. 前言 在上一篇文章中,我们介绍了MongoDB.现在,我们来看下如何在MongoDB中进行常规的CRUD操作.毕竟,作为一个存储系统,它的基本功能就是对数据进行增删改查操作. MongoDB中的 ...
- MongoDB各种查询操作详解
这篇文章主要介绍了MongoDB各种查询操作详解,包括比较查询.关联查询.数组查询等,需要的朋友可以参考下 一.find操作 MongoDB中使用find来进行查询,通过指定find的第一个参数可 ...
- Javascript的DOM操作 - 你真的了解吗?
摘要 想稍微系统的说说对于DOM的操作,把Javascript和jQuery常用操作DOM的内容归纳成思维导图方便阅读,同时加入性能上的一些问题. 前言 在前端开发的过程中,javascript极为重 ...
- mongodb学习03 操作详解
插入文档 db.test.insert({"name":"jinks"}); 批量插入 db.test.insert([{}, {}, {}]); 一次批量插入 ...
- mongodb的常用操作
对于nosql之前工作中有用到bekerlydb,最近开始了解mongodb,先简单写下mongodb的一些常用操作,当是个总结: 1.mongodb使用数据库(database)和集合(collec ...
- openerp 经典收藏 通过view实现字段的只读、隐藏操作(转载)
通过view实现字段的只读.隐藏操作 原文地址:http://cn.openerp.cn/view_groups/ 在OpenERP V7视图(ir.ui.view)多了一个非常有用的字段(group ...
随机推荐
- python语法基础-面向对象-进阶-长期维护
############### @property定义属性 ############## # 所以对于定义属性你有好几种方式了和种类了,# 静态属性,动态属性, # property # ...
- Windows 10操作系统针对不同环境下的安装方法
一.电脑系统能正常运行 1.解压win10镜像文件 到电脑的非系统分区,运行setup安装文件 2.点击setup应用程序,准备安装 3.准备安装 4.等待安装过程结束,重启即可. 二.光盘安装 1. ...
- [LC] 66. Plus One
Given a non-empty array of digits representing a non-negative integer, plus one to the integer. The ...
- python3下应用pymysql(第二卷)
上一卷讲述的是单条插入数据,现在要多条插入数据: 随意定义了一批数据 去数据库查询一下: 下面试一下查询语句: 获取游标里的数据,结果如下: 下面更改下返回数据类型,如果想用字典类型: 结果如下: 在 ...
- caffe之那些依赖的库
1. Boost库 Boost是一个可移植的,提供源代码的C++库,作为标准库的后备,是C++标准化进程的开发引擎之一.Boost强调对跨平台的支持,编译与平台无关.Caffe采用C++为主要开发语言 ...
- java 通过数据库名获得 该数据所有的表名以及字段名、字段类型
package com.nf.lc.sql_meta_data; import java.sql.*; import java.util.HashMap; import java.util.Map; ...
- 编程语言十万个为什么之java web的基础概念
1.什么是JAVA Java是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由SunMicrosystems公司于1995年5月推出的Java程序设计语言和Java平台(即JavaSE, Ja ...
- jquery关于checkbox复选框是否被选中的问题
本人在项目中需要用到,判断哪些复选框被用户选中.自然而然想到用 if($('').attr('checked') == true) 但是不管有没有选,$('').attr('checked')返回的都 ...
- ansible使用指北(二)
前言在上一篇文章里我们了解了ansible的常用模块,今天我们来了解下ansible-playbook,ansbile-playbook是一系统ansible命令的集合,其利用yaml 语言编写,an ...
- Jenkins+Git+Fastlane+Fir CI集成
上一篇有讲关于fastlane自动化部署,本篇将会着重讲关于fastlane的实际应用. 目标: 利用自动化jenkins打包工具,自动拉取git仓库代码 不需要通过手动检查修改xcode中项目配置修 ...