Mongoose - create reference on model save - node.js

In mongoose, is it possible to create a referenced document while saving the document it is being referenced in? I have tried the below but it does not seem to work for me.
var Model1Schema = new Schema({
foo: String,
child: { ref: 'Model2', type: ObjectId }
});
var Model2Schema = new Schema({
foo: String
});
mongoose.model('Model1', Model1Schema);
mongoose.model('Model2', Model2Schema);
var m = new (mongoose.model('Model1'));
m.set({
foo: 'abc',
child: {
bar: 'cba'
}
}).save();

Mongoose validation won't allow child to be created since it is a reference, so the second-best thing you can do is creating your own function to create an instance with the corrected child, that has already been saved. Something similar to this, I imagine..
var Model1Schema = new mongoose.Schema({
foo: String,
child: { ref: 'Model2', type: mongoose.Schema.ObjectId }
});
var Model2Schema = new mongoose.Schema({
foo: String
});
var Model1 = mongoose.model('Model1', Model1Schema);
var Model2 = mongoose.model('Model2', Model2Schema);
function CreateModel1WithStuff(data, cb) {
if (data.child) { // Save child model first
data.child = Model2(data.child);
data.child.save(function(err) {
cb(err, err ? null : Model1(data));
});
} else { // Proceed without dealing with child
cb(null, Model1(data));
}
}
CreateModel1WithStuff({
foo: 'abc',
child: {
bar: 'cba'
}
}, function(err, doc) {
doc.save();
});

Related

Mongoose post save hook, modifiedPaths() is always empty

Our aim is to have a post hook in place where we can track changed fields.
Model file:
const VariationSchema = new Schema({
_id: {
type: String,
},
title: {
type: String,
},
desc: {
type: String,
},
});
VariationSchema.post('save', async function (doc) {
console.log(doc.modifiedPaths());
});
const VariationModel = mongoose.model('variation', VariationSchema);
module.exports = {
VariationModel,
VariationSchema,
};
Service file:
const variationDocument = await VariationModel.findById(variationId).select({});
variationDocument.desc = (Math.random() + 1).toString(36).substring(7);
await variationDocument.save();
return variationDocument.toJSON();
No matter what we do, doc.modifiedPaths() is always empty. Please help

POST array of objects using req.body parser

I am trying to post a simple request which includes array of objects. I have created a model and passing the data as per the model.
I am having trouble accessing body parameters as it contains array of data.
I am able to store line item data by req.body.tasks[0]
which is not a standrad way of storing details in mongodb.
I am looking for a standrad way of storing array of data in mongodb
Controller:
let createBug = (req, res) => {
console.log(req.body.tasks[0].subtask[0].description)
for (var key in req.body) {
if (req.body.hasOwnProperty(key)) {
item = req.body[key];
console.log(item);
}
}
const createBug = new listModel({
title: req.body.title,
tasks: [{
title: req.body.tasks[0].title,
description: req.body.tasks[0].description,
subtask: [{
description: req.body.tasks[0].subtask[0].description
}]
}]
}).save((error, data) => {
if (data) {
let apiResponse = response.generate(false, null, 201, data);
res.status(201).send(apiResponse);
} else {
let apiResponse = response.generate(true, error, 404, null);
res.status(404).send(apiResponse);
}
});
};
body:
{
"title":"sample title",
"tasks":[{
"title": "task 1",
"description":"task1 description",
"subtask":[{
"description":"task3 description"
}]
}]
}
Model:
const mongoose = require("mongoose");
const mySchema = mongoose.Schema;
let subtask = new mySchema({
description: String
})
let taskdata = new mySchema({
title: String,
description: String,
subtask: [subtask]
});
let listSchema = new mySchema({
title: {
type: String,
require: true,
},
tasks: [taskdata],
owner: {
type: mongoose.Schema.Types.ObjectId,
ref: "users",
}
});
module.exports = mongoose.model("list", listSchema);
I think you're overcomplicating things here a little bit. The request body exactly matches the model definitions, so you can simply pass the req.body object to your mongoose model:
const createBug = new listModel(req.body).save((error, data) => { ... }

GraphQL Resolver for Interface on same Mongoose collection

I'm creating a GraphQL server that uses Mongoose and GraphQLInterfaceType. I have a GraphQLInterfaceType of Books and sub types of SchoolBooksType and ColoringBookType. in my Mongoose Schema I specified that both SchoolBooks and ColoringBooks are to be stored in the same books collection
const coloringSchema = new Schema({
title: String,//Interface
pages: String
});
module.exports = mongoose.model("ColoringBook", coloringSchema , "books");
const schoolSchema = new Schema({
title: String, //Interface
subject: String
});
module.exports = mongoose.model("SchoolBook", schoolSchema , "books");
Here is one of my types
const SchoolBookType = new GraphQLObjectType({
name: "SchoolBook",
interfaces: [BooksInterface],
isTypeOf: obj => obj instanceof SchoolBook,
fields: () => ({
title: { type: GraphQLString },
subject: { type: GraphQLString }
})
});
Here is my query: But I don't know what to return, if I need to combine the two collections into the same array?
books: {
type: new GraphQLList(BooksInterface),
resolve() {
return SchoolBook.find({}) //<---- What to return?
}
}
Here is my query:
{
books{
title
... on ColoringBook{
pages
}
... on SchoolBook{
subject
}
}
}
Any help would be great, Thank you.
I guess you can use an async resolver, and concat both queries.
resolve: async () => {
const schoolBooks = SchoolBook.find({}).exec()
const coloringBooks = ColoringBook.find({}).exec()
const [sbooks, cbooks] = await Promise.all([schoolBooks, coloringBooks])
return [...sbooks, ...cbooks]
}

MongoDB : Nested array update issue

I am using MEAN stack to display the following array in a grid.
Nested array:
{
"_id":"1",
"appDomain":[
{
"appDomainName":"XYZ",
"dashboard":[
{
"envName":"UAT",
"envDetails":[
{
"hostnme":"ABC",
"ip":"sdsdsdsd",
"cpu":"-------",
"memory":"-------",
"disk":"-------",
"downtime":"sdsdsdsd",
"version":"dsdsdsd",
"hostDetails":[
{
"hostHdrName":"tomcat",
"hostDetails":[
{
"hostname":"FLT",
"status":"UP",
"path":"dfdf",
"host":"sdsdsd",
"port":"1112"
}
]
}
]
}
]
}
]
},
{
"appDomainName":"ABC",
"dashboard":[
{
"envName":"UAT",
"envDetails":[
{
"hostnme":"ABC",
"ip":"sdsdsdsd",
"cpu":"-------",
"memory":"-------",
"disk":"-------",
"downtime":"sdsdsdsd",
"version":"dsdsdsd",
"hostDetails":[
{
"hostHdrName":"tomcat",
"hostDetails":[
{
"hostname":"FLT",
"status":"UP",
"path":"dfdf",
"host":"dfdfdf",
"port":"1112"
},
{
"hostname":"SHP",
"status":"DOWN",
"path":"dfdfdf",
"host":"fgfgfg",
"port":"1112"
}
]
}
]
}
]
}
]
}
]
}
Mangoose update: I am using express.js to add/update and delete the entry in MangoDB. I am facing the issue in update in nested array. Please see below the code for update the nested array.
router.post('/appDomain/update/:appDomainName/:envName', function(req, res, next) {
AppDomain.findOne({}, {_id: 0, appDomain: {$elemMatch: {appDomainName: req.params.appDomainName,}}}, function (err, appDomain) {
if(appDomain.appDomain[0].dashboard[0].envName == req.params.envName )
appDomain.appDomain[0].dashboard[0].envDetails.push({})
appDomain.save(function (err) {
if(err) {
console.error('ERROR!');
}
});
res.json(appDomain);
});
})
However, it is not updating. It would be great if anyone can help me out..
AppDomainModel Schema
var Schema = mongoose.Schema;
var serverDetails = new Schema({
hostname: String,
ip: String,
status: String,
path: String,
host: String,
port: String
});
var hostDetail = new Schema({
hostHdrName: String,
hostDetails: [serverDetails]
});
var keyValue = new Schema({
keyHdrName: String,
keyValueData: [{key: String, value:String}]
});
var envSchema = new Schema({
hostnme: String,
ip: String,
cpu: String,
memory: String,
disk: String,
downtime: String,
version: String,
hostDetails: [hostDetail],
keyValues: [keyValue],
activities:{
recent: [keyValue],
planned: [keyValue]
}
});
var dashboardSchema = new Schema({
envName: String,
envDetails: [envSchema]
});
var appDomainSchema = new Schema({
_id:String,
appDomain:[{
appDomainName: {type:String,index: { unique: true }},
dashboard: [dashboardSchema]
}]
});
var AppDomain = mongoose.model('AppDomain', appDomainSchema);
var Dashboard = mongoose.model('Dashboard', dashboardSchema);
var EnvSchema = mongoose.model('EnvSchema', envSchema);
After updating.I am using the following function to check the value .. However the updated one is not available in the DB.
router.get('/app/domain/get/:appDomainName', function(req, res, next) {
AppDomain.find({}, {_id: 0, appDomain: {$elemMatch: {appDomainName: req.params.appDomainName,}}},function (err, appDomain) {
res.json(appDomain);
});
});
After a long struggle, I figured it out . See below the answer.
var data2 = {};
AppDomain.update(
{_id:1,'appDomain.appDomainName': req.params.appDomainName,'dashboard.$.envName':{$nin:[req.params.envName]}},
{$push: {'appDomain.$.dashboard.0.envDetails':{'envDetails':[data2]}}},
function(err, model) {
console.log(err);
}
);
I suspect your changes are actually being saved successfully, but you're returning the response too soon. Try this:
appDomain.save(function (err, savedAppDomain) {
if(err) {
console.error('ERROR!');
next(err);
return;
}
res.json(savedAppDomain):
});
// Removed:
//res.json(appDomain);
//});

Populate with inherited document in Mongoose

I am trying to create a database schema for the following model:
I am not sure what the better way to represent this in a MongoDb would be, but since I am using Mongoose and there is a plugin for inheritance, I am trying the following:
var mongoose = require('mongoose')
, extend = require('mongoose-schema-extend')
, Schema = mongoose.Schema
, ObjectId = mongoose.Schema.Types.ObjectId
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function callback () {
//Mashup and Item are bound (itemSchema is a sub-doc)
var itemSchema = new Schema({
pos: { top: Number, left: Number }
, size: { width: Number, height: Number }
, component: { type: ObjectId, ref: 'Component' }
})
var mashupSchema = new Schema({
name: String
, desc: String
, size: { width: Number, height: Number }
, items: [itemSchema]
})
var componentSchema = new Schema({
name: String
, desc: String
}, { discriminatorKey : '_type' })
var imageComponentSchema = componentSchema.extend({
url: String
})
var textComponentSchema = componentSchema.extend({
text: String
})
var htmlComponentSchema = componentSchema.extend({
html: String
})
var webComponentSchema = componentSchema.extend({
page: { type: ObjectId, ref: 'Page' }
, selector: { type: ObjectId, ref: 'Selector' }
})
var pageSchema = new Schema({
name: String
, desc: String
, url: String
, active: { type: Boolean, default: false }
, webComponents: [{ type: ObjectId, ref: 'WebComponent' }]
})
var selectorSchema = new Schema({
desc: String
, url: String
, cssPath: String
})
///MODELS
var Mashup = db.model("Mashup", mashupSchema)
var Component = db.model("Component", componentSchema)
var ImageComponent = db.model("ImageComponent", imageComponentSchema)
var TextComponent = db.model("TextComponent", textComponentSchema)
var HtmlComponent = db.model("HtmlComponent", htmlComponentSchema)
var WebComponent = db.model("WebComponent", webComponentSchema)
var Page = db.model("Page", pageSchema)
var Selector = db.model("Selector", selectorSchema)
//CREATE
//a new empty mashup
//var aMashup = new Mashup({ name: "Test" });
Mashup.create({ name: "Test" }, function (err, mashup) {
if (err) return
console.log("Saved: empty mashup")
//mashup saved, create a webComponent
var aWebComponent = new WebComponent({ name: "Map", desc: "A map" })
//create a page
var aPage = new Page({ name: "Maps", desc: "Google Maps", url: "http://maps.google.com" })
aPage.webComponents.push(aWebComponent)
aWebComponent.page = aPage
//create a selector
var aSelector = new Selector({desc: "Just the map", url: "maps.google.com", cssPath: "#map" })
aWebComponent.selector = aSelector
//save the component
aWebComponent.save(function(err) {
if (err) return
console.log("Saved: WebComponent")
aPage.save(function(err) {
if (err) return
console.log("Saved: the Page")
aSelector.save(function(err) {
if (err) return
console.log("Saved: the Selector")
//finally add the item with the new component
var item = { pos: { top:6, left:10 }, size: { width:100, height:100}, component: aWebComponent }
mashup.items.push(item)
mashup.save(function (err) {
if (err) return
console.log("Saved: mashup with item (WebComponent with Page and Selector)")
//POPULATE
Mashup
.find({})
.populate("items.component")
.exec(function (err, mashup) {
if (err) console.log(err)
console.log(mashup);
})
})
})
})
})
})
});
This is a use case scenario, where a user creates a Mashup and then adds a new Item to it by creating a new WebComponent. I need that Item class because each different mashup should be able to have "instances" (i.e. the Items) of existing Components.
Now, I am new to Mongoose and I am sure things could be done differently. Any suggestion here is welcome. However, when I try to query the Mashups populating the results, the output I get is:
Saved: empty mashup
Saved: WebComponent
Saved: the Page
Saved: the Selector
Saved: mashup with item (WebComponent with Page and Selector)
[ { __v: 1,
_id: 520a8aae3c1052f723000002,
name: 'Test',
items:
[ { component: null,
_id: 520a8aaf3c1052f723000006,
size: [Object],
pos: [Object] } ],
size: {} } ]
component should be populated but it is not. I guess this is because it expects a Componentwhile it gets a WebComponent. How do I fix this? Should I stop trying with inheritance? What other ways are there to create a DB schema for this model?
Doh.. changing
var componentSchema = new Schema({
name: String
, desc: String
}, { discriminatorKey : '_type' })
to
var componentSchema = new Schema({
name: String
, desc: String
}, { collection : 'components', discriminatorKey : '_type' })
Fixes the issue. Not sure why.

Resources