How to update Reference Array in mongoose - node.js

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

Populated nested schema in Mongoose

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'
}
})

How to get only not joined (excluded) documents between two collections?

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}
}
}
])

Mongoose: updating a single sub-document issues

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) {
}
);

Using aggregate with populate

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()

Why arrays of objects has ids on Mongoose?

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.

Resources