mongoose save multi level nested objects - node.js

I try to implement my first mongoose based REST API.
I tried now for days but cannot get this up and running. I would like to save the survey with an array of controls and for each control an array of controlProperties.
In different scenarios I got it to save survey with controls array but without controlProperties and sometime with not even controls array.
Can someone please help me understand my error?
Thanks a lot.
The structure is as follows:
Survey
-- Array of control
-- Array of controlProperty
My schema files are:
survey.js
const mongoose = require('mongoose');
const Control = require('./control');
const surveySchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: {
type: String,
required: true,
min: 4,
max: 255
},
description: {
type: String,
required: false,
max: 1000
},
closeDate: {
type: Date,
required: false
},
controls: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Control' }]
});
module.exports = mongoose.model('Survey', surveySchema);
control.js
const mongoose = require('mongoose');
const Survey = require('./survey');
const controlSchema = new mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
survey: {type: mongoose.Schema.Types.ObjectId, ref: 'Survey'},
controlType: {
type: String,
required: true
},
name: {
type: String,
required: true
},
isInput: {
type: Boolean,
required: true
},
order: {
type: Number,
required: true
},
controlProperties: [{ type: mongoose.Schema.Types.ObjectId, ref: 'ControlProperty' }]
});
module.exports = mongoose.model('Control', controlSchema);
controlProperty.js
const mongoose = require('mongoose');
const Control = require('./control');
mongoose.Schema.Types.String.checkRequired(v => v != null);
const controlPropertySchema = new mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
control: { type: mongoose.Schema.Types.ObjectId, ref: 'Control' },
propertyName: {
type: String,
required: true
},
propertyValue: {
type: String,
required: true
},
order: {
type: Number,
required: true
}
})
module.exports = mongoose.model('ControlProperty', controlPropertySchema);
My node.js code to receive the post data is this one:
/routes/survey.js
router.post("/", (req, res, next) => {
Survey.find({ _id: req.body._id })
.exec()
.then(result => {
if (result.length >= 1) {
return res.status(409).json({
message: "Survey exists"
});
} else {
const survey = new Survey({
_id: new mongoose.Types.ObjectId(),
name: req.body.name,
description: req.body.description,
closeDate: req.body.closeDate,
order: req.body.order
});
let controlData = req.body.controls;
let arControls = [];
if(controlData != null) {
for (var i = 0, clen = controlData.length; i < clen; i++) {
let c = controlData[i];
let control = new Control({
_id: new mongoose.Types.ObjectId(),
controlType: c.controlType,
name: c.name,
isInput: c.isInput,
order: c.order
})
let controlPropertyData = c.controlProperties;
let arControlProperty = [];
if(controlPropertyData != null) {
for (var j = 0, cplen = controlPropertyData.length; j < cplen; j++) {
let cp = controlPropertyData[j];
let controlProperty = new ControlProperty({
_id: new mongoose.Types.ObjectId(),
propertyName: cp.propertyName,
propertyValue: cp.propertyValue,
order: cp.order
})
arControlProperty.push(controlProperty);
}
ControlProperty.insertMany(arControlProperty, forceServerObjectId=true,function (err,data) {
if(err!=null){
return console.log(err);
}
console.log(" " + j + " controlProperties for control " + i + " saved");
control.controlProperties = data;
console.log(data);
});
}
arControls.push(control);
}
Control.insertMany(arControls, forceServerObjectId=true,function (err,data) {
if(err!=null){
return console.log(err);
}
survey.controls = data;
console.log("controls saved");
console.log(data);
});
}
survey
.save()
.then(result => {
console.log("survey saved");
res.status(201).json(survey);
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
}
});
});
sample post data:
{
"name": "TestSurvey",
"description": "This is a test survey",
"controls": [
{
"controlType": "Label",
"name": "Label1",
"isInput": false,
"order": 1,
"controlProperties": [
{
"propertyName": "FontSize",
"propertyValue": "Large",
"order": 1
},
{
"propertyName": "BackgroundColor",
"propertyValue": "Darkgreen",
"order": 2
},
{
"propertyName": "FontAttributes",
"propertyValue": "Bold",
"order": 3
},
{
"propertyName": "HorizontalOptions",
"propertyValue": "Fill",
"order": 4
},
{
"propertyName": "HorizontalTextAlignment",
"propertyValue": "Center",
"order": 5
},
{
"propertyName": "TextColor",
"propertyValue": "White",
"order": 6
},
{
"propertyName": "Text",
"propertyValue": "Paris Work-Life Balance",
"order": 7
}
]
},
{
"controlType": "Label",
"name": "Label2",
"isInput": false,
"order": 2,
"controlProperties": [
{
"propertyName": "FontSize",
"propertyValue": "Medium",
"order": 1
},
{
"propertyName": "Margin",
"propertyValue": "20,0,20,0",
"order": 2
},
{
"propertyName": "FontAttributes",
"propertyValue": "Bold",
"order": 3
},
{
"propertyName": "HorizontalOptions",
"propertyValue": "StartAndExpand",
"order": 4
},
{
"propertyName": "HorizontalTextAlignment",
"propertyValue": "Center",
"order": 5
},
{
"propertyName": "Text",
"propertyValue": "Dear [[FirstName]], \nwas your workload on the case 12345 - 67(Company) compliant to the BCG Work Life Balance Ground Rules over the past week ?",
"order": 6
}
]
},
{
"controlType": "PWLBControl",
"name": "PWLB1",
"isInput": true,
"order": 3,
"controlProperties": [
{
"propertyName": "Margin",
"propertyValue": "20,0,20,0",
"order": 1
}
]
},
{
"controlType": "Button",
"name": "button1",
"isInput": false,
"order": 4,
"controlProperties": [
{
"propertyName": "Text",
"propertyValue": "Submit",
"order": 1
},
{
"propertyName": "HorizontalOptions",
"propertyValue": "StartAndExpand",
"order": 2
},
{
"propertyName": "IsSubmitButton",
"propertyValue": true,
"order": 3
}
]
},
{
"controlType": "Image",
"name": "image1",
"isInput": false,
"order": 5,
"controlProperties": [
{
"propertyName": "Source",
"propertyValue": "",
"order": 1
},
{
"propertyName": "VerticalOptions",
"propertyValue": "End",
"order": 2
}
]
}
]
}

There is no need to keep seperate collections for controlProperties and controls.
You can embed controlPropertySchema inside controlSchema, and embed controlSchema inside surveySchema.
So at the end we will have only one collection for survey.
This will make possible to create a survey in one insert operation. And also you will be able to get all survey info in one read operation.
Also there are a few suggestions:
It is better to not add _id fields to the schemas, mongodb will
handle it.
I see you use it if a survey exists with the given _id. Better to
use name field to check if a survey already exists.
min and max options are used for Number type, for String type
minlength and maxlength are used. Docs
So the surveySchema must look like this:
const mongoose = require("mongoose");
const controlPropertySchema = new mongoose.Schema({
// _id: mongoose.Schema.Types.ObjectId,
// control: { type: mongoose.Schema.Types.ObjectId, ref: "Control" },
propertyName: {
type: String,
required: true
},
propertyValue: {
type: String,
required: true
},
order: {
type: Number,
required: true
}
});
const controlSchema = new mongoose.Schema({
//_id: mongoose.Schema.Types.ObjectId,
// survey: {type: mongoose.Schema.Types.ObjectId, ref: 'Survey'},
controlType: {
type: String,
required: true
},
name: {
type: String,
required: true
},
isInput: {
type: Boolean,
required: true
},
order: {
type: Number,
required: true
},
controlProperties: [controlPropertySchema]
//controlProperties: [{ type: mongoose.Schema.Types.ObjectId, ref: "ControlProperty" }]
});
const surveySchema = mongoose.Schema({
// _id: mongoose.Schema.Types.ObjectId,
name: {
type: String,
required: true,
minlength: 4,
maxlength: 255
},
description: {
type: String,
required: false,
maxlength: 1000
},
closeDate: {
type: Date,
required: false
},
controls: [controlSchema]
// controls: [{ type: mongoose.Schema.Types.ObjectId, ref: "Control" }]
});
module.exports = mongoose.model("Survey", surveySchema);
Now we can create a survey with this post route:
(Please note that we don't make any conversions, since our request body's structure is the same as surveySchema)
router.post("/surveys", async (req, res) => {
try {
let survey = await Survey.findOne({ name: req.body.name });
if (survey) {
return res.status(400).send("A survey already exists with that name");
}
const result = await Survey.create(req.body);
res.send(result);
} catch (err) {
console.log(err);
if (err.name === "ValidationError") {
return res.status(400).send(err.errors);
}
res.status(500).send("Something went wrong");
}
});
In your request body, there was an empty propertyValue so I changed it to "propertyValue": "I was empty",
and also a boolean value instead of string, so I changed it to "propertyValue": "I was true"
You can use this corrected request body:
{
"name": "TestSurvey",
"description": "This is a test survey",
"controls": [
{
"controlType": "Label",
"name": "Label1",
"isInput": false,
"order": 1,
"controlProperties": [
{
"propertyName": "FontSize",
"propertyValue": "Large",
"order": 1
},
{
"propertyName": "BackgroundColor",
"propertyValue": "Darkgreen",
"order": 2
},
{
"propertyName": "FontAttributes",
"propertyValue": "Bold",
"order": 3
},
{
"propertyName": "HorizontalOptions",
"propertyValue": "Fill",
"order": 4
},
{
"propertyName": "HorizontalTextAlignment",
"propertyValue": "Center",
"order": 5
},
{
"propertyName": "TextColor",
"propertyValue": "White",
"order": 6
},
{
"propertyName": "Text",
"propertyValue": "Paris Work-Life Balance",
"order": 7
}
]
},
{
"controlType": "Label",
"name": "Label2",
"isInput": false,
"order": 2,
"controlProperties": [
{
"propertyName": "FontSize",
"propertyValue": "Medium",
"order": 1
},
{
"propertyName": "Margin",
"propertyValue": "20,0,20,0",
"order": 2
},
{
"propertyName": "FontAttributes",
"propertyValue": "Bold",
"order": 3
},
{
"propertyName": "HorizontalOptions",
"propertyValue": "StartAndExpand",
"order": 4
},
{
"propertyName": "HorizontalTextAlignment",
"propertyValue": "Center",
"order": 5
},
{
"propertyName": "Text",
"propertyValue": "Dear [[FirstName]], \nwas your workload on the case 12345 - 67(Company) compliant to the BCG Work Life Balance Ground Rules over the past week ?",
"order": 6
}
]
},
{
"controlType": "PWLBControl",
"name": "PWLB1",
"isInput": true,
"order": 3,
"controlProperties": [
{
"propertyName": "Margin",
"propertyValue": "20,0,20,0",
"order": 1
}
]
},
{
"controlType": "Button",
"name": "button1",
"isInput": false,
"order": 4,
"controlProperties": [
{
"propertyName": "Text",
"propertyValue": "Submit",
"order": 1
},
{
"propertyName": "HorizontalOptions",
"propertyValue": "StartAndExpand",
"order": 2
},
{
"propertyName": "IsSubmitButton",
"propertyValue": "I was true",
"order": 3
}
]
},
{
"controlType": "Image",
"name": "image1",
"isInput": false,
"order": 5,
"controlProperties": [
{
"propertyName": "Source",
"propertyValue": "I was empty",
"order": 1
},
{
"propertyName": "VerticalOptions",
"propertyValue": "End",
"order": 2
}
]
}
]
}

Thanks for all the valuable input.
Finally I found the reason why my code did not work from the beginning. Only thing I needed to change was to do the assignment of controlProperties array to control and the controls array to survey not in the success function of the insertMany but after the whole inserMany command.
So I changed:
ControlProperty.insertMany(arControlProperty, forceServerObjectId=true,function (err,data) {
if(err!=null){
return console.log(err);
}
console.log(" " + j + " controlProperties for control " + i + " saved");
control.controlProperties = data;
console.log(data);
});
to:
ControlProperty.insertMany(arControlProperty, forceServerObjectId=true,function (err,data) {
if(err!=null){
return console.log(err);
}
console.log(" " + j + " controlProperties for control " + i + " saved");
console.log(data);
});
control.controlProperties = arControlProperty;
Thanks for you support.

Related

How do i group my list off stories by user details

so here is my stories model,
const storySchema = new mongoose.Schema(
{
avatar: { type: String, default: null },
handle: { type: String, default: null },
new: { type: Boolean, default: true },
isLive: {type: Boolean, default: false},
url: { type: String },
type: { type: String },
userName: { type: String, default: null },
userId: { type: mongoose.Schema.Types.ObjectId, ref: "user_details" },
isDeleted: { type: Boolean, default: false}
},
{
timestamps: true,
minimize: false
}
)
userId is refered to user_details, so currently when i list stories they get listed like this ,
one story at a time and sorted by userId and createdAt,
As you can see the first 2 stories has the same userId, and what i want to do is that i group the stories by the user Object.
"status": true,
"data": [
{
"_id": "633564ab793cf2a65f7c5dad",
"avatar": null,
"handle": null,
"new": true,
"isLive": false,
"url": "https://ellingsen-group.s3.amazonaws.com/media-1664443562856.png",
"type": "",
"userName": null,
"userId": "62eb5d58512ef25f1352830b",
"isDeleted": false,
"createdAt": "2022-09-29T09:26:03.846Z",
"updatedAt": "2022-09-29T09:26:03.846Z",
"__v": 0
},
{
"_id": "633564a9793cf2a65f7c5daa",
"avatar": null,
"handle": null,
"new": true,
"isLive": false,
"url": "https://ellingsen-group.s3.amazonaws.com/media-1664443559395.png",
"type": "",
"userName": null,
"userId": "62eb5d58512ef25f1352830b",
"isDeleted": false,
"createdAt": "2022-09-29T09:26:01.032Z",
"updatedAt": "2022-09-29T09:26:01.032Z",
"__v": 0
},
{
"_id": "633564e6793cf2a65f7c5dba",
"avatar": null,
"handle": null,
"new": true,
"isLive": false,
"url": "https://ellingsen-group.s3.amazonaws.com/media-1664443621607.png",
"type": "",
"userName": null,
"userId": "6290a0e7f03b0b3585e0f740",
"isDeleted": false,
"createdAt": "2022-09-29T09:27:02.608Z",
"updatedAt": "2022-09-29T09:27:02.608Z",
"__v": 0
},
{
"_id": "633564bf793cf2a65f7c5db0",
"avatar": null,
"handle": null,
"new": true,
"isLive": false,
"url": "https://ellingsen-group.s3.amazonaws.com/media-1664443582519.png",
"type": "",
"userName": null,
"userId": "6290a0e7f03b0b3585e0f740",
"isDeleted": false,
"createdAt": "2022-09-29T09:26:23.519Z",
"updatedAt": "2022-09-29T09:26:23.519Z",
"__v": 0
}
],
"totalPages": 1,
"message": "Get user story Feed Success"
want to change this, so for user 1 ( story 1, 2, 3) user 2 ( story 1,2 ) etc,
here is the query for the result above.
const stories: any = await Story.StoryModel.aggregate([{ $match: { '_id': { $in: combined } } }, { $sort: { userId: -1, createdAt: -1 } }, listStoriesFacetPagination]).exec()
I tried grouping them like this (below) but i get at error saying that stories.groupBy is not a function, I'm stuck at this point and been trying to work this out for the past week.
const groupByUserId = stories.groupBy((userId: any) => {
return story.userId;
});
and it would not work.
You can achieve this by using reduce method, the code will be like this:
const list = [{
'name': 'John',
'userId': '1',
},
{
'name': 'Anne',
'userId': '2',
},
{
'name': 'John',
'userId': '1',
},
{
'name': 'Anne',
'userId': '2',
},
];
const groups = list.reduce((groups, item) => ({
...groups,
[item.userId]: [...(groups[item.userId] || []), item]
}), {});
This code will result in a object like this:
{
"1": [
{'name': 'John', 'userId': '1'},
{'name': 'John', 'userId': '1'}
],
"2": [
{'name': 'Anne', 'userId': '2'},
{'name': 'Anne', 'userId': '2'}
]
}
Hope it help you :D
Here is the solution i found,
const stories: any = await Story.StoryModel.aggregate([
{ $match: { '_id': { $in: combined } } },
{ $group: {
_id: '$userId',
stories: { $push: { url: '$url', _id: '$_id' , isLive: '$isLive', avatar: '$avatar', type:'$type', handle:'$handle', new:'$new', userName:'$userName', userId:'$userId', isDeleted:'$isDeleted', createdAt:'$createdAt' } } } },
{ $sort: { createdAt: 1 } },
listStoriesFacetPagination]).exec()

How to exclude an fields from populated query data? [duplicate]

This question already has answers here:
Mongoose/Mongodb: Exclude fields from populated query data
(4 answers)
Closed 2 years ago.
I just not want to pass user id in discussion array.
Now I getting back from this route like this.
{
"_id": "5f4600ab7ec81f6c20f8608d",
"name": "2",
"category": "2",
"description": "2",
"deadline": "2020-08-10",
"discussion": [
{
"date": "2020-09-03T06:12:15.881Z",
"_id": "5f5089bd265ec85b896f8491",
"user": {
"_id": "5f5089a2265ec85b896f848f",
"userName": "MdJahidHasan01"
},
"text": "3"
},
{
"date": "2020-09-03T06:12:15.881Z",
"_id": "5f5089ae265ec85b896f8490",
"user": {
"_id": "5f5089a2265ec85b896f848f",
"userName": "MdJahidHasan01"
},
"text": "2"
}
]
}
But I want to get like this
{
"_id": "5f4600ab7ec81f6c20f8608d",
"name": "2",
"category": "2",
"description": "2",
"deadline": "2020-08-10",
"discussion": [
{
"date": "2020-09-03T06:12:15.881Z",
"_id": "5f5089bd265ec85b896f8491",
"user": {
"userName": "MdJahidHasan01"
},
"text": "3"
},
{
"date": "2020-09-03T06:12:15.881Z",
"_id": "5f5089ae265ec85b896f8490",
"user": {
"userName": "MdJahidHasan01"
},
"text": "2"
}
]
}
Select does not working here. I just not want to pass user id in discussion array just username.
As I use user id for authorization. So it is not an good idea to send user id.
Project Model
const mongoose = require('mongoose');
const projectSchema = new mongoose.Schema({
name: {
type: String,
require: true
},
category: {
type: String,
require: true
},
description: {
type: String,
require: true
},
deadline: {
type: String,
require: true
},
discussion: [
{
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
date: {
type: Date,
default: Date.now()
},
text: {
type: String,
require: true
}
}
]
});
module.exports = mongoose.model('Project', projectSchema);
Project Details Route
router.get('/:projectId',async (req, res) => {
try {
const project = await Project.findById(req.params.projectId)
.populate('discussion.user', 'userName')
.select('-discussion.user._id')
console.log(project);
await res.status(200).json(project);
} catch (error) {
console.log(error);
return res.status(400).json({ 'error': 'Server Error' });
}
})
Just add this after the .populate:
delete project.discussion._id

mongodb insert into array of objects

I need to find the element by id, then go into the array of objects, find the desired by id and add the field guessId into it and insert the id key into it and assign the array of id. How can I do that ?
https://jsoneditoronline.org/?id=442f1dae0b2d4997ac69d44614e55aa6
In general, I need to create a GuessId field with such a structure
in fact, I am doing the voting, that is, the key in guessId will be go to vote, and the array to it, this is users who vote
{
"_id": "f58482b1-ae3a-4d8a-b53b-ede80fe1e225",
"bio": {
"firstname": "Лена",
"lastname": "фыв",
"middlename": "",
"company": "вв"
},
"files": [
{
"_id": "2e4e40c7-4df6-4974-8d16-bb24cd8134d6",
"destination": "./uploads/f58482b1-ae3a-4d8a-b53b-ede80fe1e225",
"filename": "2e4e40c7-4df6-4974-8d16-bb24cd8134d6.mp3",
"path": "uploads\\f58482b1-ae3a-4d8a-b53b-ede80fe1e225\\2e4e40c7-4df6-4974-8d16-bb24cd8134d6.mp3",
"folder": "f58482b1-ae3a-4d8a-b53b-ede80fe1e225",
"info": {
"size": 20805727,
"mimetype": "audio/mp3",
"encoding": "7bit",
"originalname": "Ахуевший Ленусик (Банк русский стандарт). Выпуск #5..mp3",
"fieldname": "selectedFile"
},
"userId": "5e05da745b21e61ccc84a892",
"date": "2019-12-27T10:19:12.213Z"
},
{
"_id": "81b94dea-ece6-421c-b68a-0aa59332cd0d",
"destination": "./uploads/f58482b1-ae3a-4d8a-b53b-ede80fe1e225",
"filename": "81b94dea-ece6-421c-b68a-0aa59332cd0d.mp3",
"path": "uploads\\f58482b1-ae3a-4d8a-b53b-ede80fe1e225\\81b94dea-ece6-421c-b68a-0aa59332cd0d.mp3",
"folder": "f58482b1-ae3a-4d8a-b53b-ede80fe1e225",
"info": {
"size": 13515683,
"mimetype": "audio/mp3",
"encoding": "7bit",
"originalname": "Выпуск #75 Попрошайка НСВ..mp3",
"fieldname": "selectedFile"
},
"userId": "5e05da745b21e61ccc84a892",
"date": "2019-12-27T10:25:37.710Z"
}
],
"date": "2019-12-27T10:19:12.213Z",
"__v": 1
}
Schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const HabalkaSchema = new Schema({
_id: {
type: String
},
bio: {
firstname: String,
lastname: String,
middlename: String,
company: String
},
files: [
{
_id: {
type: String
},
destination: {
type: String
},
filename: {
type: String
},
path: {
type: String
},
folder: {
type: String
},
info: {
size: {
type: Number
},
mimetype: {
type: String
},
encoding: {
type: String
},
originalname: {
type: String
},
fieldname: {
type: String
},
},
date: {
type: Date,
default: Date.now
},
bio: {
type: Object
},
userId: String,
guessId: {},
}
],
date: {
type: Date,
default: Date.now
}
});
module.exports = Habalka = mongoose.model('habalka', HabalkaSchema);
You can use the positional $ operator like this:
router.put("/habalka/:id/:fileId/:guessId", async (req, res) => {
const result = await Habalka.findOneAndUpdate(
{
_id: req.params.id,
"files._id": req.params.fileId
},
{
"files.$.guessId": {
[req.params.guessId]: ["a", "b"] //todo: get this array from req.body
}
},
{
new: true
}
);
res.send(result);
});
Route url: http://..../habalka/f58482b1-ae3a-4d8a-b53b-ede80fe1e225/2e4e40c7-4df6-4974-8d16-bb24cd8134d6/asda2
This will give you a result like this in the given file:
"files": [
{
"info": {
"size": 20805727,
"mimetype": "audio/mp3",
"encoding": "7bit",
"originalname": "Ахуевший Ленусик (Банк русский стандарт). Выпуск #5..mp3",
"fieldname": "selectedFile"
},
"date": "2019-12-27T10:19:12.213Z",
"_id": "2e4e40c7-4df6-4974-8d16-bb24cd8134d6",
"destination": "./uploads/f58482b1-ae3a-4d8a-b53b-ede80fe1e225",
"filename": "2e4e40c7-4df6-4974-8d16-bb24cd8134d6.mp3",
"path": "uploads\\f58482b1-ae3a-4d8a-b53b-ede80fe1e225\\2e4e40c7-4df6-4974-8d16-bb24cd8134d6.mp3",
"folder": "f58482b1-ae3a-4d8a-b53b-ede80fe1e225",
"userId": "5e05da745b21e61ccc84a892",
"guessId": {
"asda2": [
"a",
"b"
]
}
},
And later if you want to add an item inside one of the guessId arrays, you can use the following code:
router.put("/habalka/:id/:fileId/:guessId", async (req, res) => {
const result = await Habalka.findOneAndUpdate(
{
_id: req.params.id,
"files._id": req.params.fileId
},
{ $push: { [`files.$.guessId.${req.params.guessId}`]: "c" } },
{
new: true
}
);
res.send(result);
});

I am trying to search with populate in express mongoose model, querying after populate in Mongoose ,look what exactly i want

I'm pretty new to Mongoose and MongoDB in general so I'm having a difficult time figuring out if something like this is possible:
I'm trying to filter only those document who has created_by_id not [].
This is schema.
var CampaignSchema = new Schema({
name: { type: String, required: true },
description: { type: String, required: true },
budget: { type: String, required: true },
tags: { type: [ String ], required: true },
status: { type: Number },
payment_id: { type: String },
created_by_id: [{ type: Schema.Types.ObjectId, ref: 'User' }],
attached_file: {
uploaded_on: { type: Date, default: Date.now },
uploaded_by: { type: String, required: true },
},
added_url: {
added_on: { type: Date, default: Date.now },
added_by: { type: String, required: true },
},
updated_by: { type: String },
created_on: { type: Date, default: Date.now },
updated_on: { type: Date }
});
This is code:
_getCampaigns(req, res){
var token = helpersMethods.getToken(req.headers);
var page = parseInt(req.query.page) || 0; //for next page pass 1 here
var limit = parseInt(req.query.limit) || 10;
var term = new RegExp(req.query.search, 'i');
var obj = { "created_by_id": { "$ne": [] } };
if (token) {
Campaign.find(obj)
.populate({
path : 'created_by_id',
match : {
$or: [
{ name: { $regex: term }},
]
}
})
.sort({ updateAt: -1 })
.skip(page * limit)
.limit(limit)
.exec((err, doc) => {
if (err) {
return res.json(err);
}
Campaign.count(obj).exec((count_error, count) => {
if (err) {
return res.json(count_error);
}
return res.json({
total: count,
page: page,
pageSize: doc.length,
campaigns: doc
});
});
});
} else {
return res.status(403).send({success: false, msg: 'Unauthorized.'});
}
}
And i'm getting postman output like this, but i do not want the object who don't have created_by_id array:
{
"total": 2,
"page": 0,
"pageSize": 2,
"campaigns": [
{
"attached_file": {
"uploaded_by": "Demo user",
"uploaded_on": "2019-01-29T11:07:27.475Z"
},
"added_url": {
"added_by": "Demo user",
"added_on": "2019-01-29T11:07:27.475Z"
},
"tags": [
"tag1",
"tags2"
],
"_id": "5c5033ef28f63c72808f2225",
"created_by_id": {
"_id": "5c4965d477e7191c4d40b412",
"name": "Demo user",
"email": "demo#arth.tech",
"phone": "9918XXXXXX",
"type": "1",
"admin_rights": "1",
"password": "$2a$10$6T2ulNN60fBG9/vFgf8XhetkcWb/2zDxGXUMXMRi2Bltn8s1NEkbq",
"__v": 0,
"createdAt": "2019-01-24T07:31:03.327Z",
"loggedIn_at": "2019-01-30T06:33:04.388Z",
"loggedOut_at": "2019-01-24T08:03:44.091Z"
},
"name": "Test Campaign",
"description": "Discription of test campaign",
"budget": "2000",
"updated_by": "Demo User",
"created_on": "2019-01-29T11:07:27.475Z",
"__v": 0
},
{
"attached_file": {
"uploaded_by": "Demo User",
"uploaded_on": "2019-01-29T13:08:48.021Z"
},
"added_url": {
"added_by": "Demo user",
"added_on": "2019-01-29T13:08:48.021Z"
},
"tags": [
"test1",
"test2"
],
"_id": "5c505060b97f871123d97990",
"created_by_id": [],
"name": "Hello Campaign",
"description": "Description of Hello campaign",
"budget": "1000",
"updated_by": "Hello user",
"created_on": "2019-01-29T13:08:48.021Z",
"__v": 0
}
]
}
I want only those objects who has created_by_id, the actual output i want.
{
"total": 1,
"page": 0,
"pageSize": 1,
"campaigns": [
{
"attached_file": {
"uploaded_by": "Demo user",
"uploaded_on": "2019-01-29T11:07:27.475Z"
},
"added_url": {
"added_by": "Demo user",
"added_on": "2019-01-29T11:07:27.475Z"
},
"tags": [
"tag1",
"tags2"
],
"_id": "5c5033ef28f63c72808f2225",
"created_by_id": {
"_id": "5c4965d477e7191c4d40b412",
"name": "Demo user",
"email": "demo#arth.tech",
"phone": "9918XXXXXX",
"type": "1",
"admin_rights": "1",
"password": "$2a$10$6T2ulNN60fBG9/vFgf8XhetkcWb/2zDxGXUMXMRi2Bltn8s1NEkbq",
"__v": 0,
"createdAt": "2019-01-24T07:31:03.327Z",
"loggedIn_at": "2019-01-30T06:33:04.388Z",
"loggedOut_at": "2019-01-24T08:03:44.091Z"
},
"name": "Test Campaign",
"description": "Discription of test campaign",
"budget": "2000",
"updated_by": "Demo User",
"created_on": "2019-01-29T11:07:27.475Z",
"__v": 0
}
]
}
can any one help?
I did , what i want. here is updated code.
_getCampaigns(req, res){
var token = helpersMethods.getToken(req.headers);
var page = parseInt(req.query.page) || 0; //for next page pass 1 here
var limit = parseInt(req.query.limit) || 10;
var term = new RegExp(req.query.search, 'i');
var obj = {};
if (token) {
Campaign.find(obj)
.populate({
path : 'created_by_id',
match : {
$or: [
{ name: { $regex: term }},
]
}
})
.sort({ updateAt: -1 })
.skip(page * limit)
.limit(limit)
.exec((err, docs) => {
if (err) {
return res.json(err);
}else{
docs = docs.filter(function(doc) {
return doc.created_by_id.length != 0;
});
Campaign.count(obj).exec((count_error, count) => {
if (err) {
return res.json(count_error);
}else{
return res.json({
total: count,
page: page,
pageSize: docs.length,
campaigns: docs
});
}
});
}
});
} else {
return res.status(403).send({success: false, msg: 'Unauthorized.'});
}
}

Mongoose upsert without deleting existing fields

The goal of my code is to upsert an object into my database. I created the Schema which is a bit complex
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const objectSchema = new Schema(
{ message: String },
{ discriminatorKey: "type", _id: false }
);
const cameraSchema = new Schema(
{
number: { type: Number, required: true },
name: { type: String, required: true } //Do not update if exists
},
{ _id: false }
);
const mainSchema = new Schema(
{
name: { type: String, required: true, trim: true },
lastUpdateTime: { type: Date, default: Date.now },
objects: [objectSchema]
},
{ collection: "mainCollection" }
);
mainSchema.path("objects").discriminator(
"TypeA",
new Schema(
{
cameras: [cameraSchema],
enabled: { type: Boolean } //Do not update if exists
},
{ _id: false }
)
);
mainSchema.path("objects").discriminator(
"TypeB",
new Schema(
{
available: { type: Boolean, required: true },
enabled: { type: Boolean } //Do not update if exists
},
{ _id: false }
)
);
module.exports = mongoose.model("mainModel", mainSchema);
I am receiving the following object from an external source
{
"name": "040C7",
"objects": [
{
"type": "TypeA",
"cameras": [
{
"number": 1
},
{
"number": 2
},
{
"number": 3
}
]
},
{
"type": "TypeB",
"available": false
}
]
}
If the name exists in my database, I should update every field that I received without removing/updating the fields :
enabled in TypeA and TypeB
name in the camera objects
Otherwise, I should insert it in the database with all the fields and default values for enabled (true) and name (Camera)
I already tried the UpdateOne() function but it is deleting my fields enabled and name when they already are in my database
const obj = new myModel(message);
let upsertObj = obj.toObject();
delete upsertRcm._id;
MyModel.updateOne(
{ name: obj.name },
upsertObj,
{ upsert: true },
err => {
console.log(err)
}
);
How can I say to mongoose to keep fields that are already in my database but not in my received object ?
Document in database
{
"lastUpdateTime": "2018-11-07T09:12:30.750Z",
"name": "040C7",
"objects": [
{
"type": "TypeA",
"enabled": true,
"cameras": [
{
"number": 1,
"name": "Camera",
}
]
},
{
"type": "TypeB",
"enabled": true,
"available": false
}
]
}
After the update with the received object
{
"lastUpdateTime": "2018-11-07T09:14:30.600Z",
"name": "040C7",
"objects": [
{
"type": "TypeA",
"cameras": [
{
"number": 1,
},
{
"number": 2,
},
{
"number": 3,
}
]
},
{
"type": "TypeB",
"available": false
}
]
}
But it should be
{
"lastUpdateTime": "2018-11-07T09:14:30.600Z",
"name": "040C7",
"objects": [
{
"type": "TypeA",
"enabled": true
"cameras": [
{
"number": 1,
"name": "Camera"
},
{
"number": 2,
},
{
"number": 3,
}
]
},
{
"type": "TypeB",
"enabled": true,
"available": false
}
]
}
MongoDB version: 4.0.2
The name field is my unique identifier. Only one type "TypeA", "TypeB", ... will be present in the array. I will never receive 2 "TypeA" in my objects
The list of fields that have to be updated :
objects["TypeA"].cameras.number (value from new object)
objects["TypeB"].available (value from new object)
lastUpdateTime (Date now)
The list of fields that are in the database and should kept :
objects["TypeA"].enabled
objects["TypeA"].cameras.name
objects["TypeB"].enabled

Resources