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

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

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

  答案是可以,mongodb有很多聚合管道,使用不同的组合可以帮我们实现上述效果,由于mongodb的版本不同以及语句的复杂程度,写了好几版本,下面一一列出。

  第一版

  1. // 第一版:受限于下面的几个聚合管道,可以在3.4.4及以上使用
  2. // $addFields New in version 3.4.
  3. // $arrayToObject New in version 3.4.4.
  4. // $replaceRoot New in version 3.4.
  5. db.getCollection('FormInstace').aggregate([
  6. {
  7. $match: {
  8. "FormItems.key": { $ne: null }
  9. }
  10. },
  11. {
  12. $addFields: {
  13. FormValueObj: {
  14. $arrayToObject: {
  15. $map: {
  16. input: "$FormItems",
  17. as: "field",
  18. in: [ "$$field.key", "$$field.value" ]
  19. }
  20. }
  21. }
  22. }
  23. },
  24. {
  25. $addFields: {
  26. "FormValueObj._id": "$_id",
  27. "FormValueObj.ExtendData":"$ExtendData",
  28. "FormValueObj.CreateUserId": "$CreateUserId",
  29. "FormValueObj.CreateUserName": "$CreateUserName",
  30. "FormValueObj.CreateDate":"$CreateDate",
  31. "FormValueObj.LastModifyDate":"$LastModifyDate",
  32. "FormValueObj.FormId": "$FormId",
  33. "FormValueObj.FormVersion":"$FormVersion"
  34. }
  35. },
  36. {
  37. $replaceRoot: {
  38. newRoot: "$FormValueObj"
  39. }
  40. }
  41. ]);

  看一下执行结果(用的是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,便试了一下:

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

  第二版

  1. // 第二版:受限于下面的几个聚合管道,可以在3.4.4及以上使用
  2. // $addFields New in version 3.4.
  3. // $arrayToObject New in version 3.4.4.
  4. // $objectToArray New in version 3.4.4.
  5. // $concatArrays New in version 3.2.
  6. // $replaceRoot New in version 3.4.
  7. db.getCollection('FormInstace').aggregate([
  8. {
  9. $match: {
  10. "FormItems.key": { $ne: null }
  11. }
  12. },
  13. {
  14. $addFields: {
  15. TempFormValueObj: {
  16. $arrayToObject: {
  17. $map: {
  18. input: "$FormItems",
  19. as: "field",
  20. in: [ "$$field.key", "$$field.value" ]
  21. }
  22. }
  23. }
  24. }
  25. },
  26. {
  27. $addFields: {
  28. TempFormValues: {
  29. $objectToArray : "$TempFormValueObj"
  30. }
  31. }
  32. },
  33. {
  34. $addFields:{
  35. FormValueObj:{
  36. $arrayToObject:{
  37. $map:{
  38. input:{
  39. $concatArrays: [ "$TempFormValues", {$objectToArray : "$$ROOT"} ]
  40. },
  41. as: "kv",
  42. in: ["$$kv.k","$$kv.v"]
  43. }
  44. }
  45. }
  46. }
  47. },
  48. {
  49. $replaceRoot: {
  50. newRoot: "$FormValueObj"
  51. }
  52. },
  53. {
  54. $project: {
  55. FormItems:0,
  56. TempFormValueObj:0,
  57. TempFormValues:0
  58. }
  59. }
  60. ]);

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

  第三版

  1. // 第三版:受限于下面的几个聚合管道,可以在3.6及以上使用
  2. // $addFields New in version 3.4.
  3. // $arrayToObject New in version 3.4.4.
  4. // $replaceRoot New in version 3.4.
  5. // $mergeObjects New in version 3.6.
  6. db.getCollection('FormInstace').aggregate([
  7. {
  8. $match: {
  9. "FormItems.key": { $ne: null }
  10. }
  11. },
  12. {
  13. $addFields: {
  14. FormValueObj: {
  15. $arrayToObject: {
  16. $map: {
  17. input: "$FormItems",
  18. as: "field",
  19. in: [ "$$field.key", "$$field.value" ]
  20. }
  21. }
  22. }
  23. }
  24. },
  25. {
  26. $replaceRoot: { newRoot: { $mergeObjects: [ "$FormValueObj", "$$ROOT" ] } }
  27. },
  28. {
  29. $project: {
  30. FormItems:0,
  31. FormValueObj:0
  32. }
  33. }
  34. ]);

  这一版代码最精简,但是依赖的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/)

  1. db.createView("FormInstaceView","FormInstace",[
  2. {
  3. $match: {
  4. "FormItems.key": { $ne: null }
  5. }
  6. },
  7. {
  8. $addFields: {
  9. FormValueObj: {
  10. $arrayToObject: {
  11. $map: {
  12. input: "$FormItems",
  13. as: "field",
  14. in: [ "$$field.key", "$$field.value" ]
  15. }
  16. }
  17. }
  18. }
  19. },
  20. {
  21. $replaceRoot: { newRoot: { $mergeObjects: [ "$FormValueObj", "$$ROOT" ] } }
  22. },
  23. {
  24. $project: {
  25. FormItems:0,
  26. FormValueObj:0
  27. }
  28. }
  29. ]);

  之后你就可以向下面一样查询了

  结果和上面的没有什么区别,但是你不要将视图当作mongodb中的collection使用,这样是查不到数据的

  1. db.getCollection('FormInstaceView').find({FormId:"507048044944692000","1572493552001.id":1})

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

表单生成器(Form Builder)之mongodb表单数据——整理数据的更多相关文章

  1. 表单生成器(Form Builder)之mongodb表单数据查询——统计查询求和

    上一篇笔记仅是记录了一下简单的关联查询,根据笔记中的场景:将某一车辆关联的耗损记录全部放在了一个字段当中.不知道现在中有没有这种场景,我们的应用中没有类似的场景,可能我们更关注的是某车辆的总耗损金额和 ...

  2. 表单生成器(Form Builder)之mongodb表单数据查询——关联查询

    这一篇接着记录一下查询相关的操作.想象一下,如果想要在一张表格中展示某些车辆的耗损和营收情况,我们该怎么处理.车辆.耗损.营收各自存储在一张表中,耗损和营收中冗余了车辆信息……我们便想到了关联查询.m ...

  3. 表单生成器(Form Builder)之mongodb表单数据查询——返回分页数据和总条数

    上一篇笔记将开始定义的存储结构处理了一下,将FormItems数组中的表单项都拿到mongodb document的最外层,和以前的关系型数据类似,之不过好多列都是动态的,不固定,不过这并没有什么影响 ...

  4. 表单生成器(Form Builder)之伪造表单数据番外篇——随机车辆牌照

    前几天记录了一下表单生成器(Form Builder)之表单数据存储结构mongodb篇,之后便想着伪造一些数据.为什么要伪造数据呢?说来惭愧,因为拖拉拽设计表单以及表单对应的列表的PC端和移动端该显 ...

  5. 表单生成器(Form Builder)之伪造表单数据mongodb篇

    这篇文章终于回到了正轨:为mongodb伪造数据.之前的随机数.随机车牌照.随机时间还有这篇笔记中的获取指定长度的中文字符串,都是为这篇笔记做准备.看一下我们的准备(基础代码) // 1.获取指定范围 ...

  6. 表单生成器(Form Builder)之伪造表单数据番外篇——指定范围随机时间

    为了伪造一些尽量真实的假数据,也真是够费劲的.上一篇笔记记录了一下获取一个随机车辆牌照,这篇笔记记录一下怎么获取一个随机时间.这篇就不说那么多废话了,直接上代码 // 获取指定范围的随机数 var g ...

  7. 表单生成器(Form Builder)之表单数据存储结构mongodb篇

    从这篇笔记开始,记录一下表单生成器(Form Builder)相关的一些东西,网上关于他的介绍有很多,这里就不解释了. 开篇说一下如何存储Form Builder生成的数据.

  8. Ext JS4 学习笔记之发送表单(Form)时也将表单下的表格(Grid)数据一同发送的方法

    Ext JS4 学习笔记之发送表单(Form)时也将表单下的表格(Grid)数据一同发送的方法 昨天在开发的时候遇到个小问题,就是如何将Grid的内容与Form一起发送到服务器端.默认情况下,表单(F ...

  9. 表单组件 form fastadmin(生成表单元素)

    Form组件 定义文件位置: /extend/fast/Formphp 通用参数 $name 通常为我们组件的名称(name属性值),我们在后台接收时可以通过这个名称来获取到它所对应的值 $value ...

随机推荐

  1. ARTS-S k8s常用命令

    本地访问minikube的docker eval $(minikube docker-env) 删除statefulset kubectl delete statefulset web --casca ...

  2. Python3 网络编程基础1

    目录 开发架构 C/S架构 B/S架构 OSI模型 应用层 表示层 会话层 传输层 网络层 数据链路层 物理层 TCP协议 socket 开发架构 C/S架构 client 和 server, 既客户 ...

  3. Docker--DockerFile创建自己的image

    echo编辑整理,欢迎转载,转载请声明文章来源.欢迎添加echo微信(微信号:t2421499075)交流学习. 百战不败,依不自称常胜,百败不颓,依能奋力前行.--这才是真正的堪称强大!!! 在我们 ...

  4. 《Java基础知识》Java技术总结

    1. Java 知识点总结 Java标示符.保留字和数制:https://www.cnblogs.com/jssj/p/11114041.html Java数据类型以及变量的定义:https://ww ...

  5. 聊一聊看似简单的Promise.prototype.then()方法

    Promise.prototype.then() Proise实例的then方法是定义在原型对象Promise.prototype上的,它的作用是为Promise实例添加状态改变时的回调函数. 该方法 ...

  6. NPOI 设置下拉列表

    HSSFWorkbook workbook = new HSSFWorkbook();//创建工作簿 ISheet sheet = workbook.CreateSheet();//创建sheet页 ...

  7. React每隔0.2s颜色变淡 之生命周期 ,componentDidMount表示组件已经挂载

    05案例 每隔0.2s颜色变淡 componentDidMount表示组件已经挂载,可以进行DOM操作 import React, { Component } from "react&quo ...

  8. 如何使用TG Pro for Mac的自定义控制功能完全覆盖系统

    在某些情况下,可能需要完全覆盖系统风扇控制并使用自定义算法.通过将Auto Boost规则的强大功能与覆盖系统功能相结合,可以使用TG Pro.请记住,当风扇模式设置为Auto Boost时,这将完全 ...

  9. 【30天自制操作系统】day01:内存分布图

  10. WSL2(预览版)体验笔记

    WSL2安装 WSL2在今年5月份Microsoft Build大会上发布了,但至今Windows10一直没收到更新推送,我想这么久过去就算没进入正式,至少也到了RC版了吧,于是开始折腾准备体验一把. ...