文章详情

短信预约信息系统项目管理师 报名、考试、查分时间动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

这些MongoDB的隐藏操作你真的都掌握了吗?反正我是刚知道

2019-02-08 23:06

关注

这些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 update = Builders.Update.Set(y => y.Name, "B");
collection.UpdateOne(x => x.Id == "user1", update);

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

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

重点

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

/// 
////// 
[BsonIgnoreExtraElements]
public class Persion : BaseEntity
{
    /// 
    /// 名称
    /// 
    [BsonElement("name")]
    public string Name { get; set; }

    /// 
    /// 年龄
    /// 
    [BsonElement("age")]
    public int Age { get; set; }

    /// 
    /// 亲戚
    /// 
    [BsonElement("relatives")]
    public List Relatives { get; set; }

}
/// 
/// 亲属
/// 
public class Relative
{
    /// 
    /// 名称
    /// 
    [BsonElement("name")]
    public string Name { get; set; }

    /// 
    /// 与本人关系
    /// 
    [BsonElement("relationship")]
    public Relationship Relationship { get; set; }
}
/// 
/// 与本人关系
/// 
public enum Relationship
{
    /// 
    /// 爸爸
    /// 
    father = 0,
    /// 
    /// 妈妈
    /// 
    mother = 1,
    /// 
    /// 儿子
    /// 
    son = 2,
    /// 
    /// 女儿
    /// 
    daughter = 3,
    /// 
    /// 不明
    /// 
    unknow = 100
}

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

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

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

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

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

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

var option = new UpdateOptions()
{
    ArrayFilters = new List {
        new JsonArrayFilterDefinition("{"i.name": "赵刚"}")
    }
};

collection.UpdateMany(filter, update, option);

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

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

再生成一个决定$[i]值的JsonArrayFilterDefinition("{"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 filter = Builders.Filter.Where(x => x.Name == "赵小明" && x.Relatives != null && x.Relatives.Count > 0);

var findOptions = new FindOptions()
{
    Projection = new ProjectionDefinitionBuilder().Expression(x => x.Relatives.FirstOrDefault(r => r.Name != "赵刚"))
};

Relative cursor = collection.FindSync(filter, findOptions).FirstOrDefault();

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

结语

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

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

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

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-数据库
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯