Mongoose elemMatch and inc query based on id - mongodb

How am I going to write mongoose function that will query this mongo query based on a given userId (I could not add this to query, also need that).
db.users.update({ categories : { $elemMatch: { name: "Sport" } } },
{$inc: {"categories.$.points": 6666, points : 7777}})
That doesn't help me out.
User.findByIdAndUpdate(
request.params.userId,
//{ $inc: { points: 1 }},
{ categories : { $elemMatch: { name: "Spor" }}},
{$inc: {"categories.$.points": 6666, points : 7777}},
//{ safe: true, upsert: true, new : true },
function(err, user) {
if (!err) {
return reply(user); // HTTP 201
}
if (11000 === err.code || 11001 === err.code) {
return reply(Boom.forbidden("please provide another user id, it already exist!"));
}
return reply(Boom.forbidden(err)); // HTTP 403
}
);

You need to use findOneAndUpdate instead of findByIdAndUpdate if you want to match on anything beyond just the _id. You also don't need to use $elemMatch here as you're only matching against a single field in the categories array so a simple dot notation field match can be used instead.
So it should be:
User.findOneAndUpdate(
{ _id: request.params.userId, "categories.name": "Sport" },
{ $inc: { "categories.$.points": 6666, points : 7777 }},
{ safe: true, upsert: true, new: true },
function(err, user) { ... }
);

Related

How to increment filed in mongodb mongoose

const options = {
$addToSet: { whoLikes: userId },
$inc: { likesCount: 1 },
new: true,
};
collection.findByIdAndUpdate({ _id: postId }, options)
What I want is increment likesCount only if whoLikes array length is get incremented.
Right now likesCount incrementing all the time doesn't matter how many objects inside whoLikes array.
I'm using mongoose, node.js
use findOneAndUpdate() method
Just check condition in your query part whoLikes: { $ne: userId } userId should not inside whoLikes array
user $push instead of $addToSet
other options should be in third parameter new:true
const options = {
$push: { whoLikes: userId },
$inc: { likesCount: 1 }
};
collection.findOneAndUpdate(
{
_id: postId,
whoLikes: { $ne: userId }
},
options,
{ new: true }
)
Ex1: Add UserID that is not present
Playground
Ex2: Add UserID that is already present
Playground

Update a document and upsert a subdocument in a single query

How do I update an item in the parent document and upsert a subdocument in a single query?
This is my example schema.
const ExampleSchema = new Schema({
user_count: {
type: String,
default: 0
},
users: [
{
id: {
type: Schema.Types.ObjectId,
ref: "users",
unique: true
},
action: {
type: Boolean
}
}
],
});
I am trying to add +1 to user_count and upsert a document to the users array in a single query.
const result = await Example.updateOne(
{
_id: id,
},
{
$set: {
"user_count": user_count++,
"users.$.id": req.user.id,
"users.$.action": true
}
},
{ upsert: true }
);
I have tried the above code, but got the following error.
[0] 'The positional operator did not find the match needed from the query.',
[0] [Symbol(mongoErrorContextSymbol)]: {} }
I'm not familiar with mongoose, so I will take for granted that "user_count": user_count++ works.
For the rest, there are two things that won't work:
the $ operator in "users.$.id": req.user.id, is known as the positional operator, and that's not what you want, it's used to update a specific element in an array. Further reading here: https://docs.mongodb.com/manual/reference/operator/update/positional/
the upsert is about inserting a full document if the update does not match anything in the collection. In your case you just want to push an element in the array right?
In this case I guess something like this might work:
const result = await Example.updateOne(
{
_id: id,
},
{
$set: {
"user_count": user_count++
},
$addToSet: {
"users": {
"id": req.user.id,
"action": true
}
}
}
);
Please note that $push might also do the trick instead of $addToSet. But $addToSet takes care of keeping stuff unique in your array.
db.collection.findOneAndUpdate({_id: id}, {$set: {"user_count": user_count++},$addToSet: {"users": {"id": req.user.id,"action": true}}}, {returnOriginal:false}, (err, doc) => {
if (err) {
console.log("Something wrong when updating data!");
}
console.log(doc);
});

$inc not working mongoDB

I have a MongoDB data like this
{
_id: "5aa8f087e1eee70004a99e1d"
users: [{facebookId: "-1", unread: 0},{facebookId: "323232321", unread: 1}]
}
I want to increment users.unread where facebookId in not "-1"
I tried this query
chat.update({ "_id": { "$in": chatId }, "users.facebookId": { "$ne": "-1" } },{"$inc": { "users.$.unread": 1 } }, { multi: true, upsert: true }, function(err, data) {
if (err) throw err;
console.log(data);
});
From MongoDB docs:
If the query matches the array using a negation operator, such as $ne, $not, or $nin, then you cannot use the positional operator to update values from this array.
However, if the negated portion of the query is inside of an $elemMatch expression, then you can use the positional operator to update this field
Below code should work if you want to increment first matching element:
db.chat.update({ "_id":"5aa8f087e1eee70004a99e1d", "users": { $elemMatch: { "facebookId": { "$ne": "-1" } }}},{"$inc": { "users.$.unread": 1 }})
Otherwise you can use array filters in MongoDB 3.6
db.col.update({ "_id":"5aa8f087e1eee70004a99e1d" }, { "$inc" : { "users.$[cond].unread" : 1 } }, { arrayFilters: [ { "cond.facebookId": { $ne: "-1" } } ] })

Mongoose unique index on subdocument

Let's say I have a simple schema:
var testSchema = new mongoose.Schema({
map: { type: [ mongoose.Schema.Types.Mixed ], default: [] },
...possibly something else
});
Now let's ensure that pairs (_id, map._id) are unique.
testSchema.index({ _id: 1, 'map._id': 1 }, { unique: true });
Quick check using db.test.getIndexes() shows that it was created.
{
"v" : 1,
"unique" : true,
"key" : {
"_id" : 1,
"map._id" : 1
},
"name" : "_id_1_map._id_1",
"ns" : "test.test",
"background" : true,
"safe" : null
}
The problem is, this index is ignored and I can easily create multiple subdocuments with the same map._id. I can easily execute following query multiple times:
db.maps.update({ _id: ObjectId("some valid id") }, { $push: { map: { '_id': 'asd' } } });
and end up with following:
{
"_id": ObjectId("some valid id"),
"map": [
{
"_id": "asd"
},
{
"_id": "asd"
},
{
"_id": "asd"
}
]
}
What's going on here? Why can I push conflicting subdocuments?
Long story short: Mongo doesn't support unique indexes for subdocuments, although it allows creating them...
This comes up in google so I thought I'd add an alternative to using an index to achieve unique key constraint like functionality in subdocuments, hope that's OK.
I'm not terribly familiar with Mongoose so it's just a mongo console update:
var foo = { _id: 'some value' }; //Your new subdoc here
db.yourCollection.update(
{ '_id': 'your query here', 'myArray._id': { '$ne': foo._id } },
{ '$push': { myArray: { foo } })
With documents looking like:
{
_id: '...',
myArray: [{_id:'your schema here'}, {...}, ...]
}
The key being that you ensure update will not return a document to update (i.e. the find part) if your subdocument key already exists.
First objectId length in mongodb must be 24. Then you can turn off _id, and rename _id as id or others,and try $addToSet. Good luck.
CoffeeScript example:
FromSchema = new Schema(
source: { type: String, trim: true }
version: String
{ _id: false }//to trun off _id
)
VisitorSchema = new Schema(
id: { type: String, unique: true, trim: true }
uids: [ { type: Number, unique: true} ]
from: [ FromSchema ]
)
//to update
Visitor.findOneAndUpdate(
{ id: idfa }
{ $addToSet: { uids: uid, from: { source: source, version: version } } }
{ upsert: true }
(err, visitor) ->
//do stuff

Update multiple documents by id set. Mongoose

I wonder if mongoose has some method to update multiple documents by id set. For example:
for (var i = 0, l = ids.length; i < l; i++) {
Element.update({'_id': ids[i]}, {'visibility': visibility} ,function(err, records){
if (err) {
return false;
} else {
return true;
};
});
};
What i want to know, that if mongoose can do something like this:
Element.update({'_id': ids}, {'visibility': visibility}, {multi: true} ,function(err, records){
if (err) {
return false;
}
});
where ids is an array of ids, like ['id1', 'id2', 'id3'] - sample array.
Same question for find.
Most probably yes. And it is called using $in operator in mongodb query for update.
db.Element.update(
{ _id: { $in: ['id1', 'id2', 'id3'] } },
{ $set: { visibility : yourvisibility } },
{multi: true}
)
All you need is to find how to implement $in in mongoose.
in updateMany function no need of { multi: true }
db.collectionName.updateMany(
{
_id:
{
$in:
[
ObjectId("your object id"),
ObjectId("your object id")
]
}
},
{
$inc: { quantity: 100 }
})
I want to add one more point, you can use $in to fetch multiple document
db.collectionName.find(
{
_id:
{
$in:
[
ObjectId("your object id"),
ObjectId("your object id")
]
}
})
Updates all documents that match the specified filter for a collection.
let ids = ["kwwe232244h3j44jg3h4", "23h2u32g2h3b3hbh", "fhfu3h4h34u35"];
let visibility = true;
Element.updateMany({_id: {$in: ids}},
{ $set: { visibility } },
{multi: true} ,
function(err, records){
if (err) {
return false;
}
});
know more