Mongoose findOneAndUpdate:创建然后更新嵌套数组
Mongoose findOneAndUpdate: create and then update nested array
我有一个程序,我从服务器请求天气数据,处理数据,然后使用 mongoose 将其保存到 mlab 帐户。我正在收集 10 年的数据,但我从中请求数据的 API 只允许一次请求大约一年。
我正在使用 findOndAndUpdate create/update 每个气象站的文档,但在更新数据对象中的数组时遇到问题。 (可能不是描述它的最佳方式...)
例如,这是模型:
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' 但当我这样做时没有任何反应。
问题是我第一次运行一个站的数据,我想用第三个代码块中的格式的数据对象创建一个新文档,然后在随后的运行s,一旦该文档已经存在,用新值更新 maxT 数组。有什么想法吗?
你得到这个输出:
"data": {
"0": {
"maxT": [
"a",
"b",
因为您正在更新文档。在处理文档数组时,Upserting 变得有点复杂。
更新数组时,MongoDB 知道 data.0
指的是数组中的第一个元素。但是,在插入时,MongoDB 无法判断它是数组还是对象。所以它假设它是一个对象。因此,它不是插入 ["val"]
,而是插入 {"0": "val"}
.
最简单的解决方案
不要使用更新插入。为每个新气象站插入一个文档,然后使用 findOndAndUpdate
将值推送到文档中的数组中。只要你第一次正确插入数组,你就可以在不变成对象的情况下向它们推送。
如果 data
仅包含一个对象
的另一种简单解决方案
从你的问题来看,你在 data
中似乎只有一个对象。如果是这种情况,您可以只创建 maxT
数组 top-level,而不是数组中单个文档的 属性。然后它会像 dates
.
更复杂MongoDB 3.6 解决方案
如果你真的离不开更新插入,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)
如您所见,这增加了更多的复杂性。忘记尝试做 upsert 会容易得多。只做一个插入然后更新。
此外 mLab 目前不支持 MongoDB 3.6。因此,在支持 3.6 之前,使用 mLab 时此方法不可行。
我有一个程序,我从服务器请求天气数据,处理数据,然后使用 mongoose 将其保存到 mlab 帐户。我正在收集 10 年的数据,但我从中请求数据的 API 只允许一次请求大约一年。
我正在使用 findOndAndUpdate create/update 每个气象站的文档,但在更新数据对象中的数组时遇到问题。 (可能不是描述它的最佳方式...)
例如,这是模型:
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' 但当我这样做时没有任何反应。
问题是我第一次运行一个站的数据,我想用第三个代码块中的格式的数据对象创建一个新文档,然后在随后的运行s,一旦该文档已经存在,用新值更新 maxT 数组。有什么想法吗?
你得到这个输出:
"data": {
"0": {
"maxT": [
"a",
"b",
因为您正在更新文档。在处理文档数组时,Upserting 变得有点复杂。
更新数组时,MongoDB 知道 data.0
指的是数组中的第一个元素。但是,在插入时,MongoDB 无法判断它是数组还是对象。所以它假设它是一个对象。因此,它不是插入 ["val"]
,而是插入 {"0": "val"}
.
最简单的解决方案
不要使用更新插入。为每个新气象站插入一个文档,然后使用 findOndAndUpdate
将值推送到文档中的数组中。只要你第一次正确插入数组,你就可以在不变成对象的情况下向它们推送。
如果 data
仅包含一个对象
从你的问题来看,你在 data
中似乎只有一个对象。如果是这种情况,您可以只创建 maxT
数组 top-level,而不是数组中单个文档的 属性。然后它会像 dates
.
更复杂MongoDB 3.6 解决方案
如果你真的离不开更新插入,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)
如您所见,这增加了更多的复杂性。忘记尝试做 upsert 会容易得多。只做一个插入然后更新。
此外 mLab 目前不支持 MongoDB 3.6。因此,在支持 3.6 之前,使用 mLab 时此方法不可行。