Mongoose remove an element from array - mongodb

This is my spa schema.
const spaSchema = new Schema({
name: String,
contactNumbers: String,
address: String,
images: [String],
});
I want to delete the element in the images array where the element name is 'abc.jpg' and documant id is x. How do I achive this?

I not really sure about your question, so I will cover 2 options. On both, I will assume that you are making an update on the spa collection.
1) Removing the images field from the document
To totally remove the field images from the desired document you can use the MongoDB $unset operator, as:
Original document:
{
_id : ObjectId('some_id'),
name : 'abc.jpg',
contactNumbers: '...',
address: '...',
images: ['...']
}
Update method:
Spa.update(
{ name : 'abc.jpg', _id : ObjectId('some_id') },
{ $unset : { images : null } }
);
That will result in the :
{
_id : ObjectId('some_id'),
name : 'abc.jpg',
contactNumbers: '...',
address: '...'
}
2) Removing one element within the images field
If you are trying to remove just one element with a specific value from the images array, you can use the MongoDB $pull operator, like:
Original document:
{
_id : ObjectId('some_id'),
name : '...',
contactNumbers: '...',
address: '...',
images: ['123.jpg','abc.jpg','def.jpg']
}
Update method:
Spa.update(
{ _id : ObjectId('some_id') },
{ $pull : { images : 'abc.jpg' } }
);
That will result in the :
{
_id : ObjectId('some_id'),
name : '...',
contactNumbers: '...',
address: '...',
images: ['123.jpg','def.jpg']
}
I hope that is what you looking for. =]

To remove one element from images(where you store image's name) you should use $pull as bellow
Spa.update(
{ _id : mongoose.Types.ObjectId('some_id') },
{ $pull : { images : 'IMAGE_NAME_THAT_YOU_WANT_TO_REMOVE_FROM_ARRAY' } }
);

Related

mongoose find collection by _id in a list of array

Here's my Schema
var PositiveSchema = new mongoose.Schema({
schoolID: {
type: mongoose.Schema.Types.ObjectId, ref: 'School'
},
name: String,
restrictAwardTo: Object
})
Now restrictAwardTo saves the data in this format
"restrictAwardTo" : [
{
"_id" : "5c31907d908eb8404657cbf0",
"firstName" : "Admin 2a"
},
{
"_id" : "5c1a7677c98da061141475a8",
"firstName" : "Justice"
},
{
"_id" : "5c1a7677c98da061141475a9",
"firstName" : "Itik"
}
],
How can I search inside my document using one of the _id listed under restrictAwardTo? I tried the solutions given below
mongooseJS find docs with IDs in an array
mongoose query: find an object by id in an array but it returns empty.
in Robo3t db.getCollection('positives').find({ 'restrictAwardTo._id' : ObjectId('5c31907d908eb8404657cbf0') })
Update: In Robo3t, this query db.getCollection('positives').find({ 'restrictAwardTo._id' : {$in: ['5c1a7677c98da061141475a7']} }) works. Now I'm making it work for mongoose too.
Here's the mongoose that works for me:
Positive.find({ schoolID: mongoose.mongo.ObjectId(schoolID), "restrictAwardTo._id": { $in: [userID]} })
But I'm not entirely sure of the performance for large records.
You could go through this way.
Positive.findOne({'restrictAwardTo': {$elemMatch: {_id: userID}}},
(err,schoolInfo) => { });

Referencing for mongoose

i'm currently trying to reference a collection called items with the structure below
packageSchema = schema({
recipient: String,
contents: [{item :{type: mongoose.Schema.Types.ObjectId,
ref: 'items', required : true}, amount: String}]
Below is my code for getting one package via its id
getOnePackage : function(id,callback)
{
packageModel.findById(id,callback)
.populate('contents')
}
So when i call the above function i'm expecting to get this result
{
recipient : Dave
contents : [
{item : {
_id:5d2b0c444a3cc6438a7b98ae,
itemname : "Statue",
description : "A statue of Avery"
} ,amount : "2"},
{item : {
_id:5d25ad29e601ef2764100b94,
itemname : "Sugar Pack",
description : "Premium Grade Sugar From China"
} ,amount : "5"},
]
}
But what i got from testing in Postman is this :
{
recipient : Dave,
contents : []
}
May i know where did it went wrong? And also how do i prevent mongoose from automatically insert an objectId for every single element in the contents array....
Because element in contents array is object with item field so your populate should be:
.populate('contents.item')

How to project specific fields from a document inside an array?

here is a typical document
{
title : 'someTitle',
places : [{name : 'someName', location : 'someLocation'}, {name ...}]
}
I have the following query
var qs = {title : 'someTitle', places : {$elemMatch : {name : 'someName' } } };
where I select a document which matches the title and which contains a document entry within its 'places' array that has name equal to 'someName'. However the issue is that the entries within the places array are large documents, and I only need a couple of fields from that document. I tried projecting the fields like so but it did not work.
var projection = {'places.$.name': 1, 'places.$.location' : 1};
Which is supposed to return an array with a document containing only the 'name' and 'location' property.
I got the following error
Can't canonicalize query: BadValue Cannot specify more than one positional proj. per query.
to be clear, I would like to accomplish this without the aggregate framework
You are doing it wrong. According to the documentation
Only one positional $ operator may appear in the projection document.
But you still need to use the $ operator to get the expected result:
var qs = { title : 'someTitle', 'places.name' : 'someName' };
var projection = {'places.$': 1 };
db.collection.find(qs, projection);
Which returns:
{
"_id" : ObjectId("564f52d7d9a433df958b5630"),
"places" : [
{
"name" : "someName",
"location" : "someLocation"
}
]
}
Also you don't need the $elemMatch operator here use "dot notation" instead.
Now if what you want is an array of "name" and "location" for each subdocument in the array then aggregation is the way to go.
db.collection.aggregate([
{ '$match': {
'title' : 'someTitle',
'places.name' : 'someName'
}},
{ '$project': {
'places': {
'$map': {
'input': '$places',
'as': 'place',
'in': {
'name': '$$place.name',
'location': '$$place.location'
}
}
}
}}
])
Which yields:
{
"_id" : ObjectId("564f52d7d9a433df958b5630"),
"places" : [
{
"name" : "someName",
"location" : "someLocation"
},
{
"name" : "bar",
"location" : "foo"
}
]
}
For the fields inside an array, you can project them the same as in embedded object
var projection = {'places.name': 1, 'places.location' : 1};
Check this guideline
https://docs.mongodb.com/manual/reference/operator/aggregation/project/#include-specific-fields-from-embedded-documents

Change an existing object in an array but still preserve key uniqueness

I have a document:
{ 'profile_set' :
[
{ 'name' : 'nick', 'options' : 0 },
{ 'name' : 'joe', 'options' : 2 },
{ 'name' : 'burt', 'options' : 1 }
]
}
If I want to add new object to the profile_set only if the name of the object isn't already taken, regardless of the options, I can qualify my update with a query object that prevents the update if the name is already present in profile_set. In the shell:
db.coll.update(
{_id: id, 'profile_set.name': {$ne: 'nick'}},
{$push: {profile_set: {'name': 'nick', 'options': 2}}})
So this will only perform the $push for a doc with a matching _id and where there isn't a profile_set element where name is 'nick'.
Question But if I later need to change Nick's name (and maybe his options too...), that is change an existing array object, not add a new one. Is there a way to do that in one atomic update operation that still honor the unique constraint of name?
There are two conditions, I think:
var newName = "somename";
var oldName = "nick";
var newOption = 3;
// if not change the name
db.coll.update({
_id : id,
'profile_set.name' : oldName
}, {
$set : {
"profile_set.$.options" : newOption
}
});
// if change the name
db.coll.update({
_id : id,
$and : [ {
'profile_set.name' : {
$ne : newName
}
}, {
'profile_set.name' : oldName
} ]
}, {
$set : {
"profile_set.$.name" : newName,
"profile_set.$.options" : newOption
}
});

One operation for Update if value doesn't exists in an array using mongodb

I'm wondering if it is possible with just one operation (or just one command) to update a document inside mongodb if the value used in the update doesn't exists in an array.
example mongodb document:
{
regs : {
someid : 12345,
dataArray : [ { id : 1 }, { id : 43 }, { id : 11 }]
}
}
Now I want only to update if the id inside dataArray is not in use already, something like:
db.regs.update({ someid : 12345 }, { $push : { dataArray : { id : INT }}})
Using the above line it's possible to check if { id : INT } is alreay in my array and update only if it isn't?
In a couple of ways. For example you can use query matching document of interest:
db.regs.update(
{someid : 12345, 'dataArray.id': {$ne: INT}},
{$push : { dataArray : {id : INT }}}
)
or perform update using addToSet:
db.regs.update(
{someid : 12345},
{$addToSet : {dataArray : {id : INT }}}
)
As #zero323 has already pointed out, there is an specific update operation with that specific use case in mind. From the MongoDB documentation:
$addToSet
The $addToSet operator adds a value to an array only if the value is
not in the array already. If the value is in the array, $addToSet
returns without modifying the array.
Consider the following example:
db.collection.update( { field: value }, { $addToSet: { field: value1 } } );
Here, $addToSet appends value1 to the array stored in field,
only if value1 is not already a member of this array.