MongoDB 在数组中创建数组

Posted

技术标签:

【中文标题】MongoDB 在数组中创建数组【英文标题】:MongoDB create an array within an array 【发布时间】:2021-12-17 07:13:53 【问题描述】:

我正在努力了解 MongoDB 以及聚合和组。到目前为止,我已经花了大约 3 天时间。

我的源数据看起来像...


   "formName" : "my form",
   "updatedAt" : "2021-11-02T13:29:00.123Z",
,

   "formName" : "another form",
   "lastUpdated" : "2021-10-01T13:29:00.123123",
,

请注意,可能有不同的日期名称,尽管这些是唯一的区别。

我正在尝试实现...的输出


    "_id": null,
    "text": "my form",  (NOTE: This is the formName)
    "children": [
       "text" : 2021, (This is the year part of the updated)
       "children" : [
          "text" : 1, (These are the month part of the updated)
          "text" : 2,
          "text" : 3,
          "text" : 4
       ]
    ,
    ]

所以,基本上是一棵树,它有 formName,子分支是年,子分支是月。

各种方法我都试过了,很多都不行,比如嵌套在$groups里面的$addToSet。

我已经接近了,但我解决不了。

这是最接近的,但这不起作用。

db.FormsStore.aggregate( [
  
  $match:myKey:"a guid to group my forms together",
  $project: formName:1, lastUpdated:1, updatedAt:1,
   
    $group:  
      _id:    formName: "$formName" , 
      Year: $addToSet: $year: $dateFromString:  dateString: "$lastUpdated" ,
      Month: $addToSet: $month: $dateFromString:  dateString: "$lastUpdated" ,
     
  , 
   
    $group: 
      _id:  formName: "$_id.formName" , 
     Time: $addToSet: year: "$Year", month: "$Month"
     
   
]
)

输出显示...

 
    _id:  formName: 'One of my forms' ,
    Time: [
      
        year: [ 2021 ],
        month: [ 10, 11 ] 
      
    ]
 

这将全部用在 C# 中

非常感谢您的帮助。

【问题讨论】:

【参考方案1】:

查询

以日期格式添加一个新字段“日期” 首先将更具体的(formname+year) 分组以将月份放入数组中 然后是不太具体的(formname) 将年份放入数组中

Test code here

aggregate(
["$set": 
    "date": 
      "$cond": 
        ["$updatedAt", "$dateFromString": "dateString": "$updatedAt",
          "$dateFromString": "dateString": "$lastUpdated"],
      "updatedAt": "$$REMOVE",
      "lastUpdated": "$$REMOVE",
  "$group": 
    "_id": "text": "$formName", "year": "$year": "$date",
      "children": "$push": "text": "$month": "$date",
  "$group": 
    "_id": "$_id.text",
      "children": 
      "$push": "text": "$_id.year", "children": "$children",
  "$set": "text": "$_id", "_id": "$$REMOVE"])

编辑

下面也按年/月排序,并且每个 formName 只保留唯一的年/月。

不同之处在于按 formName,year,month 分组来取唯一(第一个 aacumulator 将只取所有 3 中相同的一个) replace-root(将第一个文档设为 ROOT 文档) 然后按这3个字段排序(降年,升月) 群 按 2 个字段排序 最后一组

Test code here *mongoplaygroung 丢失了字段的顺序,请在您的驱动程序上运行它以确保

aggregate(
["$set": 
    "date": 
      "$cond": 
        ["$updatedAt", "$dateFromString": "dateString": "$updatedAt",
          "$dateFromString": "dateString": "$lastUpdated"],
      "updatedAt": "$$REMOVE",
      "lastUpdated": "$$REMOVE",
  "$set": "year": "$year": "$date", "month": "$month": "$date",
  "$group": 
    "_id": "formName": "$formName", "year": "$year", "month": "$month",
      "doc": "$first": "$$ROOT",
  "$replaceRoot": "newRoot": "$doc",
  "$sort": "formName": 1, "year": -1, "month": 1,
  "$group": 
    "_id": "text": "$formName", "year": "$year",
      "children": "$push": "text": "$month",
  "$sort": "_id.text": 1, "_id.year": -1,
  "$group": 
    "_id": "$_id.text",
      "children": 
      "$push": "text": "$_id.year", "children": "$children",
  "$set": "text": "$_id", "_id": "$$REMOVE"])

有数据

[
  
    "formName": "my form",
    "updatedAt": "2021-11-02T23:30:15.123Z"
  ,
  
    "formName": "my form",
    "updatedAt": "2021-10-02T23:30:15.123Z"
  ,
  
    "formName": "my form",
    "updatedAt": "2020-06-02T23:30:15.123Z"
  ,
  
    "formName": "my form",
    "updatedAt": "2020-07-02T23:30:15.123Z"
  ,
  
    "formName": "another form",
    "updatedAt": "2021-10-01T23:30:15.123Z"
  ,
  
    "formName": "another form",
    "updatedAt": "2021-10-01T23:30:15.123Z"
  ,
  
    "formName": "another form",
    "updatedAt": "2021-09-01T23:30:15.123Z"
  ,
  
    "formName": "another form",
    "updatedAt": "2021-08-01T23:30:15.123Z"
  ,
  
    "formName": "another form",
    "updatedAt": "2020-10-01T23:30:15.123Z"
  
]

我得到了结果

[
  "children": [
    
      "text": 2021,
      "children": [
        
          "text": 10
        ,
        
          "text": 11
        
      ]
    ,
    
      "text": 2020,
      "children": [
        
          "text": 6
        ,
        
          "text": 7
        
      ]
    
  ],
  "text": "my form"
,

  "children": [
    
      "text": 2021,
      "children": [
        
          "text": 8
        ,
        
          "text": 9
        ,
        
          "text": 10
        
      ]
    ,
    
      "text": 2020,
      "children": [
        
          "text": 10
        
      ]
    
  ],
  "text": "another form"
]

【讨论】:

虽然我打开了它,但我自己仍在研究它......并且几乎已经破解它......通过管道进入新的$group,但我对你的解决方案非常感兴趣。我刚刚尝试在本地运行你的并得到一个错误,无法从 BSON 类型字符串转换为日期。我认为问题在于测试代码的链接,您已经设置了 ISODate,但我的数据是字符串(因此是我的 $dateFromString)。每组中的数据将更新At 或 lastUpdated,而不是两者。 好的,我更新了答案,但通常将日期保持为 Dates 更快,您可以使用$dateToString 谢谢。这几乎可以工作,但是当我使用真实数据时,给了我重复的几个月。至于日期,它们来自外部来源并直接导入 MongoDB。现在,我自己的也不太正确,但我也把它作为答案,所以你可以看到我在哪里...... 我刚刚将 $push 更新为 $addToSet,这样更好。 (我是 MongoDB 新手,所以阅读这些新指令并看到 $push 和 $addToSet 相似,但 $addToSet 保持唯一值)我现在尝试按年/月排序 - desc (按顺序排列树),但它似乎不想玩。我把 $sort: "date":-1 放在 "lastUpdated": "$$REMOVE" 之后。我在原帖中可能没有说清楚。 我更新了答案排序并只保留唯一的年/月【参考方案2】:

我继续努力,虽然这还不太正确(还),但这是我想出的。

db.FormsStore.aggregate([
  
  $project: formName:1, lastUpdated:1, updatedAt:1,
   
    $group:  
      _id: formName: "$formName", Year: $year: $dateFromString:  dateString: "$updatedAt" , Month: $month: $dateFromString:  dateString: "$updatedAt" ,
     
  ,
  
    $group: 
        _id: formName: "$_id.formName", Year: "$_id.Year", 
        Months: $addToSet: Text: "$_id.Month"
    
  ,
  
    $group: 
        _id:  "$_id.formName", Children: $addToSet: Text: "$_id.Year", Children: "$Months", 
        
    
  
])

在第一组中获取我的所有数据,然后在第二组中创建一个包含月份的集合,然后在第三组中创建一个包含年份的集合并将月份添加到每年。

【讨论】:

以上是关于MongoDB 在数组中创建数组的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 mongodb (node.js) 在集合中创建一个包含所有值的数组

在同一集合中创建对象的 mongodb 模式数组或创建新集合的更好方法保存引用

如何使用 java spring 注释在 MongoDB 中创建一个完全填充的引用对象

无法在 MongoDB(猫鼬)文档中追加数组

尝试使用 mongodb 插件在 grails 中创建 mongodb 集合

是啥在 mongodb 中创建了默认的 ObjectID?