The matching element looks like that:
{
"_id": {
"$oid": "519ebd1cef1fce06f90e3157"
},
"from": "Tester2",
"to": "Tester",
"messages": [
{
"username": "Tester2",
"message": "heeey",
"read": false
},
{
"username": "Tester",
"message": "hi!",
"read": false
},
{
"username": "Tester2",
"message": "test",
"read": false
}
],
}
Now I try to set the read property to the current date just of the subelements where the username is not equal to Tester:
var messages = db.collection('messages');
messages.update(
{
_id: new BSON.ObjectID("519ebd1cef1fce06f90e3157"),
messages: {
$elemMatch: { username: { $ne: "Tester" } }
}
},
{ $set: { 'messages.$.read': new Date() } },
{ multi: true }, function(error, result) {
console.log(error);
console.log(result);
});
But just the first messages subelement read property updates:
{
"_id": {
"$oid": "519ebd1cef1fce06f90e3157"
},
"from": "Tester2",
"to": "Tester",
"messages": [
{
"username": "Tester2",
"message": "heeey",
"read": {
"$date": "2013-01-01T00:00:00.000Z"
}
},
{
"username": "Tester",
"message": "hi!",
"read": false
},
{
"username": "Tester2",
"message": "test",
"read": false
}
],
}
What's wrong with the code?
I'm using node.js v0.10.8 and MongoDB v2.4.3 together with node-mongodb-native.
There's nothing wrong with your code; the $ operator contains the index of the first matching array element. The {multi: true} option only makes the update apply to multiple documents, not array elements. To update multiple array elements in a single update you must specify them by numeric index.
So you'd have to do something like this:
messages.update(
{
_id: new BSON.ObjectID("519ebd1cef1fce06f90e3157")
},
{ $set: {
'messages.0.read': new Date(),
'messages.2.read': new Date()
} },
function (err, result) { ... }
);
This is similar to question: How to Update Multiple Array Elements in mongodb
var set = {}, i, l;
for(i=0,l=messages.length;i<l;i++) {
if(messages[i].username != 'tester') {
set['messages.' + i + '.read'] = new Date();
}
}
.update(objId, {$set:set});
I think ArrayFilters can be used in this case
I know it's a little bit late to answer this question but if you use the $[] operator, it will do the trick.
more details here
https://www.mongodb.com/docs/manual/reference/operator/update/positional-all/
I was able to use it inside an updateMany operation, but it should work also for update
var messages = db.collection('messages');
messages.update(
{
_id: new BSON.ObjectID("519ebd1cef1fce06f90e3157"),
messages: {
$elemMatch: { username: { $ne: "Tester" } }
}
},
{ $set: { 'messages.$[].read': new Date() } },
{ multi: true }, function(error, result) {
console.log(error);
console.log(result);
});
Related
I have a data from MongoDB where I'm getting all the data of the agent, and I want to get the testimonials with isDisplayed is true only. Is there a way in ExpressJS & MongoDB where I can filter the testimonial key?
Here's what I tried
const getSingleAgent = expressAsync(async (req, res) => {
const agent = await Agents.findOne({
_id: req.params.id,
});
res.json(agent);
} else {
res.status(404);
throw new Error("Agent not found.");
}
});
Actual Result
{
"_id": "63ea901a85d4fbd62fb887b3",
"name": "test namee",
"isDeclined": false,
"testimonials": [
{
"isDisplayed": true,
"name": "test123123123123123",
},
{
"isDisplayed": false,
"name": "test123123123123123",
},
{
"isDisplayed": false,
"name": "test#gmail.com",
},
],
}
Expected Result
{
"_id": "63ea901a85d4fbd62fb887b3",
"name": "test namee",
"isDeclined": false,
"testimonials": [
{
"isDisplayed": true,
"name": "test123123123123123",
},
],
}
You can use $elemMatch into a projection stage like this:
db.collection.find({
"_id": "63ea901a85d4fbd62fb887b3",
"testimonials.isDisplayed": true
},
{
"name": 1,
"isDeclined": 1,
"testimonials": {
"$elemMatch": {
"isDisplayed": true
}
}
})
Example here
But be careful. using $elemMatch will only return the first match. Look at this example where there are two true but only one is returned.
As explained into docs:
The $elemMatch operator limits the contents of an field from the query results to contain only the first element matching the $elemMatch condition.
So if you have or want more than one value you can use and aggregation pipelina and $filter into a $project stage:
db.collection.aggregate([
{
"$match": {
"_id": "63ea901a85d4fbd62fb887b3",
"testimonials.isDisplayed": true
}
},
{
"$project": {
"name": 1,
"isDeclined": 1,
"testimonials": {
"$filter": {
"input": "$testimonials",
"cond": {
"$eq": [
"$$this.isDisplayed",
true
]
}
}
}
}
}
])
Example here
Note how in this example if exists two true values, both are displayed.
I have a document that looks something like this
{
"_id":{
"$oid":"id"
},
"side1":[
{
"username":"test1",
"id":"id1",
},
{
"username":"test2",
"id":"id2",
},
{
"username":"test3",
"id":"id3",
}
],
"side2":[
{
"username":"test4",
"id":"id4",
},
{
"username":"test5",
"id":"id5",
},
{
"username":"test6",
"id":"id6",
}
],
}
I want to be able to search and update one of the sides, for example, if I searched with username for side1 and that username would be there then I would be able to $set other fields for the object with this username. Something like: Search side1 username test1: $set result.id: "43242342" this would set the id of the object with the username of test1 to 43242342. I am not sure on how I would go about doing this, I have tried using $elemMatch but that didn't bring any results.
Test.findOne({ id: id },
{ side1: { $elemMatch: {username: username} } }, function (err, result) {
if (err) {
console.log(err);
} else {
console.log(result)
}
});
I'm not exactly sure how you want to update the document, but perhaps this is what you are looking for?
db.collection.update({
"_id": ObjectId("000000000000000000000001"),
"side1.username": "test1"
},
{
"$set": {
"side1.$.id": "43242342"
}
})
Try it on mongoplayground.net.
Example updated document:
[
{
"_id": ObjectId("000000000000000000000001"),
"side1": [
{
"id": "43242342",
"username": "test1"
},
{
"id": "id2",
"username": "test2"
},
{
"id": "id3",
"username": "test3"
}
],
"side2": [
{
"id": "id4",
"username": "test4"
},
{
"id": "id5",
"username": "test5"
},
{
"id": "id6",
"username": "test6"
}
]
}
]
Could you do something like this?
function addProperty(id, side, username, property, value) {
const query = {
_id: {
$oid: id,
},
};
const update = {
$push: {
[side]: {
username: username,
[property]: value,
},
},
};
const options = {
upsert: true,
};
db.collection("Test").updateOne(query, update, options);
}
I am building an API to store friends names for a game, I have built the API to receive the post request as so :
exports.addFriends = async (req, res) => {
try {
console.log('hit');
console.log(req.body.friendNames);
const addUser = await User.updateOne(
{ uniqueid: req.body.uniqueid },
{ $push: { friendNames: [req.body.friendNames] } }
);
res.json({
addUser
});
} catch (error) {
console.log(error);
}
};
ad the post request as
const friends = await axios.post('/api/v1/users/add/friends', {
uniqueId: this.uniqueid,
friendNames: [
{
userName: 'test',
region: 'euw'
}
]
});
My API is being hit as a see the logs, but no record is made. My User Schema is as so
const userSchema = new mongoose.Schema({
uniqueid: {
type: String,
required: true,
trim: true
},
summonerName: {
type: String
},
friendNames: [
{
userName: String,
region: String
}
]
});
I get no error and the request seems to go through, but no records are added. Any ideas?
$push is used to add one element to the array. But using the $each array update operator, we can push an array of items.
Also, I used findOneAndUpdate with new:true option to retrieve the updated document, because updateOne doesn't return the updated document.
exports.addFriends = async (req, res) => {
try {
console.log(req.body.friendNames);
const addUser = await User.findOneAndUpdate(
{ uniqueid: req.body.uniqueid },
{ $push: { friendNames: { $each: req.body.friendNames } } },
{ new: true }
);
res.json({ addUser });
} catch (error) {
console.log(error);
res.status(500).send("Something went wrong");
}
}
Let's say we have this existing document:
{
"_id": "5e31c749f26d5f242c69f3aa",
"uniqueid": "uniqueid1",
"summonerName": "John",
"friendNames": [
{
"_id": "5e31c749f26d5f242c69f3ab",
"userName": "Max",
"region": "Germany"
}
],
"__v": 0
}
Let's send a request to the controller with this request body:
{
"uniqueid": "uniqueid1",
"friendNames": [
{
"userName": "Andrew",
"region": "England"
},
{
"userName": "Smith",
"region": "USA"
}
]
}
The response will be like this:
{
"addUser": {
"_id": "5e31c749f26d5f242c69f3aa",
"uniqueid": "uniqueid1",
"summonerName": "John",
"friendNames": [
{
"_id": "5e31c749f26d5f242c69f3ab",
"userName": "Max",
"region": "Germany"
},
{
"_id": "5e31c763f26d5f242c69f3ad",
"userName": "Andrew",
"region": "England"
},
{
"_id": "5e31c763f26d5f242c69f3ac",
"userName": "Smith",
"region": "USA"
}
],
"__v": 0
}
}
{
"_id": {
"$oid": "5a4e5b1d09fb590058bfdf86"
},
"name": "ProjectStore",
"imageURL": "none",
"longitude": 0,
"latitude": 0,
"rating": 5,
"leads": [
{
"customerId": "5a0c57db65a4931768716566",
"customerName": "testuser",
"interested": "testuser",
"_id": {
"$oid": "5a4e5b5409fb590058bfdf88"
}
}
],
"items": [
{
"name": "chat",
"categoryID": "5a0c2d292235680012bd12c9",
"semiCatID": "5a0c2d5f2235680012bd12cb",
"_id": {
"$oid": "5a4e5b3009fb590058bfdf87"
}
}
],
"__v": 2
}
I added my DB log, I try to write query that will let me update/push information to "LeadStatus".
This varible should be inside each object in the "leads" array.
I have the keys for the main id "5a4e5b1d09fb590058bfdf86"
I have the second key for the specific lead "5a4e5b5409fb590058bfdf88"
I just dont know how to write the query, for now i got this.... and got error.
Store.update(
{ _id: req.body.store._id, 'leads._id': 'req.body.lead._id', },
{ $set: { 'leads.$.LeadStatus': 'open' }, },
(err, result) => {
if (err) {
res.status(500)
.json({ error: 'Unable to update leads.', });
} else {
res.status(200)
.json(result);
}
}
);
Please Help,
Thanks.
I'm assuming you want the updated doc returned (you can choose to not do so as well, switch option new to false). You have to assign an object the value that you're updating and provide all the fields within the subfield you're updating, otherwise it will remove everything else. That's why you have to do a findOne first:
return Store.findOne({ '_id': "5a4e5b1d09fb590058bfdf86" })
.then((store) => {
if (!store) { // patient was not found for some reason
throw new Error(`Store id was not found`);
}
const options = {
new: true,
};
const updatedStore = Object.assign({}, store.toObject(), {
leads: [
{
"leadStatus": "open",
"customerId": "5a0c57db65a4931768716566",
"customerName": "testuser",
"interested": "testuser",
"_id": {
"$oid": "5a4e5b5409fb590058bfdf88"
}
}
],
});
return Store.findOneAndUpdate(
{ '_id': req.body.store._id },
{ $set: updatedStore },
options);
});
In case you're curious, you can build pre/virtual hooks off of findOneAndUpdate (no documentation for this), you won't lose anything from not using update.
I've got mongoose schema like this:
var mySchema = new mongoose.Schema({
user: String,
photos: [{
url: String,
thumbnail: String,
time: Date,
mainPhoto: Boolean
}]
});
now, I'd like to make a "setMainPhoto" function - for given id - set mainPhoto flag to true, and set it to false for other photos.
There is a document like this:
{
"_id": {
"$oid": "56269dea07a455920a21bca7"
},
"user": "someUser",
"photos": [
{
"time": {
"$date": "2015-05-25T08:37:56.000Z"
},
"thumbnail": "https://s3-eu-west-1.amazonaws.com/...jpg",
"url": "https://s3-eu-west-1.amazonaws.com/...jpg",
"_id": {
"$oid": "56269e2e07a455920a21bcab"
},
"mainPhoto": false
},
{
"time": {
"$date": "2015-05-25T09:27:17.000Z"
},
"thumbnail": "https://s3-eu-west-1.amazonaws.com/...jpg",
"url": "https://s3-eu-west-1.amazonaws.com/...jpg",
"_id": {
"$oid": "56269e3507a455920a21bcae"
},
"mainPhoto": true
},
{
"time": {
"$date": "2015-05-25T09:27:17.000Z"
},
"thumbnail": "https://s3-eu-west-1.amazonaws.com/...jpg",
"url": "https://s3-eu-west-1.amazonaws.com/...jpg",
"_id": {
"$oid": "56269e3507a455920a21bcaf"
},
"mainPhoto": false
}
]}
I'm trying to do this with mongoose save function:
var myModel = mongoose.model('myCollection', mySchema);
myModel.find({user:params.user,_id:params.documentId}, function(err,data){
var newMyModel = new myModel(data[0]);
for(var i=0; i<newMyModel.photos.length; i++){
if(newMyModel.photos[i]._id == params.photoId){
newMyModel.photos[i].mainPhoto = true;
}else{
newMyModel.photos[i].mainPhoto = false;
}
}
return newTrack.save(function(err,results){
console.log(err,results);
return cb(err,results);
});
});
but I'm getting an error:
'E11000 duplicate key error index: xx.$_id_ dup key: { : ObjectId(\'56269dea07a455920a21bca7\') }',
apart from the fact I feel there is a better way to do this
You may want to try a conditional type of update operation that first updates the documents based on the given criteria using the .update() method with a callback, and then do the other update in the callback for documents that do not satisfy the given criteria. For example, you could restructure this as:
var Model = mongoose.model('myCollection', mySchema);
Model.update(
{
"_id": params.documentId,
"user": params.user,
"photos.mainPhoto": true
},
{
"$set": {
"photos.$.mainPhoto": false
}
},
function(err, numAffected) {
Model.update(
{
"_id": params.documentId,
"user": params.user,
"photos._id": params.photoId /* criteria for setting the updating the documents to true */
},
{
"$set": {
"photos.$.mainPhoto": true
}
},
function(err, numAffected) {
/* Update complete. */
}
)
}
)