在上篇笔记中,为车辆信息表、车辆耗损表以及车辆营收表插入了一些数据。以后即是查询了,重点也在查询……按照以前定好的数据结构,若是是查询mongodb document的最外层比较简单,可是咱们的重点应该是FormItems表单项集合中的表单项,对他们进行查询、统计等。根据插入的数据,我写了一个查询:查询车辆信息中主键ID为1的车辆的全部耗损记录。分析一下由于全部的表(上面的三张表都放在FormInstace中)都在一个真实的表中,因此咱们首先要肯定FormId,以后是怎么肯定是那辆车,车辆耗损表中有一个字段冗余着车辆信息(不清楚的能够翻一下前面的笔记),那么查询语句就出来了:git
db.getCollection('FormInstace').find({FormId:'507048044944692000','FormItems':{$elemMatch:{'key':'1572493552001','value.id':"1"}}})
其实原本这个查询挺简单的,肯定了表以后就是一个等值查询,可是如今却由于咱们的结构问题用到了mongodb中的$elemMatch。这仍是最简单的查询,以后要是有复杂的查询统计更是不敢想象(事实上也确实如此:如今的项目中对表单中的字典进行查询统计很是困难)……那么咱们能不能将FormItems表单项集合中的字段拿到document的最外层,和以前的关系型数据同样,以下图:github
答案是能够,mongodb有不少聚合管道,使用不一样的组合能够帮咱们实现上述效果,因为mongodb的版本不一样以及语句的复杂程度,写了好几版本,下面一一列出。mongodb
初版数据结构
// 初版:受限于下面的几个聚合管道,能够在3.4.4及以上使用 // $addFields New in version 3.4. // $arrayToObject New in version 3.4.4. // $replaceRoot New in version 3.4. db.getCollection('FormInstace').aggregate([ { $match: { "FormItems.key": { $ne: null } } }, { $addFields: { FormValueObj: { $arrayToObject: { $map: { input: "$FormItems", as: "field", in: [ "$$field.key", "$$field.value" ] } } } } }, { $addFields: { "FormValueObj._id": "$_id", "FormValueObj.ExtendData":"$ExtendData", "FormValueObj.CreateUserId": "$CreateUserId", "FormValueObj.CreateUserName": "$CreateUserName", "FormValueObj.CreateDate":"$CreateDate", "FormValueObj.LastModifyDate":"$LastModifyDate", "FormValueObj.FormId": "$FormId", "FormValueObj.FormVersion":"$FormVersion" } }, { $replaceRoot: { newRoot: "$FormValueObj" } } ]);
看一下执行结果(用的是Robo 3T 1.2.1)测试
看到这样的执行结果仍是很满意的,这不正是咱们想要的嘛!可是当我调整上图中的数值时(修改成500),却报错了(⊙o⊙)…编码
我去这是什么状况,我都怀疑个人语句写的有问题了,但是为何第一次没问题-_-||,我在另外一台电脑中测试时(操做方式相同,只是版本不一样,版本是1.3),还出现过“Cannot convert to an aggregation if ntoreturn is set”这样的错误,网上查到了这篇文章https://github.com/Studio3T/robomongo/issues/1529 (这里只是记录一下,有时间再看吧)。我还有另外一个GUI,便试了一下:spa
至此我以为语句应该没问题,最后的数据列是合并了全部表(逻辑上)中的全部字段和公共字段。设计
第二版3d
// 第二版:受限于下面的几个聚合管道,能够在3.4.4及以上使用 // $addFields New in version 3.4. // $arrayToObject New in version 3.4.4. // $objectToArray New in version 3.4.4. // $concatArrays New in version 3.2. // $replaceRoot New in version 3.4. db.getCollection('FormInstace').aggregate([ { $match: { "FormItems.key": { $ne: null } } }, { $addFields: { TempFormValueObj: { $arrayToObject: { $map: { input: "$FormItems", as: "field", in: [ "$$field.key", "$$field.value" ] } } } } }, { $addFields: { TempFormValues: { $objectToArray : "$TempFormValueObj" } } }, { $addFields:{ FormValueObj:{ $arrayToObject:{ $map:{ input:{ $concatArrays: [ "$TempFormValues", {$objectToArray : "$$ROOT"} ] }, as: "kv", in: ["$$kv.k","$$kv.v"] } } } } }, { $replaceRoot: { newRoot: "$FormValueObj" } }, { $project: { FormItems:0, TempFormValueObj:0, TempFormValues:0 } } ]);
在上一版中手动拼接了全部的公共字段,不是特别方便,这一版本中去掉了code
第三版
// 第三版:受限于下面的几个聚合管道,能够在3.6及以上使用 // $addFields New in version 3.4. // $arrayToObject New in version 3.4.4. // $replaceRoot New in version 3.4. // $mergeObjects New in version 3.6. db.getCollection('FormInstace').aggregate([ { $match: { "FormItems.key": { $ne: null } } }, { $addFields: { FormValueObj: { $arrayToObject: { $map: { input: "$FormItems", as: "field", in: [ "$$field.key", "$$field.value" ] } } } } }, { $replaceRoot: { newRoot: { $mergeObjects: [ "$FormValueObj", "$$ROOT" ] } } }, { $project: { FormItems:0, FormValueObj:0 } } ]);
这一版代码最精简,可是依赖的mongodb的版本比较高,要3.6+
如今咱们整理完了数据,再看一下上面的查询,对比一下变化
就是在最后添加一个筛选管道,结果和最上面的结果是同样的都是58条。这里你可能会发现几个问题:
一、查询表单中的表单项都是硬编码的数字(例如“1572493552001”),可读性太差了;
二、查询时间有点长(图片中有执行时间);
三、写一个简单的查询,这代码也太多了
……
对上面的问题,解释一下
一、在实际的界面中的筛选字段都是拖拉拽设计的,用户是感知不到的,“1572493552001”只是一个桥梁
二、查询所有的时间是有点长,好吧!不是有点是很长……若是业务需求须要获取全部的数据时,就不要用这种方式了,麻烦你换一种方式吧!我想说的是实际的场景中都是分页加载,这时时间仍是能够接受的,下面是获取30条的截图,时间0.114s
三、代码有点多的问题,其实你能够将上面整理数据的代码做为筛选的前置条件,只要是查询都添加这段代码;还有一种处理方式就是先建立一个视图
db.createView("FormInstaceView","FormInstace",[ { $match: { "FormItems.key": { $ne: null } } }, { $addFields: { FormValueObj: { $arrayToObject: { $map: { input: "$FormItems", as: "field", in: [ "$$field.key", "$$field.value" ] } } } } }, { $replaceRoot: { newRoot: { $mergeObjects: [ "$FormValueObj", "$$ROOT" ] } } }, { $project: { FormItems:0, FormValueObj:0 } } ]);
以后你就能够向下面同样查询了
结果和上面的没有什么区别,可是你不要将视图看成mongodb中的collection使用,这样是查不到数据的
db.getCollection('FormInstaceView').find({FormId:"507048044944692000","1572493552001.id":1})
这篇笔记仍是够长的,一大推代码……就到这里了。