Promise block my reference on MongoDB/Node.js - node.js

I use Mongoose to MongoDb with Node.js/React/GraphQL.
I have a document Article who is related to another document Event who is related to several documents Tags. When I try to save my documents I always have a pending promise to into my tags in the Event document.
Result :
- Article is save related to Event
- Event is saved but not related to Tags
- Tags are saved but not related to Event
Expecting :
- Article is save related to Event
- Event is saved and related to Tags
- Tags are saved and related to Event
Two time, when my server was on the beginning is working without pending and error. So I think my problem is a time problem, but I don't know how to resolve it. I try to put some timeout but without success.
I have the following schema in Mongoose/MongoDb
//mode/event.js
'use strict';
//import dependency
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
//create new instance of the mongoose.schema. the schema takes an object that shows
//the shape of your database entries.
var EventSchema = new Schema({
createdAt: {
type: Date,
default: Date.now
},
name: {
type: String,
required: 'Kindly enter the name of the event'
},
description: String,
site_web: String,
themes: {
type: String,
enum: ['Economics', 'Politics', 'Bitcoins', 'Sports'],
default: 'Economics'
},
picture: String,
event_date_start: Date,
event_date_end: Date,
type_event: {
type: String,
enum: ['Confrontation','Standard'],
default: 'Standard'
},
teams: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Team'
}],
tags: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Tag'
}]
});
//export our module to use in server.js
module.exports = mongoose.model('Event', EventSchema);
//model/tag.js
'use strict';
//import dependency
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
//create new instance of the mongoose.schema. the schema takes an object that shows
//the shape of your database entries.
var TagSchema = new Schema({
name: {
type: String,
required: 'Kindly enter the name of the tag'
},
});
//export our module to use in server.js
module.exports = mongoose.model('Tag', TagSchema);
//model/article.js
'use strict';
//import dependency
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
//create new instance of the mongoose.schema. the schema takes an object that shows
//the shape of your database entries.
var ArticleSchema = new Schema({
// _id: String,
createdAt: {
type: Date,
default: Date.now
},
event: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Event',
required: 'Kindly enter the event'
},
body: String,
type: {
type: String,
enum: ['Confrontation','Standard'],
default: 'Standard'
},
url_source: String,
themes: {
type: String,
enum: ['Economics', 'Politics', 'Bitcoins', 'Sports'],
default: 'Economics'
},
type_media: {
type: String,
enum: ['video', 'web', 'podcast'],
default: 'web'
},
article_date: Date,
});
//export our module to use in server.js
module.exports = mongoose.model('Article', ArticleSchema);
In my schema Node.js/GraphQL I have the resolve function
createArticle: {
type: ArticleType,
args: {
event: {type: EventCreateType},
body: {type: GraphQLString},
type: {type: articleType},
url_source: {type: GraphQLString},
themes: {type: themesType},
//type_media: {type: new GraphQLList(mediaType)}
type_media: {type: mediaType},
article_date : {type: GraphQLString}
},
resolve: async (source, params) => {
if (params.event) {
var eventparams = params.event;
var tagparams = params.event.tags;
params.event.tags = null;
params.event = null;
var tagIds = [];
//traitement des tags
var inEvent = await EventsModel.findOne({'name':eventparams.name});
if(!inEvent){
inEvent = new EventsModel(eventparams);
if(tagparams){
if(tagparams.length !=0){
tagIds = await tagparams.map(async function(c) {
var inTag = await TagsModel.findOne(c);
if(!inTag){
inTag = new TagsModel(c);
inTag.save(function(err) {
if (err) {
console.log(err);
}});
}
return inTag;
});
console.log('******************************Le tableau**************************');
console.dir(tagIds);
console.log('********************************************************');
//inEvent.tags = tagIds;
Promise.all(tagIds).then(function(savedObjects) {
console.log('********************************************************');
console.log('Le Inside Tab:',savedObjects);
console.log('********************************************************');
// Do something to celebrate?
inEvent.tags = savedObjects;
}).catch(function(err) {
// one or both errored
console.log(err);
});
}
}
inEvent.save(function(err) {
if (err) {
console.log(err);
}});
}
console.log('*******************propriete inEvent*****************************');
console.dir(inEvent);
console.log('********************************************************');
var articleModel = new ArticlesModel(params);
articleModel.event = inEvent;
console.log('***********************propriete Article before save****************');
console.dir(articleModel);
console.log('********************************************************');
articleModel.save(function(err, article) {
if (err) {
console.log(err);
}
if (article) {
return ArticlesModel.findById(article._id)
.populate('article')
.populate('event')
.exec(function(error, articles) {
console.log('article saved: succes')
articles.article.articles.push(articles);
articles.article.save(function(err, article) {
if (err) {
console.log(err);
}
});
return articles;
})
}
});
return articleModel;
}
else{
console.log('verif 3');
}
console.log('verif 4');
}
},

Related

express.js mongoose populate 2 model

I'm want to join collection mongoDB but I've 2 model in project.
ADMINDETAIL and ADMINDETAIL get UID from member.model.js .
How I populate that.
queue.model.js
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var queueSchema = Schema(
{
QUEUE: String,
UID: String,
DATETIME: String,
ADMIN_ID: String,
USERDETAIL:{
type: Schema.Types.String,
ref:"MEMBER"
},
ADMINDETAIL:{
type: Schema.Types.String,
ref:"MEMBER"
},
},
{
collection: "QUEUE"
}
);
var QUEUE = mongoose.model("QUEUE", queueSchema);
module.exports = QUEUE;
member.model.js
var mongoose = require("mongoose");
var memberSchema = mongoose.Schema(
{
UID: {type: String},
NAME: {type: String},
SURNAME: {type: String},
IDNUMBER: {type: String},
PHONE: {type: String},
ADDRESS: {type: String},
},
{
collection: "MEMBER"
}
);
var MEMBER = mongoose.model("MEMBER", memberSchema);
module.exports = MEMBER;
queue.router.js
// GET QUEUE BY USER
router.get("/byuid/:UID", (req, res) => {
var {UID} = req.params;
Queue.find({UID})
.populate({Path:"USERDETAIL",model:"MEMBER"})
.populate({Path:"ADMINDETAIL",model:"MEMBER"})
.exec((err, data) => {
if (err) return res.status(400).send(err);
return res.status(200).send(data);
});
});
Error I got.
TypeError: utils.populate: invalid path. Expected string. Got typeof `object`
change the type of filed from String to ObjectId like this:
USERDETAIL:{
type: Schema.Types.ObjectId ,
ref:"MEMBER"
},
ADMINDETAIL:{
type: Schema.Types.ObjectId ,
ref:"MEMBER"
},
},
add your new data after that you can like this for population:
.populate("USERDETAIL ADMINDETAIL")
or
.populate([{
path: 'USERDETAIL ',
model: 'MEMBER'
}, {
path: 'ADMINDETAIL',
model: 'MEMBER'
}])
I think you are missing []

It says items is undefined

The code is show below.
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const user = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
resetToken: String,
resetExpiration: String,
products: [{type: mongoose.Types.ObjectId, required: true, ref: 'Shop'}],
cart: {
items: [
{
productId: {type: mongoose.Types.ObjectId, ref: 'Shop', required: true},
quantity: {type: Number, required: true},
}
]
},
});
user.methods.addToCart = (product) => {
const itemIndex = this.cart.items.findIndex(prod => {
return prod.productId.toString() === product._id.toString();
});
let newQuantity = 1;
const updatedCartItems = [...this.cart.items];
if(itemIndex >= 0) {
newQuantity = this.cart.items[itemIndex].quantity + 1;
updatedCartItems[itemIndex].quantity = newQuantity;
} else {
updatedCartItems.push({
productId: product,
quantity: newQuantity
});
}
const updatedCart = {
items: updatedCartItems
}
this.cart = updatedCart;
return this.save();
}
const model = mongoose.model('User', user);
module.exports = model;
I am trying to store product in the cart instance method as per above schema, but when i send product from my controller to addToCart it says items is undefined on this.cart.items. I haven't used instance method much in mongoose so, i don't know this issue is it with schema or general problem.
let me know if you need any other information.
It was a silly mistake, actually i was using arrow function. so it wasn't bind to schema.

Cast to ObjectId failed for value "" at path "_id" for model

I have already checked the other entries on StackOverflow, but it did not help.
I am building a RESTapi with node.js, and I am using MongoDB with mongoose
I have a Schema that contains three different models. I am able to save POST request to the entry. I am sure that entry is saved because I checked on atlas.mongo. However, I have a problem when I am trying to use GET request.
It gives this error:
Cast to ObjectId failed for value "" at path "_id" for model
These are my Models: (These models are in different files)
const Model1 = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
word1: { type: [String], require: true }
});
----------------------------------------------
const Model2 = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
word2: { type: [String], require: true }
});
----------------------------------------------
const Model3 = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
element1: { type: [String], default: ""},
element2: { type: [String], default: ""}
});
----------------------------------------------
const Word = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
md3: { type: mongoose.Schema.Types.Mixed, ref: 'Model3', require: true },
md2: { type: mongoose.Schema.Types.Mixed, ref: 'Model2', require: true },
md1: { type: mongoose.Schema.Types.Mixed, ref: 'Model1', require: true }
});
This is my POST request:
exports.entry_create = (req, res, next) => {
const newModel3 = new Model3({
_id: new mongoose.Types.ObjectId(),
element1: req.body.element1,
element2: req.body.element2
});
const newModel2 = new Model2({
_id: new mongoose.Types.ObjectId(),
word2: req.body.word2
});
const newModel1 = new Model1({
_id: new mongoose.Types.ObjectId(),
word1: req.body.word1
});
const newEntry = new Word({
_id: new mongoose.Types.ObjectId(),
md3: newModel3,
md2: newModel2,
md1: newModel1
});
newEntry
.save(); // i have also then() and catch() part
};
This is where I got the error on Postman
exports.entry_get_all = (req, res, next) => {
Word.find()
.select('_id md3 md2 md1')
.populate('md3')
.populate('md2')
.populate('md1')
.exec()
.then(docs => {
res.status(200).json({
numOfEntries: docs.length,
Entries: docs.map(doc => {
return {
_id: doc._id,
md3: doc.md3,
md2: doc.md2,
md1: doc.md1,
request: { type: 'GET' }
}
})
});
}); // i have also catch() part
};
What could be the problem? Is _id's of md3, md2 & md1 returns null?
I believe it has to do with your references md1, md2 and md3. The way you reference another model is by the _id, which in your case it's and ObjectId. That being said, when you define md1, md2, and md3 you say the type is mixed, not an ObjectId. Do this instead:
const Word = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
md3: { type: mongoose.Schema.Types.ObjectId, ref: 'Model3', require: true },
md2: { type: mongoose.Schema.Types.ObjectId, ref: 'Model2', require: true },
md1: { type: mongoose.Schema.Types.ObjectId, ref: 'Model1', require: true }
});
Also note: You don't need to explicitly create a new ObjectId when creating an instance of your model. If using mongoose, it creates the _id for you! So you can just create a new Word like this:
let md1 = null;
let md2 = null;
let md3 = null;
const newModel3 = new Model3({
element1: req.body.element1,
element2: req.body.element2
});
// Save newModel3
newModel3.save()
.then((_md3) => {
md3 = _md3;
const newModel2 = new Model2({
word2: req.body.word2
});
return newModel2.save();
})
.then((_md2) => {
md2 = _md2;
const newModel1 = new Model1({
word1: req.body.word1
});
return newModel1.save();
})
.then((_md1) => {
md1 = _md1
const newEntry = new Word({
md3: md3._id,
md2: md2._id,
md1: md1._id
});
return newEntry.save();
})

How to implement partial document embedding in Mongoose?

I have a simple relation between topics and categories when topic belongs to a category.
So schema looks like this:
const CategorySchema = new mongoose.Schema({
name: String,
slug: String,
description: String
});
And topic
const TopicSchema = new mongoose.Schema({
category: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Category'
},
title: String,
slug: String,
body: String,
created: {type: Date, default: Date.now}
});
I want to implement particular embedding of category into topic
{
category: {
_id: ObjectId('abc'),
slug: 'catslug'
},
title: "Title",
slug: "topictitle",
...
}
It will help me avoid unnecessary population and obtain performance bonuses.
I don't want to embed whole document because I want to changes categories sometimes (it is a rare operation) and maintain references.
Hope this helps, done it in my own project to save some RTTs in common use cases. Make sure you're taking care of both copies on update.
parent.model.js:
const mongoose = require('mongoose');
const childEmbeddedSchema = new mongoose.Schema({
_id: {type: mongoose.Schema.Types.ObjectId, ref: 'Child', auto: false, required: true, index: true},
someFieldIWantEmbedded: {type: String}
});
const parentSchema = new mongoose.Schema({
child: { type: childEmbeddedSchema },
moreChildren: { type: [{type: childEmbeddedSchema }] }
});
module.exports = mongoose.model('Parent', parentSchema);
child.model.js:
const mongoose = require('mongoose');
const childSchema = new mongoose.Schema({
someFieldIWantEmbedded: {type: String},
someFieldIDontWantEmbedded: {type: Number},
anotherFieldIDontWantEmbedded: {type: Date}
});
module.exports = mongoose.model('Child', childSchema);
parent.controller.js:
const mongoose = require('mongoose');
const Parent = require('path/to/parent.model');
exports.getAll = (req, res, next) => {
const query = Parent.find();
// only populate if requested! if true, will replace entire sub-document with fetched one.
if (req.headers.populate === 'true') {
query.populate({
path: 'child._id',
select: `someFieldIWantEmbedded ${req.headers.select}`
});
query.populate({
path: 'moreChildren._id',
select: `someFieldIWantEmbedded ${req.headers.select}`
});
}
query.exec((err, results) => {
if (err) {
next(err);
} else {
res.status(200).json(results);
}
});
};

Mongoose: utils.populate: invalid path. Expected string. Got typeof 'undefined'

I am not a totally new populate user but now I do not know what's wrong.
Here I need to populate my designerId which is type of ObjectId. Take a look at my route.
ordersAdminRouter.route('/customorder/add')
.post(function(req, res){
body = req.body;
console.log(body);
CustomOrders.create(body, function(err, saved){
if (err) throw err;
Designs.findByIdAndUpdate(saved.designId, {$set: {status: 'Order Sent'}}, {new: true}).exec()
.then(function(updated){
return CustomOrders.findById(saved._id).populate(saved.designId).exec();
})
.then(function(orders){
res.json(orders);
})
.then(undefined, function(err){
console.log(err);
})
});
});
saved._id is working because when I remove the populate, it returns the document that I need without the populated document of course.
Take a look at my schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var customOrderSchema = new Schema({
designId: { type: Schema.Types.ObjectId, ref: 'customDesigns' },
size: { type: String },
quantity: { type: Number },
totalPrice: { type: Number },
paymentMode: { type: String },
rcpt_img: { type: String },
refNumber: { type: String }
});
module.exports = mongoose.model('customOrders', customOrderSchema);
Here is my customDesigns schema.
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var customDesignSchema = new Schema({
item_name: { type: String },
price: { type: Number, default: 0 },
img_url_front: { type: String },
img_url_back: { type: String },
designer: { type: Schema.Types.ObjectId, ref: 'users' },
color: { type: String },
designDate: { type: Date, default: Date.now() },
status: { type: String, default: 'For Qoutation' }
});
module.exports = mongoose.model('customDesigns', customDesignSchema);
I need to admit that I am new to promises on mongoose & express and this is my first time doing so. But using populate, i use it more than I can think of. Any suggestions?
return CustomOrders.findById(saved._id).populate('designId').then(.. your code);
By the way, you dont must use .exec() then you want execute your query, .then executes query as well. You can skip .exec()
http://mongoosejs.com/docs/populate.html
http://mongoosejs.com/docs/api.html#query_Query-populate

Resources