Mongoose Slice Not Limiting Array Size - node.js

I'm working on an application and am having an issue with Mongoose limiting array size on update. I'm not too familiar with MongoDB or Mongoose but I'm hoping this project will grow my knowledge. I tried a few other solutions I queried on SOF but none appeared to work for me. Here is my breakdown of the problem...
Library: Mongoose 5.7.7
Summary: In a document of logs, I wish to keep only the 10 latest logs with the older being pushed off the array.
Issue: With the current query I have, the slice appears not to be limiting the number of documents. The array just continues to grow.
ActivityLog.updateOne(
{ guild },
{
$push: {
logs: {
$each: [{ ...log }]
},
$slice: -10
}
},
{ upsert: true }
);

This might actually be more of a problem in your implementation. Here's the basic thing in practical usage to show that it does indeed work:
const { Schema } = mongoose = require('mongoose');
const uri = 'mongodb://localhost:27017/test';
const options = { useNewUrlParser: true, useUnifiedTopology: true };
mongoose.set("debug", true);
mongoose.set("useFindAndModify", false);
mongoose.set("useCreateIndex", true);
const demoSchema = new Schema({
_id: Number,
fifo: [Number]
},{ _id: false });
const Demo = mongoose.model('Demo', demoSchema, 'demo');
const log = data => console.log(JSON.stringify(data, undefined, 2));
(async function() {
try {
const conn = await mongoose.connect(uri, options);
await Promise.all(
Object.values(conn.models).map(m => m.deleteMany())
);
let counter = 0;
await new Promise((resolve, reject) =>
setInterval(async () => {
try {
let result = await Demo.findByIdAndUpdate(
1,
{ "$push": { "fifo": { "$each": [counter], "$slice": -3 } } },
{ upsert: true, new: true }
);
log(result);
} catch(e) {
reject(e)
}
counter++;
}, 2000)
);
} catch (e) {
console.error(e);
} finally {
mongoose.disconnect();
}
})()
On a few iterations ( and using -3 for brevity here ) you would see:
Mongoose: demo.deleteMany({}, {})
Mongoose: demo.findOneAndUpdate({ _id: 1 }, { '$setOnInsert': { __v: 0 }, '$push': { fifo: { '$each': [ 0 ], '$slice': -3 } }}, { upsert: true, remove: false, projection: {}, returnOriginal: false })
{
"fifo": [
0
],
"_id": 1,
"__v": 0
}
Mongoose: demo.findOneAndUpdate({ _id: 1 }, { '$setOnInsert': { __v: 0 }, '$push': { fifo: { '$each': [ 1 ], '$slice': -3 } }}, { upsert: true, remove: false, projection: {}, returnOriginal: false })
{
"fifo": [
0,
1
],
"_id": 1,
"__v": 0
}
Mongoose: demo.findOneAndUpdate({ _id: 1 }, { '$setOnInsert': { __v: 0 }, '$push': { fifo: { '$each': [ 2 ], '$slice': -3 } }}, { upsert: true, remove: false, projection: {}, returnOriginal: false })
{
"fifo": [
0,
1,
2
],
"_id": 1,
"__v": 0
}
Mongoose: demo.findOneAndUpdate({ _id: 1 }, { '$setOnInsert': { __v: 0 }, '$push': { fifo: { '$each': [ 3 ], '$slice': -3 } }}, { upsert: true, remove: false, projection: {}, returnOriginal: false })
{
"fifo": [
1,
2,
3
],
"_id": 1,
"__v": 0
}
Mongoose: demo.findOneAndUpdate({ _id: 1 }, { '$setOnInsert': { __v: 0 }, '$push': { fifo: { '$each': [ 4 ], '$slice': -3 } }}, { upsert: true, remove: false, projection: {}, returnOriginal: false })
{
"fifo": [
2,
3,
4
],
"_id": 1,
"__v": 0
}
Mongoose: demo.findOneAndUpdate({ _id: 1 }, { '$setOnInsert': { __v: 0 }, '$push': { fifo: { '$each': [ 5 ], '$slice': -3 } }}, { upsert: true, remove: false, projection: {}, returnOriginal: false })
{
"fifo": [
3,
4,
5
],
"_id": 1,
"__v": 0
}
Mongoose: demo.findOneAndUpdate({ _id: 1 }, { '$setOnInsert': { __v: 0 }, '$push': { fifo: { '$each': [ 6 ], '$slice': -3 } }}, { upsert: true, remove: false, projection: {}, returnOriginal: false })
{
"fifo": [
4,
5,
6
],
"_id": 1,
"__v": 0
}
So this does indeed keep an array of the specified $slice length and from the end of the array as due to the negative, both growing the array to the set size and then removing all but the last added members.

Related

mongodb query vs mysql

I have the following MySQL query in which I have done sum profit which I have fields like: rate, credit(money)
SELECT SUM((credit*(100-rate))/100) FROM roznamcha WHERE (accountNum=$id AND rate!=0.0)'
I have written the following query in mongodb using node.js but it returns null whoever I have some data in my database
const profit=await roznamcha.aggregate([
{
$match:{
rate:{$ne:0}
}
},
{
$group:{
_id :'$accountNum',
}
},
{
$addFields:{
resultMultiply:{
$divide:[
{$multiply:['$credit','$rate-$100']},100
]
},
sumcredit:{
$sum:'$resultMultiply'
}
} }
])
res.status(201).json({
status:'success',
data:{
profit
}
})
My output:
{
"status": "success",
"data": {
"profit": [
{
"_id": "612deac8fbc8ef21a0fa4ea7",
"resultMultiply": null,
"sumcredit": 0
},
{
"_id": "612223327e2af83a4cec1272",
"resultMultiply": null,
"sumcredit": 0
}
]
}
my schema:
const mongoose = require('mongoose');
const roznamchaSchem=mongoose.Schema({
accountNum: {
type:mongoose.Schema.ObjectId,
ref:'account',
required: ['please specify this record is from who', true],
},
credit: {
type: Number,
default: 0
},
debit: {
type: Number,
default: 0
},
rate: {
type: Number,
default: 0
},
description:{
type:String,
required:['description can not be empty',true],
minlength: 8
},
issueDate:{
type: Date,
required:['add an valide date',true]
}
});
roznamchaSchem.index({accountNum:-1});
const Roznamcha=mongoose.model('roznamcha',roznamchaSchem);
module.exports=Roznamcha;
and my example of document:
id:612f533e8eb5533f303966e4
credit:50
debit:0
rate:2
accountNum:612deac8fbc8ef21a0fa4ea7
description:"this it for you"
issueDate:6543-01-01T00:00:00.000+00:00
can anyone guide me in solving this query?
Besides #Joe and #Nenad answer the error for the subtraction: '$rate-$100';
You need to re-position your logic structure.
Perform calculation: SUM((credit*(100-rate))/100).
Group by $accountNum and aggregate SUM for resultMultiply.
db.collection.aggregate([
{
$match: {
rate: {
$ne: 0
}
}
},
{
$addFields: {
resultMultiply: {
$divide: [
{
$multiply: [
"$credit",
{
"$subtract": [
100,
"$rate"
]
}
]
},
100
]
}
}
},
{
$group: {
_id: "$accountNum",
total: {
$sum: "$resultMultiply"
}
}
}
])
Output
[
{
"_id": 2,
"total": 1.5
},
{
"_id": 1,
"total": 5.5
}
]
Sample MongoDB playground
'$rate-$100' is referring to a field named "rate-$100". You probably meant to subtract using
{$subtract: [ 100, "$rate"]}
You can not use mathematical operator minus for substraction, you have to use $subtract aggregation operator.
resultMultiply: {
$divide: [{
$multiply: [
"$credit",
{ $subtract: [ 100, "$rate" ] }
]
},
100
]
}

Update object with value of array

For a project where we have actions and donations. We store the donations in an array in the related action. For the connection we use Mongoose.
The schema for an action is as follows, for readability I've removed some fields which are not related to this problem:
const donationSchema = new Schema(
{
id: {
type: String,
unique: true,
required: true,
index: true,
},
amount: { type: Number },
status: {
type: String,
enum: ['pending', 'collected', 'failed'],
default: 'pending',
},
},
{ timestamps: true, versionKey: false, _id: false },
);
const schema = new Schema(
{
donations: { type: [donationSchema], default: [] },
target: { type: Number, default: 0 },
collected: { type: Number, default: 0 },
},
{
timestamps: true,
versionKey: false,
},
);
const Action = model<IAction>('Action', schema);
Let say I have an Action with three donations, one in every state:
{
"_id": "6098fb22101f22cfcbd31e3b"
"target": 10000,
"collected": 25,
"donations": [
{
"uuid": "dd90f6f1-56d7-4d8b-a51f-f9e5382d3cd9",
"amount": 25,
"status": "collected"
},
{
"uuid": "eea0ac5e-1e52-4eba-aa1f-c1f4d072a37a",
"amount": 10,
"status": "failed"
},
{
"uuid": "215237bd-bfe6-4d5a-934f-90e3ec9d2aa1",
"amount": 50,
"status": "pending"
}
]
}
Now I want to update the pending donation to collected.
This would be
Action.findOneAndUpdate(
{
_id: '6098fb22101f22cfcbd31e3b',
'donations.id': '215237bd-bfe6-4d5a-934f-90e3ec9d2aa1',
},
{
$set: {
'donations.$.status': 'collected',
},
},
{
upsert: false,
returnOriginal: false,
}
).then((action) => console.log(action);
I want to update the status to collected, but also update the collected so that it is the same as all the donations with status equal to collected. I thought of using the $inc operator, but this keeps saying that donations.$.amount is not a number and therefore not able to increment collected.
Is there a way to do this in the same update call? The reason why I cannot get the object and just count collected amount is that maybe two donation callbacks occur at the same time, so we don't want the to overwrite the previous given amount.
This aggregation can help you I believe:
db.collection.aggregate([
{
"$match": {
_id: "6098fb22101f22cfcbd31e3b"
}
},
{
"$set": {
"donations.status": {
"$reduce": {
"input": "$donations",
"initialValue": {
uuid: "215237bd-bfe6-4d5a-934f-90e3ec9d2aa1"
},
"in": {
$cond: [
{
$eq: [
"$$this.uuid",
"$$value.uuid"
]
},
"collected",
"$$this.status"
]
}
}
}
}
},
{
"$set": {
"collected": {
"$reduce": {
"input": "$donations",
"initialValue": "$collected",
"in": {
$cond: [
{
$eq: [
"$$this.status",
"collected"
]
},
{
$sum: [
"$$value",
"$$this.amount"
]
},
"$$value"
]
}
}
}
}
}
])
Edit: Above aggregation wasn't properly update status field to "collected" dunno why..
But update query below should work. I couldn't test it too. So, please let me know if something goes wrong.
db.collection.update({
"_id": "6098fb22101f22cfcbd31e3b"
},
{
"$set": {
"donations.$[element].status": "collected",
"$inc": {
"donations.$[element].amount": {
"$cond": [
{
"$eq": [
"donations.$[element].status",
"collected"
]
},
"donations.$[element].amount",
"collected"
]
}
}
}
},
{
"arrayFilters": [
{
"element.uuid": "215237bd-bfe6-4d5a-934f-90e3ec9d2aa1"
}
]
})

Pagination in aggregation and also used $project in mongoose

i want to show user list of object with projection and also want to show the total page i want output like this
{
"Success": true,
"message": " Fetched post comment successfully.",
"data": {
"docs": [
{
"_id": "60101fcc4077e698facd63aa",
"commentDetail": {
"hasReply": false,
"likeCount": 0,
"angryCount": 0,
"favCount": 0,
"totalReaction": 0,
"description": "four comment",
"media": null,
"date": "2021-01-26T13:57:32.220Z",
"action": [],
"commentReplies": []
},
"userID": "5f5238b5458b7c63a477bf87",
"postID": "5fb7a19bcae255415e99781b",
"commentID": "60101fcb4077e698facd63a9",
},
{
"_id": "60101fcc4077e698facd63aa",
"commentDetail": {
"hasReply": false,
"likeCount": 0,
"angryCount": 0,
"favCount": 0,
"totalReaction": 0,
"description": "four comment",
"media": null,
"date": "2021-01-26T13:57:32.220Z",
"action": [],
"commentReplies": []
},
"userID": "5f5238b5458b7c63a477bf87",
"postID": "5fb7a19bcae255415e99781b",
"commentID": "60101fcb4077e698facd63a9",
}
],
"count": 1
}
}
i write this query
getPostAllComment = await this.comment.aggregate([
{ $match: { postID: ObjectId(getPostCommentDTO.postID) } },
{ $sort: { createdAt: 1 } }, //sort on created At
{ $skip: (parseInt(getPostCommentDTO.pageNum) - 1) * parseInt(getPostCommentDTO.pageSize) },
{ $limit: parseInt(getPostCommentDTO.pageSize) },
{
$group: {
_id: "$postID",
docs: { $push: "$$ROOT" },
count: { $sum: 1 },
},
},
{ $project: { _id: 0 }
}
it give me output like above which i am expecting but my $project is not working here.so how can i show output exactly like above with total page count field at root level not in each document with projection.i already tried to used $facet but it not give me output like i above explain
I get my desire result by this query
getPostAllComment = await this.comment.aggregate([
{ $match: { postID: ObjectId(getPostCommentDTO.postID) } },
{
$group: {
_id: null,
collection: { $push: "$$ROOT" },
count: { $sum: 1 },
},
},
{ $unwind: "$collection" },
{ $sort: { createdAt: 1 } }, //sort on created At
{ $skip: (parseInt(getPostCommentDTO.pageNum) - 1) * parseInt(getPostCommentDTO.pageSize) },
{ $limit: parseInt(getPostCommentDTO.pageSize) },
{
$project: {
hasReply: "$collection.commentDetail.hasReply",
likeCount: "$collection.commentDetail.likeCount",
angryCount: "$collection.commentDetail.angryCount",
favCount: "$collection.commentDetail.favCount",
totalReaction: "$collection.commentDetail.totalReaction",
date: "$collection.commentDetail.date",
media: "$collection.commentDetail.media",
description: "$collection.commentDetail.description",
action: "$collection.commentDetail.action",
recentCommentReplies: "$collection.commentDetail.commentReplies",
userProfilePicture: "$collection.userProfilePicture",
userProfilePictureThumbnail: "$collection.userProfilePictureThumbnail",
userName: "$collection.userName",
userID: "$collection.userID",
postID: "$collection.postID",
commentID: "$collection.commentID",
createdAt: "$collection.createdAt",
updatedAt: "$collection.updatedAt",
count: "$count",
},
},
{
$group: {
_id: "$postID",
docs: { $push: "$$ROOT" },
total: { $first: "$count" },
},
},
{ $project: { "docs.count": 0, _id: 0 } },
])
return getPostAllComment[0]

Calculate transaction items total's and transaction item with mongoose query

In mongoose, I have a collection for transactions. Each transaction has a list of Items Something like this:
var transactionItemSchema = new mongoose.Schema({
productId: String,
quantity: Number,
price: Number
});
var transactionSchema = new mongoose.Schema({
details: String,
items: [transactionItemSchema ],
}, {
timestamps: true
});
I need to calculate each item total's value by multiplying price * quantity and rounding 2 decimals, but also I need to get the transaction total by summing all item total's in a transaction. So for example if I have this transactions in mongo:
[{
details: 'First Transaction',
items: [{
price: 5.2,
quantity: 2
}, {
price: 4,
quantity: 3
}]
}, {
details: 'First Transaction',
items: [{
price: 0.333,
quantity: 3
}]
}]
return something like this when pulling transactions:
[{
total: 22.40,
details: 'First Transaction',
items: [{
price: 5.2,
quantity: 2,
total: 10.40
}, {
price: 4,
quantity: 3,
total: 12.00
}]
}, {
total: 1.00,
details: 'Second Transaction',
items: [{
price: 0.333,
quantity: 3,
total: 1.00
}]
}]
Is there a way we can achieve this with some aggregations with mongoose?
You want $map and $multiply here.
Assuming the model is calls Transaction:
Transaction.aggregate([
{ "$addFields": {
"items": {
"$map": {
"input": "$items",
"in": {
"$mergeObjects": [
"$$this",
{ "total": { "$round": [{ "$multiply": [ "$$this.price", "$$this.quantity" ] }, 2] } }
]
}
}
}
}}
])
Or without $mergeObjects:
Transaction.aggregate([
{ "$addFields": {
"total": {
"$sum": {
"$map": {
"input": "$items",
"in": {
"$round": [{ "$multiply": [ "$$this.price", "$$this.quantity" ] }, 2]
}
}
}
},
"items": {
"$map": {
"input": "$items",
"in": {
"price": "$$this.price",
"quantity": "$$this.quantity",
"total": { "$round": [{ "$multiply": [ "$$this.price", "$$this.quantity" ] }, 2] }
}
}
}
}}
])
The $map operator is essentially used for array transforms, in which you provide an array of input and an expression to apply to each array element which defines the object output for each element. Here $multiply is applied with the two arguments to "multiply" for a result.
The $mergeObjects is optionally used as a way to take the existing object properties for each element ( price and quantity ) and include them in the output object. The alternative is to manually specify the properties in the output objects for each element, just as shown.
Of course for the document total the same is essentially supplied, but just returning a single value and feeding that to the $sum operator in order to "total" the results
All of that said, there's nothing wrong with simply manipulating the result post return from the server:
let results = await Transaction.find().lean();
// Then manipulate the items arrays
results = results.map(r =>
({
...r,
total: r.items.reduce((o, i) =>
o + parseFloat((i.price * i.quantity).toFixed(2)), 0),
items: r.items.map(i =>
({ ...i, total: parseFloat((i.price * i.quantity).toFixed(2)) })
)
})
);
Simply note the use of lean() here, which returns plain JavaScript objects rather than Mongoose Documents and thus allows you to manipulate the structure of the returned results.
Here's a full listing of both approaches:
const { Schema } = mongoose = require('mongoose');
const uri = 'mongodb://localhost:27017/test';
const opts = { useNewUrlParser: true, useUnifiedTopology: true };
mongoose.Promise = global.Promise;
mongoose.set('debug', true);
mongoose.set('useCreateIndex', true);
mongoose.set('useFindAndModify', false);
const transactionItemSchema = new Schema({
productId: String,
quantity: Number,
price: Number
});
const transactionSchema = new Schema({
details: String,
items: [transactionItemSchema]
},{
timestamps: true
});
const Transaction = mongoose.model('Transaction', transactionSchema);
const initialData = [
{
details: 'First Transaction',
items: [
{ price: 5.2, quantity: 2 },
{ price: 4, quantity: 3 }
]
},
{
details: 'Second Transaction',
items: [
{ price: 0.333, quantity: 3 }
]
}
];
const log = data => console.log(JSON.stringify(data, undefined, 2));
(async function() {
try {
const conn = await mongoose.connect(uri, opts);
// Clean data
await Promise.all(
Object.values(conn.models).map(m => m.deleteMany())
);
await Transaction.insertMany(initialData);
// Aggregate example
let result1 = await Transaction.aggregate([
{ "$addFields": {
"total": {
"$sum": {
"$map": {
"input": "$items",
"in": {
"$round": [
{ "$multiply": [ "$$this.price", "$$this.quantity" ] },
2
]
}
}
}
},
"items": {
"$map": {
"input": "$items",
"in": {
"$mergeObjects": [
"$$this",
{ "total": {
"$round": [
{ "$multiply": [ "$$this.price", "$$this.quantity" ] },
2
]
}}
]
}
}
}
}}
]);
log({ result1 });
// Plain JavaScript example
let result2 = await Transaction.find().lean();
result2 = result2.map(r =>
({
...r,
total: r.items.reduce((o, i) =>
o + parseFloat((i.price * i.quantity).toFixed(2)), 0),
items: r.items.map(i =>
({ ...i, total: parseFloat((i.price * i.quantity).toFixed(2)) })
)
})
);
log({ result2 });
} catch (e) {
console.error(e);
} finally {
mongoose.disconnect();
}
})();
And the output:
Mongoose: transactions.deleteMany({}, {})
Mongoose: transactions.insertMany([ { _id: 5d8f4dfcaf9f6a2f8ec28039, details: 'First Transaction', items: [ { _id: 5d8f4dfcaf9f6a2f8ec2803b, price: 5.2, quantity: 2 }, { _id: 5d8f4dfcaf9f6a2f8ec2803a, price: 4, quantity: 3 } ], __v: 0, createdAt: 2019-09-28T12:11:40.060Z, updatedAt: 2019-09-28T12:11:40.061Z }, { _id: 5d8f4dfcaf9f6a2f8ec2803c, details: 'Second Transaction', items: [ { _id: 5d8f4dfcaf9f6a2f8ec2803d, price: 0.333, quantity: 3 } ], __v: 0, createdAt: 2019-09-28T12:11:40.062Z, updatedAt: 2019-09-28T12:11:40.062Z } ], {})
Mongoose: transactions.aggregate([ { '$addFields': { total: { '$sum': { '$map': { input: '$items', in: { '$round': [ { '$multiply': [ '$$this.price', '$$this.quantity' ] }, 2 ] } } } }, items: { '$map': { input: '$items', in: { '$mergeObjects': [ '$$this', { total: { '$round': [ { '$multiply': [Array] }, 2 ] } } ] } } } } } ], {})
{
"result1": [
{
"_id": "5d8f4dfcaf9f6a2f8ec28039",
"details": "First Transaction",
"items": [
{
"_id": "5d8f4dfcaf9f6a2f8ec2803b",
"price": 5.2,
"quantity": 2,
"total": 10.4
},
{
"_id": "5d8f4dfcaf9f6a2f8ec2803a",
"price": 4,
"quantity": 3,
"total": 12
}
],
"__v": 0,
"createdAt": "2019-09-28T12:11:40.060Z",
"updatedAt": "2019-09-28T12:11:40.061Z",
"total": 22.4
},
{
"_id": "5d8f4dfcaf9f6a2f8ec2803c",
"details": "Second Transaction",
"items": [
{
"_id": "5d8f4dfcaf9f6a2f8ec2803d",
"price": 0.333,
"quantity": 3,
"total": 1
}
],
"__v": 0,
"createdAt": "2019-09-28T12:11:40.062Z",
"updatedAt": "2019-09-28T12:11:40.062Z",
"total": 1
}
]
}
Mongoose: transactions.find({}, { projection: {} })
{
"result2": [
{
"_id": "5d8f4dfcaf9f6a2f8ec28039",
"details": "First Transaction",
"items": [
{
"_id": "5d8f4dfcaf9f6a2f8ec2803b",
"price": 5.2,
"quantity": 2,
"total": 10.4
},
{
"_id": "5d8f4dfcaf9f6a2f8ec2803a",
"price": 4,
"quantity": 3,
"total": 12
}
],
"__v": 0,
"createdAt": "2019-09-28T12:11:40.060Z",
"updatedAt": "2019-09-28T12:11:40.061Z",
"total": 22.4
},
{
"_id": "5d8f4dfcaf9f6a2f8ec2803c",
"details": "Second Transaction",
"items": [
{
"_id": "5d8f4dfcaf9f6a2f8ec2803d",
"price": 0.333,
"quantity": 3,
"total": 1
}
],
"__v": 0,
"createdAt": "2019-09-28T12:11:40.062Z",
"updatedAt": "2019-09-28T12:11:40.062Z",
"total": 1
}
]
}

Two queries in FindOneAndUpdate don't work with arrays

I have this schema in Mongoose:
var CoinAmountSchema = new Schema(
{
user: [{ type: Schema.ObjectId, ref: 'User' }],
coinAmounts: [{
_id: false,
coinID: { type: Number, ref: 'Coin' },
amount: Number
}]
})
I am writing this query, that checks the userID and coinID and should update the amount of only that coinID's amount.
exports.coin_amount_update = [
(req, res, next) => {
CoinAmount.update({
"user": req.params.userId,
"coinAmounts.coinID": req.params.coinId
},
{
'$set': {
'coinAmounts.$.amount': req.body.amount
}
},
function (err, model) {
if (err) {
console.log(err)
return res.send(err)
}
return res.json(model)
})
}]
But like this, it only updates the first coin's in the array amount. BUT, if I delete the line "user": req.params.userId, it would find and update the right coin. I need to check for a user as well though, so how can I make it work?
Is there something wrong with the query or the way the data is structured?
EDIT: I send a request in React-native:
fetch(`${apiBaseURL}/users/${getState().user._id}/coins/${id}/update`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount: getState().coins[id].amount
}),
})
If the request is /users/:userID/coins/0/update (with amount: 1)
then the result will be
{ _id: 5a579d0d44e7390ba3029327,
__v: 0,
coinAmounts:
[ { coinID: 0, amount: 1 },
{ coinID: 1, amount: 0 },
{ coinID: 2, amount: 0 },
{ coinID: 3, amount: 0 },
{ coinID: 4, amount: 0 } ],
user: [ 5a579d0d44e7390ba3029326 ] }
The same result if the request is /users/:userID/coins/1/update with the same amount.
But if as mentioned before, I remove the check for userID, the request /users/:userID/coins/1/update would produce this:
{ _id: 5a579d0d44e7390ba3029327,
__v: 0,
coinAmounts:
[ { coinID: 0, amount: 0 },
{ coinID: 1, amount: 1 },
{ coinID: 2, amount: 0 },
{ coinID: 3, amount: 0 },
{ coinID: 4, amount: 0 } ],
user: [ 5a579d0d44e7390ba3029326 ] }
Hope I was clear.
It looks like a bug when using two arrays in find with $ positional update, it gets matching index of user for $ positional update
tried below workarounds, both updates correct coinID
workaround-1, using arrayFilters
db.coins.update(
{ "user" : "5a579d0d44e7390ba3029326" }, //user
{
$set: { "coinAmounts.$[elem].amount" : 1 } //update
},
{
multi: false,
arrayFilters: [
{ "elem.coinID": 2 } //coinID
]
}
)
workaround-2, using elemMatch for user array
db.coins.update(
{
"coinAmounts.coinID" : 1, //coinID
"user" : { $elemMatch : { $eq : "5a579d0d44e7390ba3029326" } } //user
},
{ $set : { "coinAmounts.$.amount" : 1 } } //update
)

Resources