MongoDB $pull value from array of ObjectIDs - mongodb

I have this document in my collection:
{
"_id" : ObjectId("52718433e18a711923000005"),
"owners" : [
ObjectId("52718433e18a711923000004"),
ObjectId("52ed40dccc5bc50000000003"),
ObjectId("52ed4171abe2780000000003")
]
}
I have the following statement, where I am trying to remove one of the values in owners field:
Business.update({_id:req.body._id}, {$pull:{"owners":req.body.userid}}, function(err){
if(err){
res.json(500, {message:"Could not remove user from admin list"});
}else{
res.json(200);
}
});
I know that req.body._id and req.body.userid have valid values:
{ _id: '52718433e18a711923000005',
userid: '52ed4171abe2780000000003' }
Other operations, such as finding business by ID, etc, work, so it's not an ObjectId format issue. What else could it be?
--
Edit: here is my (abbreviated) schema definition:
var BusinessSchema = new Schema({
business_name: {type: String, required: true},
owners: {type: Array}
});

Your current schema doesn't provide any direction to Mongoose regarding the data type contained within the owners array field. If you want Mongoose to cast your string to an ObjectID you need to provide type information in your schema:
var BusinessSchema = new Schema({
business_name: {type: String, required: true},
owners: [{type: Schema.ObjectId}]
});

It looks like a conversion to ObjectId is required when trying to match values to pull. But not to search. So this works:
var ObjectId = require('mongoose').Types.ObjectId;
Business.update({_id:req.body._id}, {$pull:{"owners": new ObjectId(req.body.userid)}}, function(err){
if(err){
res.json(500, {message:"Could not remove user from admin list"});
}else{
res.json(200);
}
});
--
Edit: So, if I change my schema from owners: {type: Array} to owners: [Schema.Types.ObjectId], I can skip the conversion, and my original statement works.

Related

How can I update multiple fields in an array of embedded documents Mongoose?

My Mongoose model:
const userSchema = Schema({
firstName: String,
lastName: String,
books: [{title: String, author: String, isbn: Number}]
});
I'd like to add a new title & author with a book object for each POST request. My current approach giving me positional operator did not find the match error :
var User = mongoose.model("User", userSchema);
router.post('/submit', (req, res) => {
let update={
'books.$.title' : req.body.title,
'books.$.author' : req.body.author
}
User.findOneAndUpdate({_id:req.body.user_id},{$push: update},{new: true}).exec(function(err, doc) {
if (err){
console.log(err);
return;
}
res.send(doc);
});
});
I expect the following result for two form submit(POST) with different author and title in my DB:
{
_id: 'SomeId'
firstName: 'John',
lastName: 'Cena',
books: [{title: 'a', author:'b' }, {{title: 'c', author:'d' }}]
}
I don't care about ISBN in both post as it's not required field in our schema. Since my books sub-document is empty at beginning so I'm not able not set positional operator ($) properly in my code. Please help me to write findOneAndUpdate query correctly!
$push requires field name and value, in your case field name is book and value is an object {author, title}
User.findOneAndUpdate({_id:req.user._id}, {$push : { books : { author: "c", title: "d"}}})

findOneAndUpdate doesn't create ObjectId

I need to make a patch request to update only one (or several) field(s) at the same time.
I've got a big object which is my document, and inside nested array of objects.
For example, for my car array, this is the schema :
const carSchema = new Schema({
owner: [{value: {type: String}, label: {type: String}}],
carPlate: {type: String},
carColor: {type: String},
carBrand: {type: String},
carStatus: {type: String}
});
const myObject = new Schema({
...
cars: [carSchema]
...
});
When I send my changes, I do it this way :
let dynamicVar = 'cars.'+i+'.'+myfield;
this.props.updateGeneral({_id: this.props.general._id, [dynamicVar ]: [myfield.value]});
I'm on redux, so my action looks like :
export function updateGeneral(data) {
let _id = data._id;
delete data._id;
return {
type: 'UPDATE_GENERAL',
payload: client.patch(`${url}/${_id}`, data)
}
}
And my PATCH request is like :
router.patch('/url/:id', async (req, res, next) => {
myObject.findOneAndUpdate({_id: req.params.id}, {$set: req.body }, {upsert: true, new: true}, function (err, objectReturn) {
if (err) return next(err);
cn = cn.substr(0, cn.indexOf(' {'));
res.json(objectReturn);
});
});
My BIG issue is that my field is update or inserted, but if it's inserted and it creates a new array it won't create the objectId linked. It won't even create the array of object,just an object with a property.
How can I make mongoose initiates ObjectId??
Per the reply to this SO post it looks like you cannot update object IDs. When doing so, you are effectively "deleting" the object and creating a new one.

Mongoose why would you use populate over another find?

I'm guessing because you save resources by making 1 request instead of 2 to the database. Is this significant? Should I care to use populate if I'm populating only 1 field (the advantage is clearer when you populate more than 1)?
You don't save resources by using populate. Under the hood mongoose calls the database as many times as required. Consider the example:
module.exports = function(){
var UserSchema = new Schema({
email : {type : String, required: true},
password: {type: String, required: true}
});
return mongoose.model("User", UserSchema);
};
module.exports = function(){
var AccountSchema = new Schema({
number : {type : String, required: true},
user: {type: Schema.Types.ObjectId, ref: 'User'}
});
return mongoose.model("Account", AccountSchema);
};
mongoose.set('debug', true); //to see queries mongoose is using behind the scenes
Account.find({}, function(err, res){
console.log(res)
}).populate("user")
Apart from the results, you'll see something like this on console:
Mongoose: accounts.find({}, { fields: undefined })
Mongoose: users.find({ _id: { '$in': [ ObjectId("5807d6d6aa66d7633a5d7025"), ObjectId("5807d6d6aa66d7633a5d7026"), ObjectId("5807d709aa66d7633a5d7027") ] } }, { fields: undefined })
That's mongoose finding account documents and then user for each one of them.
It's saving you a lot of code and I don't see why you should not use it irrespective of the number of fields you're populating.

Mongoose: does a custom _id need to be declared as an index and be unique

Here is the common way to define a collection structure with Mongoose :
var UserSchema = new Schema({
_id: Schema.Types.ObjectId,
username: String,
...
});
And Now I want _id field declared as Number type :
var UserSchema = new Schema({
_id: Number,
username: String,
...
});
The problem is, do I need to declare more infomation about _id ? Such as :
var UserSchema = new Schema({
_id: {type: Number, required: true, index: {unique: true}},
username: String,
...
});
I am not sure whether MongoDB would do it automatically.
if you know the answer, could you leave a comment below ? Thank you!
Well, after some practice, I realized that, MongoDB would set _id as PRIMARY KEY (NOT NULL + UNIQUE INDEX) automatically. So, just type:
_id: Number,
...

Sort Nested document in MongooseJS

This is my schema:
var Review = new Schema({
user: {type: ObjectId, ref:'User'},
lastModified: {type: Date, default: Date.now }
});
var Subject = new Schema({
name: String,
review: [Review],
...
});
The query will return all the subjects with review from a user.
{'review.user': id}
Is it possible to sort the result based on review.lastModified?
Subject.find({'review.user': id}).select('name').sort('???').exec( function(err, subjects){
if (err) return res.json(error);
console.log('subject', subjects);
});
You cannot sort within a document using MongoDB. Sorting within the document must be done at the application level.