Javascript Mongoose findOneAndUpdate:创建并更新嵌套数组

Javascript Mongoose findOneAndUpdate:创建并更新嵌套数组,javascript,node.js,mongoose,mlab,Javascript,Node.js,Mongoose,Mlab,我有一个程序,我从服务器请求天气数据,处理数据,然后使用mongoose将其保存到mlab帐户。我收集了10年的数据,但是我请求数据的API每次只允许请求大约一年 我正在使用FindAndUpdate为每个气象站创建/更新文档,但在更新数据对象中的数组时遇到问题。(可能不是最好的描述方式…) 例如,以下是模型: const stnDataSchema = new Schema( { station: { type: String, default: nul

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

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

例如,以下是模型:

    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对象如下所示:

    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.....
    "data": {
      "0": {
          "maxT": [
            "a",
            "b",
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)
我希望在运行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": [
       {
        "maxT": [
            0,
            null,
            4.4,
            0,
            -2.7,
            etc.....
    "data": {
      "0": {
          "maxT": [
            "a",
            "b",
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)
问题是我得到的是一个“0”,而不是一个元素数组。我尝试了“data[0].maxT”,但执行此操作时什么也没有发生


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

您将获得以下输出:

 "data": {
    "0": {
      "maxT": [
        "a",
        "b",
因为您正在升级文档。在处理文档数组时,升级变得有点复杂

更新数组时,MongoDB知道
data.0
引用数组中的第一个元素。但是,在插入时,MongoDB无法判断它是要作为数组还是对象。所以它假设它是一个对象。因此,它不是插入
[“val”]
,而是插入
{“0”:“val”}


最简单的解决方案

不要使用向上插入。为每个新气象站插入一个文档,然后使用
findonandupdate
将值推送到文档中的数组中。只要第一次正确插入阵列,就可以将其推入,而不会将其变成对象


如果
数据
仅包含一个对象

从您的问题来看,似乎在
数据中只有一个对象。如果是这种情况,您可以将
maxT
数组设置为顶级,而不是数组中单个文档的属性。然后它的行为就像日期一样


更复杂的MongoDB 3.6解决方案

如果你真的不能没有upserts,MongoDB 3.6引入了过滤的位置操作符
$[]
。您可以使用此运算符更新数组中与查询匹配的特定元素,只要使用精确匹配,就可以使用新的
$[]
运算符进行upsert

您可以在此处阅读有关此运算符的更多信息:

因此,您的
数据
对象需要有一个字段,该字段可以精确匹配(比如
名称
)。示例查询如下所示:

    "data": [
       {
        "maxT": [
            0,
            null,
            4.4,
            0,
            -2.7,
            etc.....
    "data": {
      "0": {
          "maxT": [
            "a",
            "b",
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)
如您所见,这增加了更多的复杂性。忘记尝试做更高的事会容易得多。只需进行一次插入,然后进行更新


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

您将获得以下输出:

 "data": {
    "0": {
      "maxT": [
        "a",
        "b",
因为您正在升级文档。在处理文档数组时,升级变得有点复杂

更新数组时,MongoDB知道
data.0
引用数组中的第一个元素。但是,在插入时,MongoDB无法判断它是要作为数组还是对象。所以它假设它是一个对象。因此,它不是插入
[“val”]
,而是插入
{“0”:“val”}


最简单的解决方案

不要使用向上插入。为每个新气象站插入一个文档,然后使用
findonandupdate
将值推送到文档中的数组中。只要第一次正确插入阵列,就可以将其推入,而不会将其变成对象


如果
数据
仅包含一个对象

从您的问题来看,似乎在
数据中只有一个对象。如果是这种情况,您可以将
maxT
数组设置为顶级,而不是数组中单个文档的属性。然后它的行为就像日期一样


更复杂的MongoDB 3.6解决方案

如果你真的不能没有upserts,MongoDB 3.6引入了过滤的位置操作符
$[]
。您可以使用此运算符更新数组中与查询匹配的特定元素,只要使用精确匹配,就可以使用新的
$[]
运算符进行upsert

您可以在此处阅读有关此运算符的更多信息:

因此,您的
数据
对象需要有一个字段,该字段可以精确匹配(比如
名称
)。示例查询如下所示:

    "data": [
       {
        "maxT": [
            0,
            null,
            4.4,
            0,
            -2.7,
            etc.....
    "data": {
      "0": {
          "maxT": [
            "a",
            "b",
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)
如您所见,这增加了更多的复杂性。忘记尝试做更高的事会容易得多。只需进行一次插入,然后进行更新

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