I have a Group Collection, which is having a Reference array of Members. Two Objects are inter-connected like follow. When I am adding new members to the group the members field of Group object needed to be updated. How can I do this with a mongoose update operator.
var MemberSchema = new Schema({
name:{
type:String,
default:null
},
user_id:{
type : Schema.ObjectId,
ref : 'User',
default : null
}
});
var GroupSchema = new Schema({
name:{
type:String,
default:null
},
description:{
type:String,
default:null
},
members:[MemberSchema],
},{collection:"groups"});
Thank You in advance.
Update
I added a sample document of group.
{
"_id" : ObjectId("586a2e694467c41218b302c3"),
"members" : [
{
"_id" : ObjectId("586a2e694467c41218b302c6"),
"user_id" : ObjectId("58171d75e72bf516f92dcd4e"),
"name" : "Lakmal Kapukotuwa"
},
{
"_id" : ObjectId("586a2e694467c41218b302c5"),
"user_id" : ObjectId("5821807516325e127f59438e"),
"name" : "Prasad Perera"
},
{
"_id" : ObjectId("586a2e694467c41218b302c4"),
"user_id" : ObjectId("586263515356e908de6c899a"),
"name" : "Sadun Prasad"
}
],
"description" : "Des 1",
"name" : "My group",
"__v" : 0
}
If you are sending the new members as a list of objects with the following structure e.g.
membersListToAdd = [
{
"user_id": "58171d75e72bf516f92dcd4e",
"name": "foo"
},
{
"user_id": "5821807516325e127f59438e",
"name": "bar"
}
]
then use $push with $each modifier in an update as follows:
var query = { name: 'My Group' },
options = {},
callback = function (err, result) { console.log(result); };
Group.update(query, { $push: { members: { $each: membersListToAdd } } }, options, callback)
You are doing this wrong,
no need to have links in both collections and no need to nest models
try this instead
var Group = mongoose.model("Group", new Schema({
name: {
type:String
},
description: {
type:String
},
}));
Group.virtual("users", {
ref: "User",
localField: "_id",
foreignField: "groups"
});
var User = mongoose.model("User", new Schema({
name:{
type:String
},
groups: [{
type : Schema.ObjectId,
ref : 'Group'
}]
}));
Related
This is my Schema, I have a CampaignStat Object which has a nested Product Schema, but at the same time Product is another model.
const ProductsSchema = new Schema({
_id: {
type: mongoose.Schema.ObjectId,
ref: 'Product'
},
clickedAt: Date,
deviceInfo: {
type: Schema.Types.Mixed
}}, { _id: false })
const CampaignStatSchema = new Schema({
campaign: {
type: mongoose.Schema.ObjectId,
ref: 'Campaign'
},
subscriber: {
type: mongoose.Schema.ObjectId,
ref: 'Subscriber'
},
openedAt: Date,
products: [ProductsSchema]
}, {
timestamps: { createdAt: true, updatedAt: false }
})
An this is how is being stored in MongoDB
"_id" : ObjectId("5f18d01ecab4eb6175cc0f83"),
"subscriber" : ObjectId("5ed6890da2c99843280a0058"),
"campaign" : ObjectId("5ed937829ff2a1f99de55150"),
"products" : [
{
"_id" : ObjectId("5f160ca2014be4e159f32fcd")
},
{
"_id" : ObjectId("5f160ca2014be4e159f3302e")
},
{
"_id" : ObjectId("5f160ca2014be4e159f33036")
},
{
"_id" : ObjectId("5f160ca2014be4e159f3311a")
},
{
"_id" : ObjectId("5f160ca2014be4e159f33159")
}
],
"createdAt" : ISODate("2020-07-22T23:47:42.228Z"),
"__v" : 0
I need to populate Products, I'm doing this but I cannot get the Product collection attributes
const CampaignStats = await this.find({ campaign: "5ed937829ff2a1f99de55150" })
.populate('subscriber')
.populate({
path: 'products',
model: 'Product'
})
In case somebody gets into the same problem, I changed this (_id for product):
const ProductsSchema = new Schema({
product: {
type: mongoose.Schema.ObjectId,
ref: 'Product'
},
clickedAt: Date,
deviceInfo: {
type: Schema.Types.Mixed
}}, { _id: false })
And my query is like this:
this.find(query)
.populate({
path: 'products',
populate: {
path: 'product',
model: 'Product'
}
})
The problem is around two collections Users and Forms. Each user is joined with his form by Id.So every form in a Forms collection has a userId foreign key.
I need to get all documents from a Users collection , which doesn't have any form connected to it (there is no such a form).
In SQL I would do something like this:
SELECT *
FROM Users U
LEFT JOIN Forms F
ON U._id= F.userId
WHERE F.userId IS NULL
U=A , F=B
How can we achieve this functionality in MongoDb , may be with Mongoose on a nodeJs side? I couldn't find any solution based on a $lookup aggregation.
Schemas:
*employeeId is a reference to Users _id
let FormsSchema = new Schema({
employeeId:{
type:Schema.Types.ObjectId , ref:'User'
},
companyId:{
type:Schema.Types.ObjectId
},
formData : {
type:String
},
formType : {
type:String
},
formDate :{
type:Date,default: Date.now
},
formState: {
type: String, required: true, enum: ["new","draft","aproved","pendingHR","pendingEMP","pendingSigned"], default: "new"
},
messageData : {
type:Schema.Types.Mixed
},
formIdkunDate:{
type:Date,default: Date.now
},
attachmentDirty:{
type:Boolean
},
company : {
type:Schema.Types.Mixed
},
formShnatMas : {
type:String
},
sendDate : {
type:Date,default: Date.now
},
draftDate : {
type:Date,default: Date.now
},
fixDate : {
type:Date,default: Date.now
},
is101FormOpenedAfterArchive : {
type:Boolean
},
}
let UserSchema = new Schema({
userName:{
type:String,
trim:true
},
CreateDate :{
type:Date,default: Date.now
},
UpdateDate :{
type:Date,default: Date.now
},
password : {
type:String,
required: true,
trim:true
},
isFirstEntrance : {
type:Boolean
},
resetToken : {
type:String
},
resetPasswordExpires : {
type:String
},
resetOtpPassExpires : {
type:String
},
otpPass : {
type:String,
trim:true
},
userType: {
type: String, required: true, enum: ["employee","hr","admin"], default: "employee"
} ,
employeeData : {
type:Schema.Types.Mixed
},
partnerData : {
type:Schema.Types.Mixed
},
externalId : {
type:String
},
isChangedFromPrevious : {
type:Boolean
},
isFirstYearWorker101 : {
type:Boolean
},
lastLogin :{
type:Number
},
loginAttempts : {
type:Number
},
});
I have followed the schema given by you and I am able to get all documents from a Users collection , which doesn't have any form connected to it using $lookup. Please use below aggregate query
db.user.aggregate([
{
$lookup:{
"localField": "_id",
"from": "form",
"foreignField": "employeeId",
"as": "forminfo"
}
},{
$match:{
forminfo: {$size: 0}
}
}
])
Schema:
var educationSchema = new Schema({
schoolName: String,
startDate: Number,
endDate: Number,
degree: String,
major: String,
grade: String
});
var UserSchema = new Schema({
firstName: String,
lastName: String,
education: [educationSchema]
});
Update code:
User.findOneAndUpdate(
{"_id": req.user.id, "education._id": req.body.id},
{
"$set": {
"education.$": req.body
}
},
function(err, edu) {
}
);
Now, if the user only edits the schoolName on the UI the following happens:
Pre-save State:
{
"_id" : ObjectId("5878fb4f51ec530358fea907"),
"firstName" : "John",
"lastName" : "Doe",
"education" : [
{
"schoolName" : "ABC",
"startDate" : 1998,
"endDate" : 2005,
"degree" : "Bachelor’s Degree",
"major" : "CS",
"grade" : "3.5",
"_id" : ObjectId("5878fbb951ec530358fea909")
}
]
}
Post-save State:
"education" : [
{
"schoolName" : "XYZ"
}
]
Is $set not the right operator to use?
Updating education.$ updates the sub-document. If you want to update only the schoolName you must use education.$.schoolName.
Change your update code to:
User.findOneAndUpdate(
{"_id": req.user.id, "education._id": req.body.id},
{
"$set": {
"education.$.schoolName": req.body
}
},
function(err, edu) {
}
);
EDIT: (update any field sent through req.body)
const update = {};
Object.getOwnPropertyNames(req.body).forEach(key => {
update['education.$.' + key] = req.body[key];
});
User.findOneAndUpdate(
{"_id": req.user.id, "education._id": req.body.id},
{
"$set": update
},
function(err, edu) {
}
);
I am trying to use aggregate with populate. I need $group to variable of referenced ObjectId Schema. The structure is like:
var jobFilter = mongoose.Schema({
location : { type: Schema.Types.ObjectId, ref: 'Location' },
field : {type: Schema.Types.ObjectId, ref: 'Jobfield'}
})
var locationSchema = mongoose.Schema({
city : String,
longitude : Number,
latitude : Number
});
and i want to group by _field and city. I wrote this query but it did not work because of city. Is it possible to populate Location somehow in this query?
jobFilter.aggregate(
[
{
$match : {
"jobFilter.field" : {$ne: null},
"jobFilter.location" : {$ne: null}}
},
{
$group :
{ _id: {
"field" : "$jobFilter.field" ,
"location" : "$jobFilter.location.city"
}, count: { $sum: 1}}},
{
$sort : { count : -1}
}
]
)
.exec()
I've went through some problems while saving some documents and realized that some inner documents has id references.
Let's take this schema for example:
var VisitSchema = mongoose.Schema({
user_id: { type: Schema.Types.ObjectId, ref: 'User' },
name: String,
city: {
name: String,
reference: String,
location: [Number]
},
photos: [{
image_url: String,
description: String
}]
});
When this schema is saved, here is the resulting document:
{
"_id" : ObjectId("5449eceeea5281056768aef8"),
"user_id" : ObjectId("540603dd797be100008b4340"),
"name" : "Test",
"photos" : [
{
"image_url" : "testURL",
"description" : "testDescription",
"_id" : ObjectId("5449eceeea5281056768aefb")
}
],
"city" : {
"name" : "New York, NY, United States",
"reference" : "CoQBdAAAAFfbZuaWptFtAkhGaO87UVSaXbyYwtTY_xX-pH84mS6QO2ypLr15znsCnYvaZ_N8CvwCBAr4y34PSQqyTUwxa5qbUKBK0yHnUFgTLKPQMFKqyQ8xd4vcDPNXf5XSxlpsXKKPYnU0AJZEVVxXLJ_6IBiUay9emFvNXPiYb7kT04NjEhB8fK-jKIZHJ5Bfz_mfyk2MGhTJwOPU68NQiV7RlAdb0Gca1bg0ew",
"location" : [
-74.0059413,
40.7127837
]
},
"__v" : 0
}
Why does the subdocument photos has an id reference? It doesn't make sense.
Thanks.