I have a circle model in my project:
var circleSchema = new Schema({
//circleId: {type: String, unique: true, required: true},
patientID: {type: Schema.Types.ObjectId, ref: "patient"},
circleName: String,
caregivers: [{type: Schema.Types.ObjectId}],
accessLevel: Schema.Types.Mixed
});
circleSchema.virtual('caregiver_details',{
ref: 'caregiver',
localField: 'caregivers',
foreignField: 'userId'
});
caregiver schema:
var cargiverSchema = new Schema({
userId: {type: Schema.ObjectId, unique: true}, //objectId of user document
detailId: {type: Schema.ObjectId, ref: "contactDetails"},
facialId: {type: Schema.ObjectId, ref: "facialLibrary"}, //single image will be enough when using AWS rekognition
circleId: [{type: Schema.Types.ObjectId, ref: "circle"}], //multiple circles can be present array of object id
});
Sample Object:
{
"_id" : ObjectId("58cf4832a96e0e3d9cec6918"),
"patientID" : ObjectId("58fea8ce91f54540c4afa3b4"),
"circleName" : "circle1",
"caregivers" : [
ObjectId("58fea81791f54540c4afa3b3"),
ObjectId("58fea7ca91f54540c4afa3b2")
],
"accessLevel" : {
"location\"" : true,
"notes" : false,
"vitals" : true
}
}
I have tried virtual populate for mongoosejs but I am unable to get it to work.
This seems to be the exact same problem: https://github.com/Automattic/mongoose/issues/4585
circle.find({"patientID": req.user._id}).populate('caregivers').exec(function(err, items){
if(err){console.log(err); return next(err) }
res.json(200,items);
});
I am only getting the object id's in the result. It is not getting populated.
Figured out what the problem was.
By default, the virtual fields are not included in the output.
After adding this in circle schema:
circleSchema.virtual('caregiver_details',{
ref: 'caregiver',
localField: 'caregivers',
foreignField: 'userId'
});
circleSchema.set('toObject', { virtuals: true });
circleSchema.set('toJSON', { virtuals: true });
It now works perfectly.
If you use expressJs res.json method it's ehough to add only:
yourSchema.set('toJSON', { virtuals: true });
Or you can directly use toJSON/toObject:
doc.toObject({ virtuals: true })) // or doc.toJSON({ virtuals: true }))
Related
So I have 2 models, one is users and the other is sessions.
It was initially setup as:
const users = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
username: {type: String, unique: true},
}
);
module.exports = mongoose.model('User', users,);
I then went on and added another model, called sessions,
const sessions = mongoose.Schema({
_id: {type: mongoose.Schema.Types.ObjectId, default: mongoose.Schema.Types.ObjectId, null: false},
user_id: {type: mongoose.Schema.Types.ObjectId, ref: 'User'},
timestamp: {type: Date},
});
module.exports = mongoose.model('Sessions', sessions);
I then decided, I want to to be able to pull the user's sessions along with the users and added
sessions: [{type: mongoose.Schema.Types.ObjectId, ref: 'SurfSessions', default: null}] to my users model schema.
I quickly learnt that users.findOne(username:admin).populate('sessions') isn't gonna fill up sessions by itself, as sessions returns as an empty array [].
Since I don't have sessions inside users populated with objectid's does that mean I cannot populate sessions or is there another way?
You should be able to do this with virtual populate:
const users = new mongoose.Schema(
{
_id: mongoose.Schema.Types.ObjectId,
username: { type: String, unique: true },
},
{ toJSON: { virtuals: true }, toObject: { virtuals: true } }
);
module.exports = mongoose.model('User', users);
Then:
userSchema.virtual('sessions', {
ref: 'Session', // model to search in
foreignField: 'user_id',
localField: '_id', // checks where user_id on session === _id on user
});
After this, use .populate('sessions') as normal. For more check this out: https://mongoosejs.com/docs/tutorials/virtuals.html#populate
The problem is:
I have a collection of photos schema and likes schema, and inside photos there is an array of like ObjectIds called likeArray which is used to populate data from likes colletion.
But when i delete a like from likes collection, the ObjectId of that like in the likeArray still exists.
I tried to find the index of like._id in the likeArray and use likeArray.splice(index, 1) but couldn't work.
Can someone know the solution?
Here's the photo schema:
var Photo = mongoose.model('Photo', new mongoose.Schema({
photo_url: String,
photo_description: String,
user:{
id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'user'
},
username: String,
profile_photo: String
},
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'comment'
}
],
likes: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'like'
}
],
created_at: {type: Date, default: Date.now}
}));
Here's the Like schema:
var Like = mongoose.model('Like', new mongoose.Schema({
user_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'user'
},
photo_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'photo'
},
created_at: {type: Date, default: Date.now}
}));
Instead of splice you can use $pull operator. Here's how it'd look in mongo shell.
db.likes.remove({_id: like_oid});
db.photos.update({likes: like_oid}, {$pull: {likes: like_oid}}, { multi: true });
I triying the following population/projection and work fine
modelBaby.findOne({'userId': req.user.data._id, '_id': req.params.id})
.populate('categories._categoryId', {
levels:{
$elemMatch:{
name: 'PURPLE'
}
}
})
But i need name as a variable something like:
modelBaby.findOne({'userId': req.user.data._id, '_id': req.params.id})
.populate('categories._categoryId', {
levels:{
$elemMatch:{
// over heree !!
name: 'categories._levelName' or this._levelName
}
}
})
the scheme ref is
const babyCategory = new mongoose.Schema(
{
answer: {type: String},
_categoryId: {type: Schema.Types.ObjectId, required: true, ref: 'category'},
_categoryName: {type: String, required: true},
_levelName: {type: String, required: true}
},
{versionKey: false, _id : false}
);
These are my schemas (Topic is parent and contains a list of 'Thought's):
var TopicSchema = new mongoose.Schema({
title: { type: String, unique: true },
category: String,
thoughts: [ThoughtSchema]
}, {
timestamps: true,
toObject: {virtuals: true},
toJSON: {virtuals: true}
});
var ThoughtSchema = new mongoose.Schema({
text: String,
author: {type: mongoose.Schema.Types.ObjectId, ref: 'User'},
votes:[{
_id:false,
voter: {type: mongoose.Schema.Types.ObjectId, ref: 'User'},
up: Boolean,
date: {type: Date, default: Date.now}
}]
}, {
timestamps: true,
toObject: {virtuals: true},
toJSON: {virtuals: true}
});
....
I am trying to read the thought's author and change my get Topic api like this:
...
var cursor = Topic.find(query).populate({
path: 'thoughts',
populate: {
path: 'author',
model: 'User'
}
}).sort({popularity : -1, date: -1});
return cursor.exec()
.then(respondWithResult(res))
.catch(handleError(res));
...
But the author is null.. i also do not get any error in the console. What is wrong here?
Edit: Actually i do not need the Thought as a schema, it does not have its own collection in database. It will be saved in topics. But in order to use timestamps option with thoughts, i needed to extract its contents to a new local schema ThoughtSchema. But i have now defined the contents of thoughtSchema directly in the thoughts array of topics, it still does not work.
Edit2: This is the cursor object just before it is executed. Unfortunately i cannot debug in Webstorm, this is a screenshot from node inspector:
Did you try using Model.populate?
Topic.find(query).populate('thoughts')
.sort({popularity : -1, date: -1})
.exec(function(err, docs) {
// Multiple population per level
if(err) return callback(err);
Thought.populate(docs, {
path: 'thoughts.author',
model: 'User'
},
function(err, populatedDocs) {
if(err) return callback(err);
console.log(populatedDocs);
});
});
UPDATE:
You can try with deep populate like this:
Topic.find(query).populate({
path: 'thoughts',
populate: {
path: 'author',
model: 'User'
}
})
.sort({popularity : -1, date: -1})
.exec(function(err, docs) {
if(err) return callback(err);
console.log(docs);
});
How about
Topic.find(query).populate('thoughts')
.sort({popularity : -1, date: -1})
.exec(function(err, docs) {
// Multiple population per level
if(err) return callback(err);
Topic.populate(docs, {
path: 'thoughts.author',
model: 'User'
},
function(err, populatedDocs) {
if(err) return callback(err);
console.log(populatedDocs);
});
});
These are the schemas :
var TopicSchema = new mongoose.Schema({
title: { type: String, unique: true },
category: String,
thoughts: [ThoughtSchema]
}, {
timestamps: true,
toObject: {virtuals: true},
toJSON: {virtuals: true}
});
var ThoughtSchema = new mongoose.Schema({
text: String,
author: {type: mongoose.Schema.Types.ObjectId, ref: 'User'},
votes:[{
_id:false,
voter: {type: mongoose.Schema.Types.ObjectId, ref: 'User'},
up: Boolean,
date: {type: Date, default: Date.now}
}]
}, {
timestamps: true,
toObject: {virtuals: true},
toJSON: {virtuals: true}
});
Did you try Aggregation Instead of Populate. Aggregate Makes much easier for populating the embedded data using $lookup. Try the below code.
UPDATE
Topic.aggregate([{$unwind: "$thoughts"},{ $lookup: {from: 'users', localField: 'thoughts.author', foreignField: '_id', as: 'thoughts.author'}},{$sort:{{popularity : -1, date: -1}}}],function(err,topics){
console.log(topics) // `topics` is a cursor.
// Perform Other operations here.
})
Explanation:
$unwind: Deconstructs an array field from the input documents to output a document for each element.
$lookup: The $lookup stage does an equality match between a field from the input documents with a field from the documents of the “joined” collection. The lookup does the population's job.
$lookup works like
from : this says from which collection the data needs to be populated.(users in this scenario).
localField : this is the local field which needs to be populated. (thoughts.author in this scenario).
foreignField : this is the foreign field present in the collection from which data needs to be populated (_id field in users collection in this scenario).
as : this is the field as what you want to display the joined value as. (this will project thoughts.author's id as thoughts.author document).
Hope this works.
So I have a Mongoose query question that I can't seem to figure out. I'm trying to query a schema to find all documents in the collection that have a parameter specified user id in one of a few places... What I have so far is this:
Schema:
var Module = new Schema({
template: { type: Schema.ObjectId, ref: 'template' },
owner: { type: String, required: true, index: { unique: false } },
primary: { type: Schema.ObjectId, ref: 'User' },
users: [{ type: Schema.ObjectId, ref: 'User' }],
data: [],
children: [{ type: Schema.ObjectId, ref: 'Module' }],
parent: { type: Schema.ObjectId, ref: 'Module' }
});
Query:
Module.find()
.or([{owner: req.user.id}, {primary: req.user.id}])
.populate('primary users children parent template')
.exec(function (err, modules) {
...
}
So as of now, it seems to correctly find documents that have the req.user.id as the primary or owner field, but how would I also return all documents where the users array is also searched for this value?
Thanks!