So, i have this schema
所以,我有这个架构
Purchases Schema:
购买架构:
const purchasesSchema = new Schema({
date: String,
status: String,
product: { type: Schema.Types.ObjectId, ref: 'Product' }
})
Product Schema:
产品架构:
const productSchema = new Schema({
product_name: String,
transaction: [{
price: Number,
purchase: { type: Schema.Types.ObjectId, ref: 'Purchases' }
}]
The algorithm, before making a purchase must create a product name, then create product transaction in purchase with bond transaction.purchase.
在购买之前,算法必须创建产品名称,然后在购买时使用bond transaction.purchase创建产品交易。
Purchase document sample:
购买文件样本:
[{
"_id": "5ac0b7cab7924a1710398c9e",
"date": "01/04/2018",
"status": "Not Paid",
"product": {
"_id": "5ac0b7b1b7924a1710398c9a",
"product_name": "Milk",
"transactions": [
{
"_id": "5ac0b7c9b7924a1710398c9b",
"price": 5000,
}
],
}
}]
Expected document with purchase id binding in transactions
在交易中具有购买ID绑定的预期文档
[{
"_id": "5ac0b7cab7924a1710398c9e",
"date": "01/04/2018",
"status": "Not Paid",
"product": {
"_id": "5ac0b7b1b7924a1710398c9a",
"product_name": "Milk",
"transactions": [
{
"_id": "5ac0b7c9b7924a1710398c9b",
"price": 5000,
"purchase": "the id"
}
],
}
}]
So far i had tried, in case i want to push transaction to already product name:
到目前为止,我曾尝试过,以防我想将交易推送到已经的产品名称:
const newTransaction = {
product_price: req.body.product_price
}
Product.findOneAndUpdate({ _id: req.body.product_id }, { $push: { transaction: newTransaction } }, { new: true }, (err, data) => {
const TRANSACTION_ID = data.transaction[data.transaction.length -1]._id
const newPurchase = new Purchase({
date: moment(req.body.date).format('DD/MM/YYYY'),
status: 'Not Paid',
product: req.body.product_id,
})
newPurchase.save((err, data) => {
if (err) throw err
const PURCHASES_ID = data._id
Product.findOneAndUpdate({ 'transactions._id': TRANSCATION_ID }, { $set: { transactions: { purchase: PURCHASES_ID } } }, (err, data) => { if (err) return handleError(err) })
The problem how to push Purchase id as 'transactions.purchase' in the nested array product schema. Thank you.
如何在嵌套数组产品模式中将Purchase id作为'transactions.purchase'推送出来的问题。谢谢。
1 个解决方案
#1
0
Solved from this answer https://*.com/a/23577266/5834822
解决了这个答案https://*.com/a/23577266/5834822
General Scope and Explanation
There are a few things wrong with what you are doing here. Firstly your query conditions. You are referring to several _id
values where you should not need to, and at least one of which is not on the top level.
你在这里做的事情有些不对劲。首先是您的查询条件。您指的是您不应该需要的几个_id值,并且其中至少有一个不在顶层。
In order to get into a "nested" value and also presuming that _id
value is unique and would not appear in any other document, you query form should be like this:
为了进入“嵌套”值并假设_id值是唯一的并且不会出现在任何其他文档中,您的查询表单应如下所示:
Model.update(
{ "array1.array2._id": "123" },
{ "$push": { "array1.0.array2.$.answeredBy": "success" } },
function(err,numAffected) {
// something with the result in here
}
);
Now that would actually work, but really it is only a fluke that it does as there are very good reasons why it should not work for you.
现在这实际上是有效的,但实际上它只是一个侥幸它,因为有很好的理由为什么它不适合你。
The important reading is in the official documentation for the positional $
operator under the subject of "Nested Arrays". What this says is:
重要的读物是在“嵌套数组”主题下的位置$运算符的官方文档中。这说的是:
The positional $ operator cannot be used for queries which traverse more than one array, such as queries that traverse arrays nested within other arrays, because the replacement for the $ placeholder is a single value
位置$运算符不能用于遍历多个数组的查询,例如遍历嵌套在其他数组中的数组的查询,因为$ placeholder的替换是单个值
Specifically what that means is the element that will be matched and returned in the positional placeholder is the value of the index from the first matching array. This means in your case the matching index on the "top" level array.
具体来说,这意味着将在位置占位符中匹配并返回的元素是来自第一个匹配数组的索引的值。在您的情况下,这意味着“顶部”级别数组上的匹配索引。
So if you look at the query notation as shown, we have "hardcoded" the first ( or 0 index ) position in the top level array, and it just so happens that the matching element within "array2" is also the zero index entry.
因此,如果您查看显示的查询表示法,我们已经“硬编码”了*数组中的第一个(或0个索引)位置,并且恰好“array2”中的匹配元素也是零索引条目。
To demonstrate this you can change the matching _id
value to "124" and the result will $push
an new entry onto the element with _id
"123" as they are both in the zero index entry of "array1" and that is the value returned to the placeholder.
为了证明这一点,您可以将匹配的_id值更改为“124”,结果将$将一个新条目推送到带有_id“123”的元素,因为它们都位于“array1”的零索引条目中,这是返回的值占位符。
So that is the general problem with nesting arrays. You could remove one of the levels and you would still be able to $push
to the correct element in your "top" array, but there would still be multiple levels.
这就是嵌套数组的一般问题。你可以删除其中一个级别,你仍然可以推送到“顶部”数组中的正确元素,但仍然会有多个级别。
Try to avoid nesting arrays as you will run into update problems as is shown.
尝试避免嵌套数组,因为您将遇到更新问题,如图所示。
The general case is to "flatten" the things you "think" are "levels" and actually make theses "attributes" on the final detail items. For example, the "flattened" form of the structure in the question should be something like:
一般情况是“压扁”你“认为”是“水平”的东西,并实际上在最后的细节项目上做出“属性”。例如,问题中结构的“扁平”形式应该是这样的:
{
"answers": [
{ "by": "success", "type2": "123", "type1": "12" }
]
}
Or even when accepting the inner array is $push
only, and never updated:
或者甚至在接受内部数组时只需$ push,并且从不更新:
{
"array": [
{ "type1": "12", "type2": "123", "answeredBy": ["success"] },
{ "type1": "12", "type2": "124", "answeredBy": [] }
]
}
Which both lend themselves to atomic updates within the scope of the positional $
operator
这两者都适用于位置$运算符范围内的原子更新
MongoDB 3.6 and Above
From MongoDB 3.6 there are new features available to work with nested arrays. This uses the positional filtered $[<identifier>]
syntax in order to match the specific elements and apply different conditions through arrayFilters
in the update statement:
从MongoDB 3.6开始,有一些新功能可用于嵌套数组。这使用位置过滤的$ [
Model.update(
{
"_id": 1,
"array1": {
"$elemMatch": {
"_id": "12","array2._id": "123"
}
}
},
{
"$push": { "array1.$[outer].array2.$[inner].answeredBy": "success" }
},
{
"arrayFilters": [{ "outer._id": "12" },{ "inner._id": "123" }]
}
)
The "arrayFilters"
as passed to the options for .update()
or even .updateOne()
, .updateMany()
, .findOneAndUpdate()
or .bulkWrite()
method specifies the conditions to match on the identifier given in the update statement. Any elements that match the condition given will be updated.
传递给.update()或.updateOne()或.updateMany(),. findOneAndUpdate()或.bulkWrite()方法的选项的“arrayFilters”指定了与update语句中给出的标识符匹配的条件。任何符合条件的元素都将被更新。
Because the structure is "nested", we actually use "multiple filters" as is specified with an "array" of filter definitions as shown. The marked "identifier" is used in matching against the positional filtered $[<identifier>]
syntax actually used in the update block of the statement. In this case inner
and outer
are the identifiers used for each condition as specified with the nested chain.
因为结构是“嵌套的”,所以我们实际使用“多个过滤器”,如图所示,使用过滤器定义的“数组”。标记的“标识符”用于匹配在语句的更新块中实际使用的位置过滤的$ [
This new expansion makes the update of nested array content possible, but it does not really help with the practicality of "querying" such data, so the same caveats apply as explained earlier.
这种新的扩展使得嵌套数组内容的更新成为可能,但它并没有真正帮助“查询”这些数据的实用性,因此同样的警告适用于前面所解释的。
You typically really "mean" to express as "attributes", even if your brain initially thinks "nesting", it's just usually a reaction to how you believe the "previous relational parts" come together. In reality you really need more denormalization.
你通常真的“意味着”表达为“属性”,即使你的大脑最初认为是“嵌套”,它通常只是对你如何相信“以前的关系部分”走到一起的反应。实际上,你真的需要更多的非规范化。
Also see How to Update Multiple Array Elements in mongodb, since these new update operators actually match and update "multiple array elements" rather than just the first, which has been the previous action of positional updates.
另请参阅如何在mongodb中更新多个数组元素,因为这些新的更新运算符实际上匹配并更新“多个数组元素”而不仅仅是第一个,这是以前的位置更新操作。
NOTE Somewhat ironically, since this is specified in the "options" argument for
.update()
and like methods, the syntax is generally compatible with all recent release driver versions.注意有点讽刺的是,因为这是在.update()和类似方法的“options”参数中指定的,所以语法通常与所有最新的发行版驱动程序版本兼容。
However this is not true of the
mongo
shell, since the way the method is implemented there ( "ironically for backward compatibility" ) thearrayFilters
argument is not recognized and removed by an internal method that parses the options in order to deliver "backward compatibility" with prior MongoDB server versions and a "legacy".update()
API call syntax.然而,mongo shell并不是这样,因为在那里实现该方法的方式(“具有讽刺意味的是为了向后兼容性”),arrayFilters参数不被内部方法识别和删除,该方法解析选项以提供“向后兼容性”使用以前的MongoDB服务器版本和“遗留”.update()API调用语法。
So if you want to use the command in the
mongo
shell or other "shell based" products ( notably Robo 3T ) you need a latest version from either the development branch or production release as of 3.6 or greater.因此,如果您想在mongo shell或其他“基于shell”的产品(特别是Robo 3T)中使用该命令,则需要从3.6或更高版本开始分支或生产版本的最新版本。
See also positional all $[]
which also updates "multiple array elements" but without applying to specified conditions and applies to all elements in the array where that is the desired action.
另请参见位置全部$ [],它还更新“多个数组元素”,但不应用于指定的条件,并应用于数组中所需的操作所有元素。
#1
0
Solved from this answer https://*.com/a/23577266/5834822
解决了这个答案https://*.com/a/23577266/5834822
General Scope and Explanation
There are a few things wrong with what you are doing here. Firstly your query conditions. You are referring to several _id
values where you should not need to, and at least one of which is not on the top level.
你在这里做的事情有些不对劲。首先是您的查询条件。您指的是您不应该需要的几个_id值,并且其中至少有一个不在顶层。
In order to get into a "nested" value and also presuming that _id
value is unique and would not appear in any other document, you query form should be like this:
为了进入“嵌套”值并假设_id值是唯一的并且不会出现在任何其他文档中,您的查询表单应如下所示:
Model.update(
{ "array1.array2._id": "123" },
{ "$push": { "array1.0.array2.$.answeredBy": "success" } },
function(err,numAffected) {
// something with the result in here
}
);
Now that would actually work, but really it is only a fluke that it does as there are very good reasons why it should not work for you.
现在这实际上是有效的,但实际上它只是一个侥幸它,因为有很好的理由为什么它不适合你。
The important reading is in the official documentation for the positional $
operator under the subject of "Nested Arrays". What this says is:
重要的读物是在“嵌套数组”主题下的位置$运算符的官方文档中。这说的是:
The positional $ operator cannot be used for queries which traverse more than one array, such as queries that traverse arrays nested within other arrays, because the replacement for the $ placeholder is a single value
位置$运算符不能用于遍历多个数组的查询,例如遍历嵌套在其他数组中的数组的查询,因为$ placeholder的替换是单个值
Specifically what that means is the element that will be matched and returned in the positional placeholder is the value of the index from the first matching array. This means in your case the matching index on the "top" level array.
具体来说,这意味着将在位置占位符中匹配并返回的元素是来自第一个匹配数组的索引的值。在您的情况下,这意味着“顶部”级别数组上的匹配索引。
So if you look at the query notation as shown, we have "hardcoded" the first ( or 0 index ) position in the top level array, and it just so happens that the matching element within "array2" is also the zero index entry.
因此,如果您查看显示的查询表示法,我们已经“硬编码”了*数组中的第一个(或0个索引)位置,并且恰好“array2”中的匹配元素也是零索引条目。
To demonstrate this you can change the matching _id
value to "124" and the result will $push
an new entry onto the element with _id
"123" as they are both in the zero index entry of "array1" and that is the value returned to the placeholder.
为了证明这一点,您可以将匹配的_id值更改为“124”,结果将$将一个新条目推送到带有_id“123”的元素,因为它们都位于“array1”的零索引条目中,这是返回的值占位符。
So that is the general problem with nesting arrays. You could remove one of the levels and you would still be able to $push
to the correct element in your "top" array, but there would still be multiple levels.
这就是嵌套数组的一般问题。你可以删除其中一个级别,你仍然可以推送到“顶部”数组中的正确元素,但仍然会有多个级别。
Try to avoid nesting arrays as you will run into update problems as is shown.
尝试避免嵌套数组,因为您将遇到更新问题,如图所示。
The general case is to "flatten" the things you "think" are "levels" and actually make theses "attributes" on the final detail items. For example, the "flattened" form of the structure in the question should be something like:
一般情况是“压扁”你“认为”是“水平”的东西,并实际上在最后的细节项目上做出“属性”。例如,问题中结构的“扁平”形式应该是这样的:
{
"answers": [
{ "by": "success", "type2": "123", "type1": "12" }
]
}
Or even when accepting the inner array is $push
only, and never updated:
或者甚至在接受内部数组时只需$ push,并且从不更新:
{
"array": [
{ "type1": "12", "type2": "123", "answeredBy": ["success"] },
{ "type1": "12", "type2": "124", "answeredBy": [] }
]
}
Which both lend themselves to atomic updates within the scope of the positional $
operator
这两者都适用于位置$运算符范围内的原子更新
MongoDB 3.6 and Above
From MongoDB 3.6 there are new features available to work with nested arrays. This uses the positional filtered $[<identifier>]
syntax in order to match the specific elements and apply different conditions through arrayFilters
in the update statement:
从MongoDB 3.6开始,有一些新功能可用于嵌套数组。这使用位置过滤的$ [
Model.update(
{
"_id": 1,
"array1": {
"$elemMatch": {
"_id": "12","array2._id": "123"
}
}
},
{
"$push": { "array1.$[outer].array2.$[inner].answeredBy": "success" }
},
{
"arrayFilters": [{ "outer._id": "12" },{ "inner._id": "123" }]
}
)
The "arrayFilters"
as passed to the options for .update()
or even .updateOne()
, .updateMany()
, .findOneAndUpdate()
or .bulkWrite()
method specifies the conditions to match on the identifier given in the update statement. Any elements that match the condition given will be updated.
传递给.update()或.updateOne()或.updateMany(),. findOneAndUpdate()或.bulkWrite()方法的选项的“arrayFilters”指定了与update语句中给出的标识符匹配的条件。任何符合条件的元素都将被更新。
Because the structure is "nested", we actually use "multiple filters" as is specified with an "array" of filter definitions as shown. The marked "identifier" is used in matching against the positional filtered $[<identifier>]
syntax actually used in the update block of the statement. In this case inner
and outer
are the identifiers used for each condition as specified with the nested chain.
因为结构是“嵌套的”,所以我们实际使用“多个过滤器”,如图所示,使用过滤器定义的“数组”。标记的“标识符”用于匹配在语句的更新块中实际使用的位置过滤的$ [
This new expansion makes the update of nested array content possible, but it does not really help with the practicality of "querying" such data, so the same caveats apply as explained earlier.
这种新的扩展使得嵌套数组内容的更新成为可能,但它并没有真正帮助“查询”这些数据的实用性,因此同样的警告适用于前面所解释的。
You typically really "mean" to express as "attributes", even if your brain initially thinks "nesting", it's just usually a reaction to how you believe the "previous relational parts" come together. In reality you really need more denormalization.
你通常真的“意味着”表达为“属性”,即使你的大脑最初认为是“嵌套”,它通常只是对你如何相信“以前的关系部分”走到一起的反应。实际上,你真的需要更多的非规范化。
Also see How to Update Multiple Array Elements in mongodb, since these new update operators actually match and update "multiple array elements" rather than just the first, which has been the previous action of positional updates.
另请参阅如何在mongodb中更新多个数组元素,因为这些新的更新运算符实际上匹配并更新“多个数组元素”而不仅仅是第一个,这是以前的位置更新操作。
NOTE Somewhat ironically, since this is specified in the "options" argument for
.update()
and like methods, the syntax is generally compatible with all recent release driver versions.注意有点讽刺的是,因为这是在.update()和类似方法的“options”参数中指定的,所以语法通常与所有最新的发行版驱动程序版本兼容。
However this is not true of the
mongo
shell, since the way the method is implemented there ( "ironically for backward compatibility" ) thearrayFilters
argument is not recognized and removed by an internal method that parses the options in order to deliver "backward compatibility" with prior MongoDB server versions and a "legacy".update()
API call syntax.然而,mongo shell并不是这样,因为在那里实现该方法的方式(“具有讽刺意味的是为了向后兼容性”),arrayFilters参数不被内部方法识别和删除,该方法解析选项以提供“向后兼容性”使用以前的MongoDB服务器版本和“遗留”.update()API调用语法。
So if you want to use the command in the
mongo
shell or other "shell based" products ( notably Robo 3T ) you need a latest version from either the development branch or production release as of 3.6 or greater.因此,如果您想在mongo shell或其他“基于shell”的产品(特别是Robo 3T)中使用该命令,则需要从3.6或更高版本开始分支或生产版本的最新版本。
See also positional all $[]
which also updates "multiple array elements" but without applying to specified conditions and applies to all elements in the array where that is the desired action.
另请参见位置全部$ [],它还更新“多个数组元素”,但不应用于指定的条件,并应用于数组中所需的操作所有元素。