去年三月底入职上海的一家互联网公司,由于项目使用的是MongoDB数据库所以有机会接触了MongoDB。在项目的开发过程中使用系统原有的一些方法查询MongoDB感觉很费力,用起来也不爽,所以私下里就自己学了一些C#查询MongoDB的方法。

先说一些MongoDB的内嵌数组查询,公司原有的方法是使用Map进行聚合,这导致查询效率底下。多个项目组的成员都有抱怨。

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

在研究MongoDB测试用例后总结出使用投影技术来替换Map的方法:

public class Test
{

       public ObjectId Id{get;set;}        
       public string AAA{get;set;}
        public IList<TestOne> TestOneList{get;set;}

}

public class TestOne
{

     public long TestOneId{get;set;}
public string Name{get;set;}
public IList<TestTwo> TestTwoList{get;set;} }

public class TestTwo
{
public long TestTwoId{get;set;}
}

假定有这么一个数据结构 我想查询指定的TestOne 我们就可以利用投影技术来实现

var projection = new ProjectionDefinitionBuilder<Test>();
var pp = projection.Expression(r =>
                r.TestOneList.FirstOrDefault(s => s.TestOneId == 123));
 
dbContext.Get(filter, new FindOptions<Test, TestOne>() { Projection = pp });

这样我们就可以直接拿到指定的TestOne 了。同样对于多层嵌套 我们可以在Expression里一层一层往下写 

var pp = projection.Expression(r =>
                r.TestOneList.FirstOrDefault(s => s.TestOneId == 123).TestTwoList.FirstOrDefault(t=>t.TestTwoId==333));
dbContext.Get(filter, new FindOptions<Test, TestTwo>() { Projection = pp });

在公司的测试服务器上相较map方式性能提升近10倍 。

在MongoDB中我们常常会面临一个问题,一个文档的数据量过大,譬如 我们TestTwoList里的数据量达到万级别,这个时候我们查询一个Test数据的耗时肯定会非常的大,即便是我们放到Redis等缓存中依然会很慢。那么有什么方法能够优化呢?

在实际使用过程中我们的各个业务不同对于Test里的数据需要也不同,比如业务A只需要Test中的TestOne 的Name信息,业务B只需要Test中的AAA信息。那么我们可以根据实际需要来进行投影以获取我们实际需要的数据,而不是获取所有的数据。

var rr = Builders<Test>.Filter.Eq(r=>r.Id, 1123123);
            var project =new ProjectionDefinitionBuilder<Test>();
            var pp = project.Include("Name");
            var ss = dbContext.Get(rr, new FindOptions<Test, 自定义类型>()
            {
                Projection = pp
            });

这样我们就可以只拿我们需要的数据。

Linq 查询MongoDB。我们拿到Query后可以使用Linq进行各种查询,group 等。非常实用。

最后呢推荐一种 多层嵌套数据操作方法,在多层数组嵌套中最难搞得就是定位的问题。好在MongoDB官方在3.6版本提供了ArrayFilters ,极大的方便了开发人员

var filterBuiler = Builders<Test>.Filter;
           
            var filter = filterBuiler.Eq(r => r.Id, 598882505);

            var ArrayFilters = new List<ArrayFilterDefinition>();
            ArrayFilters.Add(new JsonArrayFilterDefinition<Test>("{\"i.TestOneId\":{$eq:"+ 598882571 + "}}"));
            ArrayFilters.Add(new JsonArrayFilterDefinition<Test>("{\"j.TestTwoId\":{$eq:" + 598882573 + "}}"));

            UpdateOptions updateOptions=new UpdateOptions();
            updateOptions.ArrayFilters = ArrayFilters;
            var update = new UpdateDefinitionBuilder<Test>();
            var updatef = update.Set("TestOneList.$[i].TestTwoList.$[j].xxxxx", "哈哈");
           await dbContext.UpdateAsync(filter, updatef, updateOptions);

这种方法只被3.6版本的数据库支持

使用MongoDB的同仁一定要记得 MongoDB中对于整数,长整数,双精度浮点数有一个Inc操作!!!!!

吐槽一下:不知道为什么总能遇到一些拥有迷之自信的程序员? 不知道各位同仁有没有遇到过拥有迷之自信的程序员?

 

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄