Mongoose findOneAndUpdate:创建然后更新嵌套数组

Posted

技术标签:

【中文标题】Mongoose findOneAndUpdate:创建然后更新嵌套数组【英文标题】:Mongoose findOneAndUpdate: create and then update nested array 【发布时间】:2018-08-18 14:59:34 【问题描述】:

我有一个程序,我从服务器请求天气数据,处理数据,然后使用 mongoose 将其保存到 mlab 帐户。我正在收集 10 年的数据,但我从中请求数据的 API 一次只允许请求大约一年。

我正在使用 findOndAndUpdate 为每个气象站创建/更新文档,但在更新数据对象中的数组时遇到了问题。 (可能不是最好的描述方式……)

例如,这是模型:

    const stnDataSchema = new Schema(
        
        station:  type: String, default: null , 
        elevation:  type: String, default: null ,
        timeZone:  type: String, default: null ,
        dates: ,
        data: 
       , 
        collection: 'stndata' ,
        runSettersOnQuery: true 
     )

日期对象如下所示:

    dates: ["2007-01-01",
    "2007-01-02",
    "2007-01-03",
    "2007-01-04",
    "2007-01-05",
    "2007-01-06",
    "2007-01-07",
    "2007-01-08",
    "2007-01-09"]

和这样的数据对象:

    "data": [
       
        "maxT": [
            0,
            null,
            4.4,
            0,
            -2.7,
            etc.....

我想要发生的是,当我运行 findOneAndUpdate 时,我想根据站点查找文档,然后将新的 maxT 值和日期附加到相应的数组中。我让它适用于日期数组,但是由于我更新的元素是嵌套的,因此数据数组遇到了麻烦。 我试过这个:

    const update = 
    $set: 'station': station, 'elevation': elevation, 'timeZone': timeZone,
    $push: 'dates': datesTest, 'data.0.maxT': testMaxT;
    StnData.findOneAndUpdate( query, update, upsert: true ,
    function(err, doc) 
    if (err) 
     console.log("error in updateStation", err)
     throw new Error('error in updateStation')
   
   else 
     console.log('saved')

但是得到了这样的输出到 mlab:

    "data": 
      "0": 
          "maxT": [
            "a",
            "b",

问题是我得到一个“0”而不是一个元素的数组。我尝试了'data [0] .maxT',但是当我这样做时没有任何反应。

问题是我第一次为一个站运行数据时,我想在我的第三个代码块中创建一个具有格式数据对象的新文档,然后在后续运行中,一旦该文档已经存在,更新具有新值的 maxT 数组。有什么想法吗?

【问题讨论】:

【参考方案1】:

你得到这个输出:

 "data": 
    "0": 
      "maxT": [
        "a",
        "b",

因为您正在更新文档。在处理文档数组时,Upserting 会变得有点复杂。

更新数组时,MongoDB 知道data.0 指的是数组中的第一个元素。但是,在插入时,MongoDB 无法判断它是一个数组还是一个对象。所以它假设它是一个对象。所以不是插入["val"],而是插入"0": "val"


最简单的解决方案

不要使用 upsert。为每个新气象站插入一个文档,然后使用findOndAndUpdate 将值推送到文档中的数组中。只要您第一次正确插入数组,您就可以将它们推送到它们而不会变成对象。


如果data 仅包含一个对象,则另一种简单解决方案

根据您的问题,您似乎在data 中只有一个对象。如果是这种情况,您可以将 maxT 数组设为***,而不是成为数组中单个文档的属性。然后它的行为就像dates


更复杂的 MongoDB 3.6 解决方案

如果你真的离不开 upsert,MongoDB 3.6 引入了过滤位置运算符$[<identifier>]。您可以使用此运算符更新与查询匹配的数组中的特定元素。 Unlike the simple positional operator $,只要使用精确匹配,就可以使用新的 $[<identifier>] 运算符进行 upsert。

您可以在此处阅读有关此运算符的更多信息:https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/

所以你的data 对象需要有一个可以完全匹配的字段(比如name)。示例查询如下所示:

let query = 
  _id: 'idOfDocument', 
  data: [name: 'subobjectName'] // Need this for an exact match


let update = $push: 'data.$[el].maxT': testMaxT

let options = upsert: true, arrayFilters: ['el.name': 'subobjectName']

StnData.findOneAndUpdate(query, update, options, callbackFn)

如您所见,这增加了更多的复杂性。忘记尝试做 upserts 会容易得多。只需执行一次插入然后更新。

此外,mLab 目前不支持 MongoDB 3.6。因此,在支持 3.6 之前,使用 mLab 时此方法不可行。

【讨论】:

以上是关于Mongoose findOneAndUpdate:创建然后更新嵌套数组的主要内容,如果未能解决你的问题,请参考以下文章

Mongoose:findOneAndUpdate 不返回更新的文档

Mongoose - 使用 findOneAndUpdate 更新子文档

Mongoose `findOneAndUpdate` 回调不传递更新的文档

Mongoose `findOneAndUpdate` 回调不传递更新的文档

Mongoose:findOneAndUpdate 不适用于多种条件

Mongoose findOneAndUpdate 更新多个字段