[{
"date": "18/12/2010",
"babies": [{
"id":1,
"name": "James",
"age": 8,
}, {
"id":2,
"name": "John",
"age": 4,
}]
}]
I want to set the age of John to 10 but failed. I have to do multi condition to be more specified.
Babies.update({"date":date, 'babies.id': 1}, {'$set': {age:10}, function(err, response){
res.json(response);
})
The first condition is date and the second condition is the array of object of babies, which in this case it's the id. Above query has no error and no effect, where did I do wrong?
I debug with doing this query
Babies.find({'babies.id': 1}, function(err, response){
res.json(response);
})
and it couldn't find the correct target, maybe that's the problem
Use {'$set': {'babies.$.age':10}} instead of {'$set': {age:10}}.
Babies.update({"date":date, 'babies.id': 1},
{'$set': {
'babies.$.age':10
}
},
function(err, response){
res.json(response);
})
The positional $ operator identifies an element in an array to update without explicitly specifying the position of the element in the array.
Refer to MongoDB Positional Operator for more information.
Instead of passing only object of field value {age:10} in $set flag, Pass the value in format of Array.index.field. So it would become like this -
{ $set: { 'babies.$.age': 10 } }
Related
I have a document structure something along the lines of the following:
{
"_id" : "777",
"someKey" : "someValue",
"someArray" : [
{
"name" : "name1",
"someNestedArray" : [
{
"name" : "value"
},
{
"name" : "delete me"
}
]
}
]
}
I want to delete the nested array element with the value "delete me".
I know I can find documents which match this description using nested $elemMatch expressions. What is the query syntax for removing the element in question?
To delete the item in question you're actually going to use an update. More specifically you're going to do an update with the $pull command which will remove the item from the array.
db.temp.update(
{ _id : "777" },
{$pull : {"someArray.0.someNestedArray" : {"name":"delete me"}}}
)
There's a little bit of "magic" happening here. Using .0 indicates that we know that we are modifying the 0th item of someArray. Using {"name":"delete me"} indicates that we know the exact data that we plan to remove.
This process works just fine if you load the data into a client and then perform the update. This process works less well if you want to do "generic" queries that perform these operations.
I think it's easiest to simply recognize that updating arrays of sub-documents generally requires that you have the original in memory at some point.
In response to the first comment below, you can probably help your situation by changing the data structure a little
"someObjects" : {
"name1": {
"someNestedArray" : [
{
"name" : "value"
},
{
"name" : "delete me"
}
]
}
}
Now you can do {$pull : { "someObjects.name1.someNestedArray" : ...
Here's the problem with your structure. MongoDB does not have very good support for manipulating "sub-arrays". Your structure has an array of objects and those objects contain arrays of more objects.
If you have the following structure, you are going to have a difficult time using things like $pull:
array [
{ subarray : array [] },
{ subarray : array [] },
]
If your structure looks like that and you want to update subarray you have two options:
Change your structure so that you can leverage $pull.
Don't use $pull. Load the entire object into a client and use findAndModify.
MongoDB 3.6 added $[] operator that facilitates updates to arrays that contain embedded documents. So the problem can be solved by:
db.test.update(
{ _id : "777" },
{$pull : {"someArray.$[].someNestedArray" : {"name":"delete me"}}}
)
As #Melkor has commented (should probably be an answer as itself),
If you do not know the index use:
{
_id: TheMainID,
"theArray._id": TheArrayID
},
{
$pull: {
"theArray.$.theNestedArray": {
_id: theNestedArrayID
}
}
}
From MongoDB 3.6 on you can use arrayFilters to do this:
db.test.update(
{ _id: "777" },
{ $pull: { "someArray.$[elem].someNestedArray": { name: "delete me" } } },
{ arrayFilters: [{ "elem.name": "name1"}] }
)
see also https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/index.html#update-all-documents-that-match-arrayfilters-in-an-array
Other example and usage could be like this:
{
"company": {
"location": {
"postalCode": "12345",
"Address": "Address1",
"city": "Frankfurt",
"state": "Hessen",
"country": "Germany"
},
"establishmentDate": "2019-04-29T14:12:37.206Z",
"companyId": "1",
"ceo": "XYZ"
},
"items": [{
"name": "itemA",
"unit": "kg",
"price": "10"
},
{
"name": "itemB",
"unit": "ltr",
"price": "20"
}
]
}
DELETE : Mongodb Query to delete ItemB:
db.getCollection('test').update(
{"company.companyId":"1","company.location.city":"Frankfurt"},
{$pull : {"items" : {"name":"itemB"}}}
)
FIND: Find query for itemB:
db.getCollection('test').find(
{"company.companyId":"1","company.location.city":"Frankfurt","items.name":"itemB"},
{ "items.$": 1 }
)
3.UPDATE : update query for itemB:
db.getCollection('test').update
(
{"company.companyId":"1","company.location.city":"Frankfurt","items.name":"itemB"},
{ $set: { "items.$[].price" : 90 }},
{ multi: true });
I have a document that looks like this:
{
"id": "12345",
"channels": [
{
"id": "67890",
"count": 1
}
]
}
What I want to do is increment a given channel count by one. So a user sends a message and I look up the user, find the proper channel by "id" in the "channels" array, and increment "count" by one.
I tried the following query:
UserModel.findOneAndUpdate({id: this.id},
{'channels.id': channel.id},
{$set: {$inc: {'channels.$.count': 1}}}
It didn't fail, surprisingly. But it also didn't increment the count.
Two fixes needed: query has to be a single object and $inc is a separate operator so you don't need $set:
UserModel.findOneAndUpdate({id: this.id, 'channels.id': channel.id},
{ $inc: {'channels.$.count': 1}})
I am trying to query a data collection to return just one object from an array of objects using elemMatch. I have this data:
[
{
"_id": "5ba10e24e1e9f4062801ddeb",
"user": {
"_id": "5b9b9097650c3414ac96bacc",
"firstName": "blah",
"lastName": "blah blah",
"email": "blah#gmail.com"
},
"appointments": [
{
"date": "2018-09-18T14:39:36.949Z",
"_id": "5ba10e28e1e9f4062801dded",
"treatment": "LVL",
"cost": 30
},
{
"date": "2018-09-18T14:39:32.314Z",
"_id": "5ba10e24e1e9f4062801ddec",
"treatment": "LVL",
"cost": 30
}
],
"__v": 1
}
]
I need to query the DB and pull out just one appointment depending on the id passed to params. I am using the code below based on the docs here.
router.get(
"/booked/:app_id",
passport.authenticate("jwt", { session: false }),
(req, res) => {
Appointment.find()
.elemMatch("appointments", { id: req.params.app_id })
.then(app => res.json(app));
}
);
This is returning an empty array though when I test with postman. This is the first time I have used node.js and mongoDB so any help would be appreciated. Thanks.
You are using elemmatch as query operator, but you need to use it as projection operator
Change your function to this :
Appointment.find(
{"appointments": {$elemMatch: {_id: req.params.app_id }}},
{"appointments": {$elemMatch: {_id: req.params.app_id }}}
)
Why twice??
First object in find query param is $elemmatch query operator. It will return all documents, that have at least one entry matching _id in there appointments array. (but will return whole documents)
Second object (the same) is elemmatch projection oprator, which 'filters' appointments array and returns only the first matching element in array.
PS : cannot test this command. Try on your side
I need to push multiple values into an array in mongoose using one call. I tried doing it using a smaller array but the array is getting inserted as a sub-array.
var kittySchema = new mongoose.Schema({
name: String,
values: [Number]
});
var Kitten = db.model('Kitten', kittySchema);
Kitten.update({name: 'fluffy'},{$push: {values:[2,3]}},{upsert:true},function(err){
if(err){
console.log(err);
}else{
console.log("Successfully added");
}
});
The result of the calling the above code thrice gives the below result:
{ "_id" : ObjectId("502b0e807809d79e84403606"), "name" : "fluffy", "values" : [ [ 2, 3 ], [ 2, 3 ], [ 2, 3 ] ] }
Whereas what I want is something like this:
{ "_id" : ObjectId("502b0e807809d79e84403606"), "name" : "fluffy", "values" : [ 2, 3 ,2 ,3, 2, 3] }
Another thing I noticed was that the type in the array (values) is specified as Number, then wouldnt the 'strict' option ensure that anything other than Numbers are not inserted ? In this case another array is being allowed to be inserted.
(Dec-2014 update) Since MongoDB2.4 you should use:
Kitten.update({name: 'fluffy'}, {$push: {values: {$each: [2,3]}}}, {upsert:true}, function(err){
if(err){
console.log(err);
}else{
console.log("Successfully added");
}
});
Deprecated see other solution below using $push $each
Your example is close, but you want $pushAll rather than $push to have each value added separately (rather than pushing another array onto the values array):
var Kitten = db.model('Kitten', kittySchema);
Kitten.update({name: 'fluffy'},{$pushAll: {values:[2,3]}},{upsert:true},function(err){
if(err){
console.log(err);
}else{
console.log("Successfully added");
}
});
Or use the $each modifier with $addToSet:
https://docs.mongodb.com/manual/reference/operator/update/addToSet/#each-modifier
// Existing tags array
{ _id: 2, item: "cable", tags: [ "electronics", "supplies" ] }
// Add "camera" and "accessories" to it
db.inventory.update(
{ _id: 2 },
{ $addToSet: { tags: { $each: [ "camera", "accessories" ] } } }
)
Currently, the updated doc doesn't support $pushAll. It seems to have been deprecated.
Now the good choice is to use the combination of $push & $each
an example:
//User schema: {uid: String, transaction: [objects] }
const filter = {"uid": uid};
const update = {
$push: {
transactions: {$each: dataarr}
}
}
User.updateOne(filter, update, {upsert:true}, (err) => {
if(err){
console.log(err)
}
})
pass {upsert: true} at options to insert if the filter returns false.
I am having hard time figuring out how to increment a value in an object within an array
For instance I have this document based on Poll schema:
{
"_id": "584b2cc6817758118e9557d8",
"title": "Number of Skittles",
"description": "Test1",
"date": "Dec 9, 2016",
"__v": 0,
"labelOptions": [
{
"Bob": 112
},
{
"Billy": 32
},
{
"Joe": 45
}
]
}
Using express, I am able to get this far:
app.put('/polls/:id', function(req, res){
let id = req.params.id;
let labelOption = req.query.labelOption;
Poll.findOneAndUpdate(
{'_id' : id},
{$inc: {`labelOptions.$.${labelOption}`: 1 }},
function(err){
console.log(err)
})
where labelOption is the one that I would like to increment its value
To be more concise, I am having trouble transversing inside the document.
It is not possible to directly increment the value in the .find query if labelOptions is an Array of Object. To make it easier, you should change the labelOptions type from Array of Objects to Object:
"labelOptions": {
"Bob": 112,
"Billy": 32,
"Joe": 45
};
Also consider using .findByIdAndUpdate instead of .findOneAndUpdate if you are querying by the document's _id. And then, you can achieve what you want by:
Poll.findByIdAndUpdate(
id,
{$inc: {`labelOptions.${labelOption}`: 1 }},
function(err, document) {
console.log(err);
});
UPDATE: If you are persistent on using Array of Objects for labelOptions, there is a workaround:
Poll.findById(
id,
function (err, _poll) {
/** Temporarily store labelOptions in a new variable because we cannot directly modify the document */
let _updatedLabelOptions = _poll.labelOptions;
/** We need to iterate over the labelOptions array to check where Bob is */
_updatedLabelOptions.forEach(function (_label) {
/** Iterate over key,value of the current object */
for (let _name in _label) {
/** Make sure that the object really has a property _name */
if (_label.hasOwnProperty(_name)) {
/** If name matches the person we want to increment, update it's value */
if (_name === labelOption) ++_label._name;
}
}
});
/** Update the documents labelOptions property with the temporary one we've created */
_poll.update({labelOptions: _updatedLabelOptions}, function (err) {
console.log(err);
});
});
There is another way to do this which allows a more flexible document model. If you add a field to your object like:
{
"_id": "584b2cc6817758118e9557d8",
"title": "Number of Skittles",
"description": "Test1",
"date": "Dec 9, 2016",
"__v": 0,
"labelOptions": [
{
"name": "Bob",
"number": 112
},
{
"name": "Billy",
"number": 32
},
{
"name": "Joe"
"number": 45
}
]
}
Then you can do:
app.put('/polls/:id', function(req, res){
let id = req.params.id;
let labelOption = req.query.labelOption;
Poll.findOneAndUpdate(
{
'_id' : id,
'labelOptions.name
},
{$inc: {
`labelOptions.$.number`: 1
}},
function(err){
console.log(err)
})