how to populate reference field when that document is updated - mongodb

I have 2 collections Users and Bookings. I created userSchema and bookingSchema and routes of bookings and users.
User Schema
const userSchema = new mongoose.Schema({
name: String,
active: {
type: Boolean,
default: true,
// select: false,
},
});
userSchema.pre(/^find/, function (next) {
this.find({ active: { $ne: false } });
next();
});
Booking Schema
const bookingSchema = new mongoose.Schema({
from: String,
to: String,
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
},
});
bookingSchema.pre(/^find/, function (next) {
this.populate({
path: 'user ',
});
next();
});
I defined my routes as follows
router.get('/api/bookings/:id', async (req, res) => {
const booking = await Booking.findById(req.params.id);
res.send(booking);
});
My issue is I created user document
{
_id: "60ab239958c0ac2882d30531",
active: true,
name: "rohit"
}
and booking document
{
"_id": "60ab1fe50618d805581f154a",
"from": "delhi",
"to": "mumbai",
"user": {
"_id": "60ab1fac0618d805581f1549",
"name": "rohit",
"__v": 0
},
"__v": 0
}
Now when I update active field to false in user "rohit" then when I fetch booking of rohit I get null in user field! (I can't delete pre middleware in user schema)
{
"_id": "60ab1fe50618d805581f154a",
"from": "delhi",
"to": "mumbai",
"user": null,
"__v": 0
}

Related

I cannot remove a subdocment in mongo Db?

I am trying to delete a subdocument from my users collection in Mongo Db, my best attempt to remove the subdocument is the the route below. However is does not work. I have been able to $unset the entire Favorite Movies array but I only want to delete one Item by its _id within the Favorite Movies Sub-Document. What am I doing Wrong.
// My Route That is supposed to Delete a Subdocument:
app.put('/Favorites/:UserName/delete/:_id',passport.authenticate('jwt', { session: false }), (req, res) => {
users.findOneAndUpdate({ UserName: req.params.UserName })
.then((user) => {
if (!user) {
res.status(400).send('ID: ' + req.params._id + ' was not found!!');
} else {
user.updateOne(
{UserName: req.params.UserName},
{
$pull: {
"FavoriteMovies": {
"ObjectId": req.params._id
}
}
})
res.status(200).send('ID: ' + req.params._id + ' was deleted!');
}
})
.catch((err) => {
console.error(err);
res.status(500).send('Error: ' + err);
});
});
//Mongoose Model Schema for User in which the Subdocuemnt I want to delete is in Favorite Movies:
let usersSchema = mongoose.Schema({
_id: {type: Object},
UserName: {type: String, required: true},
Password: {type: String, required: true},
Email: {type: String, required: true},
Birthday: Date,
FavoriteMovies:{type: Object},
ImagePath: String
});
The User Object with Favorite Movies Subdocument in Postman- Raw:
[
{
"_id": 1650119097711,
"UserName": "robbies",
"Password": "$2b$10$UZmRBLZF0UGWrB1OrZVI2ePc7N1ae5sSZj0RlSU8WyRIRsfdE.yYW",
"Email": "rob#gmail.com",
"Birthday": "1988-05-05T00:00:00.000Z",
"FavoriteMovies": [
{
"ObjectId": 2009,
"Title": "The NoteBook",
"Genre": "Romance"
},
{
"ObjectId": 2001,
"Title": "Hacksaw Ridge",
"Genre": "Action"
}
],
"ImagePath": null,
"__v": 0
}
]

Mongoose virtual field for nested field returning null

My Schema looks like this, where associated is an array of String which are not _id and i use the Virtual method to populate another field in anoher Schema
const DetailSchema = mongoose.Schema({
candidate:{
.
.
associated: [String],
.
.
}
}, { toObject: { virtuals: true }, toJSON: { virtuals: true } });
My Virtual looks like
DetailSchema.virtual('associatedJobs', {
ref: 'Jobs',
localField: 'candidate.associated',
foreignField: 'jobID',
justOne: false
});
The returned field is always null. Is there something wrong?
Your reference to Jobs (ref: 'Jobs',), it maybe Job (ref: 'Job',) if you've declared your model is Job (without 's')
Your associatedJobs will be returned not in object, this is example, it maybe with format:
{
"candidate": {
"associated": [
"J2",
"J3",
"J5"
]
},
"_id": "5c1b4ab6683beb0b8162c80f",
"id": "D1",
"__v": 0,
"associatedJobs": [
{
"_id": "5c1b4ab6683beb0b8162c80b",
"jobID": "J2",
"name": "Job name 2",
"__v": 0
},
{
"_id": "5c1b4ab6683beb0b8162c80c",
"jobID": "J3",
"name": "Job name 3",
"__v": 0
},
{
"_id": "5c1b4ab6683beb0b8162c80e",
"jobID": "J5",
"name": "Job name 5",
"__v": 0
}
]
}
This is my solution for your problem, you can download on gist to run on local https://gist.github.com/huynhsamha/a728afc3f0010e49741ca627750585a0
My simple schemas as your schemas:
var DetailSchema = new Schema({
id: String,
candidate: {
associated: [String]
}
}, {
toObject: { virtuals: true },
toJSON: { virtuals: true }
});
var JobSchema = new Schema({
jobID: String,
name: String
});
DetailSchema.virtual('associatedJobs', {
ref: 'Job',
localField: 'candidate.associated',
foreignField: 'jobID',
justOne: false
});
var Detail = mongoose.model('Detail', DetailSchema);
var Job = mongoose.model('Job', JobSchema);
And you should add populate when find:
const d = await Detail.findOne({ id: 'D1' }).populate('associatedJobs');
You should mention both schema structure to find the error in your query ,I have post example of virtual field may be you get some help
const mongoose = require('mongoose');
mongoose.Promise = global.Promise;
mongoose.connect('mongodb://localhost:27017/test', { useMongoClient: true });
var PersonSchema = new mongoose.Schema({
name: String,
band: String
});
var BandSchema = new mongoose.Schema({
name: String
}, { toObject: { virtuals: true } });
BandSchema.virtual('members', {
ref: 'Person', // The model to use
localField: 'name', // Find people where `localField`
foreignField: 'band', // is equal to `foreignField`
// If `justOne` is true, 'members' will be a single doc as opposed to
// an array. `justOne` is false by default.
justOne: false
});
var Person = mongoose.model('Person', PersonSchema);
var Band = mongoose.model('Band', BandSchema);

Mongoose $push add to document

I'm trying to add an element to an existing array, but it produces an error:
The field 'data' must be an array but is of type object in document
Scheme:
const testScheme = new Schema({
user: {
type: String,
required: true
},
data: [{
platform: {
type: String,
required: true
},
item_name: {
type: String,
required: true
},
price: {
type: Number,
default: 0
},
updatedAt: Date
}]
}, {
versionKey: false,
timestamps: true
});
Document in mongodb:
"data": [{
"price": 50,
"_id": "5a84268d6c78a60c10479437",
"platform": "pl1",
"item_name": "test"
}],
"_id": "5a841bccb44cb8cd5b974d71",
"user": "Ivan",
"updatedAt": "2018-02-14T12:07:41.793Z",
"createdAt": "2018-02-14T11:21:48.104Z"
Query:
var item = {
"platform": "pl700",
"item_name": "someText",
"price": 700,
"updatedAt": new Date()
};
Data.findOneAndUpdate({
'user': 'Ivan'
}, {
$push: {
'data': item
}
}, {
safe: true,
upsert: true
},
function(err, data) {
if (err) return res.status(500).send({
'error': err
});
res.status(200).send({
'data': data
});
}
);
I trying query with $set parametr and it works, but $push, $addToSet didn't work for me. Also i tried to google this problem and can't solve it.
It is not clear what you are intending to do.
To push an item into array you use $addToSet/$push. For updating a array you use $set.
Using $set you can update the whole document or you can update the specific field.
Update whole doc
Data.findOneAndUpdate({
'user': 'Ivan',
'data._id':item._id
}, {
$set: {
'data.$': item
}
}...
)
Update specific field
Data.findOneAndUpdate({
'user': 'Ivan',
'data._id':item._id
}, {
$set: {
'data.$.price': item.price
}
}...
)

Return true/false if users id is found in array of objects

I am new with mongodb. I am saving user recommendations in below schema. I am saving date on which user liked the business. I only want to return true/false if user id (uid) is present in users array.
const userSchema = new Schema({
uid: { type: String},
createdAt: { type: Date, default: Date.now() },
_id: false
});
const businessRecommendationsSchema = new Schema({
businessID: { type: String, unique: true},
users: [userSchema]
},{
timestamps: true
});
I do not want to return whole object if present but only true of false. Please help.
Sample data:
{
"businessID": "35M1L66a8YXzKmDYvbya3sHGJkH2",
"updatedAt": {
"$date": "2017-09-27T10:03:01.866Z"
},
"createdAt": {
"$date": "2017-09-27T10:03:01.866Z"
},
"users": [
{
"uid": "PFwwUecwpFhhWCbJEaFjmfnGOix1",
"createdAt": {
"$date": "2017-09-27T10:03:01.867Z"
}
}
]
}
I tried below query but this only works if it is simple array not array of objects.
Business.aggregate()
.match({ businessID: req.params.businessID })
.lookup({ from : 'businessrecommendations', localField: 'businessID', foreignField: 'businessID',
as: 'recommendations' })
.project({_id: 0, businessID: 1, businessName: 1,
recommendedBy: { $arrayElemAt: [ "$recommendations", 0]}})
.project({_id: 0, businessID: 1, businessName: 1,
recommended: { $in : [req.query.uid, "$recommendedBy.users"]}});

auto increment ids in mongoose

How do I have autoincrement ids in mongoose? I want my ids to start like 1, 2, 3, 4, not the weird id numbers mongodb creates for you?
Here's my schema:
var PortfolioSchema = mongoose.Schema({
url: String,
createTime: { type: Date, default: Date.now },
updateTime: { type: Date, default: Date.now },
user: {type: Schema.Types.ObjectId, ref: 'User'}
});
Use mongoose-auto-increment:
https://github.com/codetunnel/mongoose-auto-increment
var mongoose = require('mongoose');
var autoIncrement = require('mongoose-auto-increment');
var connection = ....;
autoIncrement.initialize(connection);
var PortfolioSchema = new mongoose.Schema({
url: String,
createTime: { type: Date, default: Date.now },
updateTime: { type: Date, default: Date.now },
user: {type: Schema.Types.ObjectId, ref: 'User'}
});
//Auto-increment
PortfolioSchema.plugin(autoIncrement.plugin, { model: 'Portfolio' });
module.exports = mongoose.model('Portfolio', PortfolioSchema);
Or if you prefer to use an additional field instead of overriding _id, just add the field and list it in the auto-increment initialization:
var PortfolioSchema = new mongoose.Schema({
portfolioId: {type: Number, required: true},
url: String,
createTime: { type: Date, default: Date.now },
updateTime: { type: Date, default: Date.now },
user: {type: Schema.Types.ObjectId, ref: 'User'}
});
//Auto-increment
PortfolioSchema.plugin(autoIncrement.plugin, { model: 'Portfolio', field: 'portfolioId' });
If you want to have a incrementing numeric value in _id then the basic process is you are going to need something to return that value from a store somewhere. One way to do this is use MongoDB itself to store data that holds the counters for the _id values for each collection, which is described within the manual itself under Create and Auto-Incrementing Sequence Field.
Then as you create each new item, you use the implemented function to get that "counter" value, and use it as the _id in your document.
When overriding the default behavior here, mongoose requires that you both specify the _id and it's type explicitly with something like _id: Number and also that you tell it to no longer automatically try to supply an ObjectId type with { "_id": false } as an option on the schema.
Here's a working example in practice:
var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/test');
var counterSchema = new Schema({
"_id": String,
"counter": { "type": Number, "default": 1 }
},{ "_id": false });
counterSchema.statics.getNewId = function(key,callback) {
return this.findByIdAndUpdate(key,
{ "$inc": { "counter": 1 } },
{ "upsert": true, "new": true },
callback
);
};
var sampleSchema = new Schema({
"_id": Number,
"name": String
},{ "_id": false });
var Counter = mongoose.model( 'Counter', counterSchema ),
ModelA = mongoose.model( 'ModelA', sampleSchema ),
ModelB = mongoose.model( 'ModelB', sampleSchema );
async.series(
[
function(callback) {
async.each([Counter,ModelA,ModelB],function(model,callback) {
model.remove({},callback);
},callback);
},
function(callback) {
async.eachSeries(
[
{ "model": "ModelA", "name": "bill" },
{ "model": "ModelB", "name": "apple" },
{ "model": "ModelA", "name": "ted" },
{ "model": "ModelB", "name": "oranage" }
],
function(item,callback) {
async.waterfall(
[
function(callback) {
Counter.getNewId(item.model,callback);
},
function(counter,callback) {
mongoose.model(item.model).findByIdAndUpdate(
counter.counter,
{ "$set": { "name": item.name } },
{ "upsert": true, "new": true },
function(err,doc) {
console.log(doc);
callback(err);
}
);
}
],
callback
);
},
callback
);
},
function(callback) {
Counter.find().exec(function(err,result) {
console.log(result);
callback(err);
});
}
],
function(err) {
if (err) throw err;
mongoose.disconnect();
}
);
For convience this implements a static method on the model as .getNewId() which just descriptively wraps the main function used in .findByIdAndUpdate(). This is a form of .findAndModify() as mentioned in the manual page section.
The purpose of this is that it is going to look up a specific "key" ( actually again the _id ) in the Counter model collection and perform an operation to both "increment" the counter value for that key and return the modified document. This is also aided with the "upsert" option, since if no document yet exists for the requested "key", then it will be created, otherwise the value will be incremented via $inc, and it always is so the default will be 1.
The example here shows that two counters are being maintained independently:
{ _id: 1, name: 'bill', __v: 0 }
{ _id: 1, name: 'apple', __v: 0 }
{ _id: 2, name: 'ted', __v: 0 }
{ _id: 2, name: 'oranage', __v: 0 }
[ { _id: 'ModelA', __v: 0, counter: 2 },
{ _id: 'ModelB', __v: 0, counter: 2 } ]
First listing out each document as it is created and then displaying the end state of the "counters" collection which holds the last used values for each key that was requested.
Also note those "weird numbers" serves a specific purpose of always being guranteed to be unique and also always increasing in order. And note that they do so without requiring another trip to the database in order to safely store and use an incremented number. So that should be well worth considering.