• 表单生成器(Form Builder)之mongodb表单数据——整理数据


      在上篇笔记中,为车辆信息表、车辆耗损表以及车辆营收表插入了一些数据。之后便是查询了,重点也在查询……按照之前定好的数据结构,如果查询mongodb document的最外层比较简单,但是我们的重点应该是FormItems表单项集合中的表单项,对他们进行查询、统计等。针对插入的数据,我写了一个查询:查询车辆信息表中主键ID为1的车辆的所有耗损记录。分析一下,因为所有的表(上面的三张表都放在FormInstace表中)都在一个真实的表中,所以我们首先要确定FormId,之后是怎么确定是哪辆车,车辆耗损表中有一个字段冗余着车辆信息(不清楚的可以翻一下前面的笔记),那么查询语句就出来了:

    db.getCollection('FormInstace').find({FormId:'507048044944692000','FormItems':{$elemMatch:{'key':'1572493552001','value.id':"1"}}})

      这个查询挺简单的,确定了表之后就是一个等值查询,但是现在却因为我们的数据存储结构用到了mongodb中的$elemMatch。这还是最简单的查询,之后要是有复杂的查询统计更是不敢想象(事实上也确实如此:现在的项目中对表单中的字段进行查询统计非常困难)……那么我们能不能将FormItems表单项集合中的字段拿到document的最外层,和之前的关系型数据一样,如下图:

      答案是可以,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,便试了一下:

      至此我觉得语句应该没问题。最后的数据列是合并了所有表(逻辑上)中的所有字段和公共字段。

      第二版

    //    第二版:受限于下面的几个聚合管道,可以在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
            }
        }
    ]);

      在上一版中手动拼接了所有的公共字段,不是特别方便,这一版本中去掉了

      第三版

    //    第三版:受限于下面的几个聚合管道,可以在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条。这里你可能会发现几个问题:

      1、查询表单中的表单项都是硬编码的数字(例如“1572493552001”),可读性太差了;

      2、查询时间有点长(图片中有执行时间);

      3、写一个简单的查询,这代码也太多了

      ……

      对上面的问题,解释一下

      1、在实际的界面中的筛选字段都是拖拉拽设计的,用户是感知不到的,“1572493552001”只是一个桥梁

      2、查询全部的时间是有点长,好吧!不是有点是很长……如果业务需求需要获取所有的数据时,就不要用这种方式了,麻烦你换一种方式吧!我想说的是实际的场景中都是分页加载,这时时间还是可以接受的,下面是获取30条的截图,时间0.114s

      3、代码有点多的问题,其实你可以将上面整理数据的代码作为筛选的前置条件,只要是查询都添加这段代码;还有一种处理方式就是先创建一个视图(mongodb 3.4+支持视图,https://docs.mongodb.com/manual/core/views/)

    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})

      这篇笔记还是够长的,一大推代码……就到这里了。

  • 相关阅读:
    反转链表
    《Java JDK7 学习笔记》课后练习题1
    《Java JDK7 学习笔记》课后练习题2
    《java JDK7 学习笔记》课后练习题3
    SQL与NoSQL(关系型与非关系型)数据库的区别
    编程中编码的来源及发展
    JDK7学习笔记之基础类型
    《java JDK7学习笔记》之跨平台与路径设置
    《java jdk7学习笔记》之java三大平台
    VS2015安装之后加装SQL SERVER2014的步骤
  • 原文地址:https://www.cnblogs.com/du-blog/p/11820110.html
Copyright © 2020-2023  润新知