Meteor Collection 根据数组中的最新日期时间查找文档

Meteor Collection Find Documents Based on Latest Date Time in an Array

如何使用日期时间从集合中获取最新文档?

我在 SO 中搜索了这个特定问题,但找不到与我的数据结构相似的示例。我有这样的数据结构:

[
   {
      stationId: 'xxxxx',
      stationName:  'xxxx',
      state: 'xxxx',
      lat: 'xxxxx',
      long: 'xx.xxxxx',
      waterLevel: [
         {
              wlDateTime: '11/04/2022 11:30',
              wlSeverity: 'Danger',
              wlLevel: 7.5
         },
         {
              wlDateTime: '11/04/2022 09:00',
              wlSeverity: 'Danger',
              wlLevel: 7.3
         },
         {
              wlDateTime: '11/04/2022 03:00',
              wlSeverity: 'Normal',
              wlLevel: 5.2
         }
      ],
      rainfallData: [
         {
              rfDateTime: '11/04/2022 11:30',
              rfSeverity: 'Heavy',
              rfLevel: 21
         },
         {
              rfDateTime: '11/04/2022 10:30',
              rfSeverity: 'Heavy',
              rfLevel: 21
         },
         {
              rfDateTime: '11/04/2022 9:30',
              rfSeverity: 'Heavy',
              rfLevel: 21
         }
      ]
   }
]

问题是,我怎样才能得到今天 wlDateTime 等于 wlSeverity 等于 Danger 的文档,但我只想要 [=17] 的最新记录=]数组。 rainfallData数组的情况相同,即 return 今天的最新读数。

样本预期 return 将是这样的:

[
   {
      stationId: 'xxxxx',
      stationName:  'xxxx',
      state: 'xxxx',
      lat: 'xxxxx',
      long: 'xx.xxxxx',
      waterLevelData: [
         {
              wlDateTime: '11/04/2022 11:30',  //latest data compared to the array
              wlSeverity: 'Danger',
              wlLevel: 7.5
         }
      ],
      rainfallData: [
         {
              rfDateTime: '11/04/2022 11:30', //latest data compared to the array
              rfSeverity: 'Heavy',
              rfLevel: 21
         }
      ]
   }
]

我试过这样查询:

    Meteor.publish('Alerts', function(){
      return AlertLatest.find({
        'waterLevelData.wlSeverity':'Danger',
      }, {
        fields : {
        'stationName'               : 1,
        'state'                     : 1,
        'lat'                       : 1,
        'long'                      : 1,
        'waterLevelData.wlDateTime' : 1,
        'waterLevelData.wlSeverity' : 1,
        'waterLevelData.wlLevel'    : 1,
        'rainfallData.rfSeverity'   : 1,      
      }},{sort: { 'waterLevelData.wlDateTime' : -1}});
    })

但是查询 returned 数据不是我想要的。任何帮助将不胜感激。

更新

我试过@YuTing提供的方案,就是用聚合来自定义发布查询。我继续阅读了一些关于 Mongodb 聚合的内容,并找到了一个简化流程的 Meteorjs 社区包 (tunguska:reactive-aggregate)。

这是目前有效聚合的样本:

Meteor.publish('PIBDataAlerts', function(){
      const start = dayjs().startOf('day'); // set to 12:00 am today
      const end = dayjs().endOf('day'); // set to 23:59 pm today
      ReactiveAggregate(this, PIBLatest, [
        {
          $match: {   
            'stationStatus' : 'ON',
            'waterLevelData': {        //trying to get only today's docs
                "$elemMatch" : {
                    "wlDateTime" : {
                        $gte: start.format()  , $lt: end.format()  
                    }
                }
            }
          }
        },
        {
          $set: {
            waterLevelHFZ: {
              $filter: {
                input: "$waterLevelData",
                as: "w",
                cond: {
                  $and: [
                    { $or : [
                      { $eq: [ "$$w.wlSeverity", "Alert" ] },
                      { $eq: [ "$$w.wlSeverity", "Warning" ] },
                      { $eq: [ "$$w.wlSeverity", "Danger" ] },
                    ]},
                    { $eq: [ "$$w.wlDateTime", { $max: "$waterLevelData.wlDateTime" } ] }
                  ],
                }
              }
            },
            rainfallDataHFZ: {
              $filter: {
                input: "$rainfallData",
                as: "r",
                cond: { $eq: [ "$$r.rfDateTime", { $max: "$rainfallData.rfDateTime" } ] }
              }
            }
          }
        },
        {
          $project : {
            "stationId": 1,
            "stationName" :1,
            "state": 1,
            "waterLevelHFZ": 1,
            "rainfallDataHFZ": 1
          }
        }
      ]);
    })

我正在努力获取只有 wlDateTime 等于今天的文档。我已经尝试在 $match 中查询,但它 return 为空数组。如果 $match 设置为 {},它将 return 所有 1548 条记录,即使 wlDateTime 不等于今天。

我认为您不能按数组字段中的嵌入文档进行排序。这不是 mongodb 的工作方式。

  1. 将您的日期字符串更改为日期
  2. 过滤数组以找到最大值
db.collection.aggregate([
  {
    $match: {
      $expr: {
        $or: [
          {
            $ne: [
              {
                $filter: {
                  input: "$waterLevel",
                  as: "w",
                  cond: {
                    $eq: [
                      {
                        $dateTrunc: {
                          date: {
                            $dateFromString: {
                              dateString: "$$w.wlDateTime",
                              format: "%d/%m/%Y %H:%M"
                            }
                          },
                          unit: "day"
                        }
                      },
                      {
                        $dateTrunc: {
                          date: "$$NOW",
                          unit: "day"
                        }
                      }
                    ]
                  }
                }
              },
              []
            ]
          },
          {
            $ne: [
              {
                $filter: {
                  input: "$rainfallData",
                  as: "r",
                  cond: {
                    $eq: [
                      {
                        $dateTrunc: {
                          date: {
                            $dateFromString: {
                              dateString: "$$r.rfDateTime",
                              format: "%d/%m/%Y %H:%M"
                            }
                          },
                          unit: "day"
                        }
                      },
                      {
                        $dateTrunc: {
                          date: "$$NOW",
                          unit: "day"
                        }
                      }
                    ]
                  }
                }
              },
              []
            ]
          }
        ]
      }
    }
  },
  {
    $set: {
      waterLevel: {
        $map: {
          input: "$waterLevel",
          as: "w",
          in: {
            $mergeObjects: [
              "$$w",
              {
                wlDateTime: {
                  $dateFromString: {
                    dateString: "$$w.wlDateTime",
                    format: "%d/%m/%Y %H:%M"
                  }
                }
              }
            ]
          }
        }
      },
      rainfallData: {
        $map: {
          input: "$rainfallData",
          as: "r",
          in: {
            $mergeObjects: [
              "$$r",
              {
                rfDateTime: {
                  $dateFromString: {
                    dateString: "$$r.rfDateTime",
                    format: "%d/%m/%Y %H:%M"
                  }
                }
              }
            ]
          }
        }
      }
    }
  },
  {
    $set: {
      waterLevel: {
        $filter: {
          input: "$waterLevel",
          as: "w",
          cond: {
            $and: [
              {
                $in: [
                  "$$w.wlSeverity",
                  [
                    "Alert",
                    "Warning",
                    "Danger"
                  ]
                ]
              },
              {
                $eq: [
                  "$$w.wlDateTime",
                  {
                    $max: "$waterLevel.wlDateTime"
                  }
                ]
              },
              {
                $eq: [
                  {
                    $dateTrunc: {
                      date: "$$w.wlDateTime",
                      unit: "day"
                    }
                  },
                  {
                    $dateTrunc: {
                      date: "$$NOW",
                      unit: "day"
                    }
                  }
                ]
              }
            ]
          }
        }
      },
      rainfallData: {
        $filter: {
          input: "$rainfallData",
          as: "r",
          cond: {
            $and: [
              {
                $eq: [
                  "$$r.rfDateTime",
                  {
                    $max: "$rainfallData.rfDateTime"
                  }
                ]
              },
              {
                $eq: [
                  {
                    $dateTrunc: {
                      date: "$$r.rfDateTime",
                      unit: "day"
                    }
                  },
                  {
                    $dateTrunc: {
                      date: "$$NOW",
                      unit: "day"
                    }
                  }
                ]
              }
            ]
          }
        }
      }
    }
  }
])

mongoplayground

but I just want the latest

如果您只对最新的文档感兴趣,您可以省略 sort 并使用自然的负光标:

Meteor.publish('Alerts', function(){
  return AlertLatest.find({
    'waterLevelData.wlSeverity':'Danger',
  }, {
    fields : {
    'stationName'               : 1,
    'state'                     : 1,
    'lat'                       : 1,
    'long'                      : 1,
    'waterLevelData.wlDateTime' : 1,
    'waterLevelData.wlSeverity' : 1,
    'waterLevelData.wlLevel'    : 1,
    'rainfallData.rfSeverity'   : 1,      
  }},{ hint: { $natural: -1}});
})

它将从末尾开始计算文档数,而不是从头开始。

https://docs.meteor.com/api/collections.html#Mongo-Collection-find