add to collection in sails - node.js

Maybe my question is very simple and superficial
But please, guide me
my Place Model is:
module.exports = {
attributes: {
title: {
type: 'string',
},
body: {
type: 'string',
columnType: 'text',
},
address: {
type: 'string',
columnType: 'text',
},
x_map: {
type: 'number',
columnType: 'float'
},
y_map: {
type: 'number',
columnType: 'float'
},
like: {
type: 'number',
columnType: 'integer'
},
dis_like: {
type: 'number',
columnType: 'integer'
},
visited: {
type: 'number',
columnType: 'integer'
},
emtiaz: {
type: 'number',
columnType: 'integer'
},
tags: {
type: 'string',
},
city:{
collection: 'city',
via: 'place_owner'
},
},
};
And my City Model is:
module.exports = {
attributes: {
title: {
type: 'string',
},
ostan_owner :{
model: 'ostan'
},
place_owner: {
model: 'place'
}
},
};
And my place controller is:
create: function (req, res, next) {
Place.create(req.params.all, function place_created(err,new_place){
if(err && err.invalidAttributes) {
err = validator(Place, err);
return res.json({'status':false, 'errors':err.Errors});
}
else{
new_place.add('city',req.param('city'));
new_place.save(function (err) {
if(err){
return res.json({'status':false,'errors':err});
}
else {
res.json({'status':true,'result':new_place});
}
});
}
});
},
now whene i try to create new place and add new city to collection it give me Error:
TypeError: Cannot read property 'add' of undefined
at place_created (C:\Programing_workspace\gardeshgar_sailsV1\gardeshgar\api\controllers\PlaceController.js:28:19)
whene i use model_X.addToCollection i recive same error
i use sails v 1.0 and i am new in sails.js
please Help me

Sails.js 1.0 does not support .add() and .save() anymore.
Please use .addToCollection() instead.
await Place.addToCollection(new_place.id, 'city').members(req.param('city'));
The other problem is that sails.js 1.0 does not automatically fetch the created/updated Object (new_place in your case).
You need to add { fetch: true } in order the get the created object.
For example:
User.create({...}, function(err, createdUser) {
//...
}, { fetch: true });
Or using await:
var createdUser = await User.create({...}).fetch();
I think, in your case, the solution should be:
create: function (req, res, next) {
Place.create(req.params.all, function place_created(err,new_place){
if(err && err.invalidAttributes) {
err = validator(Place, err);
return res.json({'status':false, 'errors':err.Errors});
}
else{
await Place.addToCollection(new_place.id, 'city').members(req.param('city')).exec(function (err) {
if(err){
return res.json({'status':false,'errors':err});
}
else {
res.json({'status':true,'result':new_place});
}
});
}
}, { fetch: true });
},

Related

Mongoose: populate an object inside an array

I have a schema exported like that:
const PackageSchema = new Schema({
name: { type: String, required: true },
maneuver: [
{
maneuverId: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: ManeuverMainly,
},
period: { type: String, enum: ["day", "night"], required: true },
},
],
timestamp: { type: Date, default: Date.now() },
});
When I make a find() like that:
Package.find().populate("maneuver", "name").exec((err, data) => {
if (err) {
res.status(500).send({ message: "Failed!" });
return;
}
res.status(200).send(data);
});
My populate method does not work. How can I populate my every maneuverId from PackageSchema with my name column from ManeuverMainlySchema?
Obs: my ManeuverMainlySchema bellow:
const ManeuverMainlySchema = new Schema({
name: { type: String, required: true },
description: { type: String, required: true },
timestamp: { type: Date, default: Date().now },
});
taken from Mongoose populate with array of objects containing ref you have to specify the field within the object of the array you want to populate against.
Package.find().populate("maneuver.maneuverId", "name").exec((err, data) => {
if (err) {
res.status(500).send({ message: "Failed!" });
return;
}
res.status(200).send(data);
});
Package.find().populate(["maneuver.maneuverId", "name"]).exec((err, data) => {
if (err) {
res.status(500).send({ message: "Failed!" });
return;
}
res.status(200).send(data);
});
If you want to populate one of them, don't need to use array in populate as
populate("maneuver.maneuverId") or populate("name").

I want to pass array object inside array subdocument in mongoose

Here is my Schema
I am trying to add replies array inside answers array. If someone answers a question and if someone wants to reply on the given answer
const mongoose = require("mongoose");
const { ObjectId } = mongoose.Schema;
const questionSchema = new mongoose.Schema(
{
postedBy: {
type: ObjectId,
required: true,
ref: "User",
},
question: {
type: String,
required: true,
},
photo: {
data: String,
required: false,
},
answers: [
{
userId: { type: ObjectId, ref: "User" },
answerType: {
data: String,
required: false,
},
answer: String,
replies: [
{
userId: { type: ObjectId, ref: "User" },
reply: String,
replyType: {
data: String,
required: false,
},
},
],
},
],
questionType: {
data: String,
required: false,
},
createdAt: {
type: Date,
required: true,
default: Date.now,
},
},
{ timeStamps: true }
);
module.exports = mongoose.model("Question", questionSchema);
Here is my Controller method
exports.postReply = (req, res) => {
const reply = req.body.reply || "";
const userId = req.user._id || "";
const answerId = req.body.answerId || "";
Question.findByIdAndUpdate(
{ _id: answerId },
({ $push: { answers: { answer: { replies: { reply, userId } } } } },
{ new: true }),
(err, newReply) => {
if (err) {
res.status(400).json({
error: errorHandler(err),
});
} else {
res.json({
msg: "Reply posted successfully",
newReply,
});
}
}
);
};
I feel I am going wrong on the findOneAndUpdate method. I am getting no error on the console but newReply comes null. Any help will be appreciated.
I would suggest you using the $addToSet instead of the $push operator as you are adding a document to the array. (see: https://docs.mongodb.com/manual/reference/operator/update/addToSet/).
If you want to add more than one document to the array, refer also to the $each operator together with $addToSet.
So your coding can look similiar to this (note: the variable 'yourDocument' is the document you want to add):
Question.findByIdAndUpdate(
{ _id: answerId },
{ $addToSet: { answers: yourDocument } },
{ new: true },
(err, newReply) => {
if (err) {
res.status(400).json({
error: errorHandler(err),
});
} else {
res.json({
msg: "Reply posted successfully",
newReply,
});
}
}
);
};
The problem is clearly the parentesis around
({ $push: { answers: { answer: { replies: { reply, userId } } } } }, { new: true })
Doing this console.log( ({a:1}, {b:2}) ); will log {b: 2} which means you are doing this
Question.findByIdAndUpdate( { _id: answerId }, { new: true }, (err, newReply) => {
So remove the parentesis and you should be good
Question.findByIdAndUpdate(
{ _id: answerId },
{ $push: { answers: { answer: { replies: { reply, userId } } } } },
{ new: true },
(err, newReply) => {
if (err) {
res.status(400).json({
error: errorHandler(err),
});
} else {
res.json({
msg: "Reply posted successfully",
newReply,
});
}
}
);

Cast to ObjectId failed for value at path for model error

This is my Profile Schema:
const mongoose = require('mongoose');
const ProfileSchema = new mongoose.Schema({
user: {
// Special field type because
// it will be associated to different user
type: mongoose.Schema.Types.ObjectId,
ref: 'user',
},
company: {
type: String,
},
website: {
type: String,
},
location: {
type: String,
},
status: {
type: String,
required: true,
},
skills: {
type: [String],
required: true,
},
bio: {
type: String,
},
githubusername: {
type: String,
},
experience: [
{
title: {
type: String,
required: true,
},
company: {
type: String,
required: true,
},
location: {
type: String,
},
from: {
type: Date,
required: true,
},
to: {
type: Date,
},
current: {
type: Boolean,
default: false,
},
description: {
type: String,
},
},
],
education: [
{
school: {
type: String,
required: true,
},
degree: {
type: String,
required: true,
},
fieldofstudy: {
type: String,
required: true,
},
from: {
type: Date,
required: true,
},
to: {
type: Date,
},
current: {
type: Boolean,
default: false,
},
description: {
type: String,
},
},
],
social: {
youtube: {
type: String,
},
twitter: {
type: String,
},
facebook: {
type: String,
},
linkedin: {
type: String,
},
instagram: {
type: String,
},
},
date: {
type: Date,
default: Date.now,
},
});
module.exports = Profile = mongoose.model('profile', ProfileSchema);
This is my view api. It doesn't work. it only return Cast to ObjectId failed for value { 'experience._id': '5edcb6933c0bb75b3c90a263' } at path _id for model profile
router.get('/experience/viewing/:viewexp_id', auth, async (req, res) => {
try {
const exp = await Profile.findById({
'experience._id': req.params.viewexp_id,
});
if (!exp) {
return res.status(404).json({ msg: 'Experience not found' });
}
res.json(exp);
} catch (err) {
console.error(err.message);
res.status(500).send(err.message);
}
});
How can I fix this? I tried looking at the stackoverflow of the same errors. still it doesn't seem to work.
and this is what I am trying to hit
The problem is that you have to convert your string _id to mongoose object id using this function mongoose.Types.ObjectId and my suggestion is to use findOne function instead of findById,
var mongoose = require('mongoose');
router.get('/experience/viewing/:viewexp_id', auth, async (req, res) => {
try {
let id = mongoose.Types.ObjectId(req.params.viewexp_id);
const exp = await Profile.findOne(
{ "experience._id": req.params.viewexp_id },
// This will show your sub record only and exclude parent _id
{ "experience.$": 1, "_id": 0 }
);
if (!exp) {
return res.status(404).json({ msg: 'Experience not found' });
}
res.json(exp);
} catch (err) {
console.error(err.message);
res.status(500).send(err.message);
}
});
var mongoose = require('mongoose');
router.get('/experience/viewing/:viewexp_id', auth, async (req, res) => {
try {
const exp = await Profile.findOne({
'experience._id': mongoose.Types.ObjectId(req.params.viewexp_id),
});
if (!exp) {
return res.status(404).json({ msg: 'Experience not found' });
}
res.json(exp);
} catch (err) {
console.error(err.message);
res.status(500).send(err.message);
}
});
You are saving object id . but your param id is string. convert it in ObjectId. Please check my solution.
router.post(
"/",
[
auth,
[
check("status", "status is required").not().isEmpty(),
check("skills", "skills is required").not().isEmpty(),
],
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const {
company,
website,
location,
bio,
status,
githubuername,
skills,
youtube,
facebook,
twitter,
instagram,
linkedin,
} = req.body;
const profileFileds = {};
profileFileds.user = req.user.id;
if (company) profileFileds.company = company;
if (website) profileFileds.website = website;
if (location) profileFileds.location = location;
if (bio) profileFileds.bio = bio;
if (status) profileFileds.status = status;
if (githubuername) profileFileds.githubuername = githubuername;
if (skills) {
profileFileds.skills = skills.split(",").map((skill) => skill.trim());
}
//Build profile object
profileFileds.social = {};
if (youtube) profileFileds.social.youtube = youtube;
if (twitter) profileFileds.social.twitter = twitter;
if (facebook) profileFileds.social.facebook = facebook;
if (linkedin) profileFileds.social.linkedin = linkedin;
if (instagram) profileFileds.social.instagram = instagram;
try {
let profile = await Profile.findOne({ user: req.user.id });
if (profile) {
//update
profile = await Profile.findOneAndUpdate(
{ user: req.user.id },
{ $set: profileFileds },
{ new: true }
);
return res.json(profile);
}
//Create profile
profile = new Profile(profileFileds);
await profile.save();
res.json(profile);
} catch (err) {
console.error(err.message);
res.status(500).send("server Error");
}
}
);

sequelize includes not returning data

I am trying to get data mapped with empid from 2 tables viz-skillsrepo and certifications and render it to frontend,I am getting all data from certifications table,but i need data only of the empid which i send in request
tried using includes method
app.get('/updateprofile/:id', function (req, res) {
db.skillsrepo.find({
where: { employeeId: req.params.id },
include: [
{
model: db.certifications
},
{
model: db.attachments
},
{
model: db.project
}
]
}).then(result => {
if (result != null) {
res.render('updateprofile', {
user: result,
eid: req.params.id,
});
console.log("**********", result)
}
})
});
This is the Schema:
var skillsrepo = exports.skillsrepo = connection.define('skillsrepo', {
firstname: {
type: Sequelize.STRING(23)
},
lastname: {
type: Sequelize.STRING(23)
},
highQual: {
type: Sequelize.STRING(23)
},
fivekeystrenghts: {
type: Sequelize.TEXT
},
domain: {
type: Sequelize.STRING(23)
},
technicalskills: {
type: Sequelize.STRING(23)
},
typesoftesting: {
type: Sequelize.STRING(23)
},
employeeId: {
type: Sequelize.INTEGER(11),
references: {
model: 'employeemastertablee',
key: 'id'
}
}
});
skillsrepo.hasMany(certifications, {
foreignKey: "employeeId"
});
certifications.belongsTo(skillsrepo, {
foreignKey: "employeeId"
});

How to implement many to many association using through in Sails?

I'm using Sails v0.11.2 and MongoDB 3.2 on Mac OS X El Capitan and I'm trying to implement Many-To-Many association using Through option which isn't supported yet.
However, googling I found this Waterline Github Issue and elennaro, a github user, gave me a couple of links with some examples:
First one
Second one
I have tried to adapt them to my own Sails app but I can't make it works. I got no errors on the console but the record or document on the intermediary table is not created only the Form document in it's table.
These are my models:
User.js
module.exports = {
schema: true,
tableName: 'users',
autoCreatedAt: false,
autoUpdatedAt: false,
attributes:
{
email : { type: 'email', required: true, unique: true },
encrypted_password : { type: 'string' },
reset_password_token: { type: 'string', defaultsTo: null},
permission_level : { type: 'integer', required: true, min: 1, max: 3, defaultsTo: 0 },
belongs_to : { type: 'string', required: true, defaultsTo: 0 },
signin_count : { type: 'integer', required: true, defaultsTo: 1 },
status_active : { type: 'boolean', required: true, defaultsTo: false },
last_signin_at : { type: 'datetime', defaultsTo: function (){ return new Date(); } },
last_signin_ip : { type: 'string', defaultsTo: '0.0.0.0' },
// Add a reference to Person
person_id:
{
model: 'person'
},
// Add a reference to Forms collection
forms:
{
collection: 'form',
via: 'user_id',
through: 'userhasform'
},
has:
{
collection: 'userhasform',
via: 'form_id'
}
}
};
Form.js
module.exports = {
schema: true,
tableName: 'forms',
attributes:
{
name : { type: 'string', required: true, unique: true },
creator : { type: 'string', unique: false },
sequence: { type: 'integer', autoIncrement: true },
// Add a reference to Questions collection
questions:
{
collection: 'question',
via: 'form_id'
},
// Add a reference to the owners Users
owners: {
collection: 'user',
via: 'form_id',
through: 'userhasform'
}
}
};
UserHasForm.js
module.exports = {
schema: true,
tableName: 'users_have_forms',
attributes:
{
to_edit : { type: 'boolean' },
to_delete : { type: 'boolean' },
user_id : { model: 'user' },
form_id : { model: 'form' }
}
};
The controller in which I create a form and it is supposed the intermediary document is been created at the join table is:
FormController.js
module.exports = {
create: function (req, res)
{
var ownerJson = {},
tmpFolio;
// Get the logged user to make the Folio and then create the form
SessionService.getUser(req, createForm);
// Callback function
function createForm (err, session)
{
// If there's no logged user or any error
if (err || !session)
{
console.log(err);
return res.json(err.status, {error: err});
}
console.log('User to create Folio: ', session.id);
ownerJson.owner_a = session.first_name;
ownerJson.owner_b = session.second_name;
ownerJson.owner_c = session.last_name;
// Construct the Folio creator part like AVC
tmpFolio = FolioService.generateFolio(ownerJson);
Form.create({
name: req.body.name,
creator: tmpFolio
})
.then(function (form){
if (err)
{
console.log(err);
return res.json(err.status, {error: err});
}
// Create the jointable record
var createdRecord = UserHasForm.create({
to_edit: true,
to_delete: true,
user_id: session.id,
form_id: form.id
})
.then(function (createdRecord){
if (err)
{
console.log(err);
return res.json(err.status, {error: err});
}
return createdRecord;
});
return [form, createdRecord];
})
.spread(function (form, createdRecord){
return res.json(200,
{
message: 'The form was created successfuly!',
data: form,
sharing: createdRecord
});
})
.fail(function (err){
if (err)
{
console.log(err);
res.json(err.status, {error: err});
}
});
}
},
};
When I run this code I got the next error:
[ReferenceError: UserHasForm is not defined]
Unhandled rejection TypeError: Cannot read property 'toString' of undefined
So I suppose it can't find the model so I add the next line to the model at the beginning:
var UserHasForm = require('../models/UserHasForm');
And now I get the next error:
[TypeError: UserHasForm.create is not a function]
All this is following the the first example on the list.
Any idea why I'm getting this error?
Any kind of help will be welcomed!
Well after trying to many examples finally I found the solution thanks to #elennaro for all his support. The whole conversation could be found in the link to the chat we both started under the main question's comments.
Also I can tell you that the examples in the links provided by him (which are in the question above) works fine, the problem was that the version I was using didn't support the features that those examples show.
Basically what I had to do is to install the most recent version for NodeJS, SailsJS and Waterline.
In my case I actually have the next ones:
Node v5.3.0
Sails v0.11.3
Waterline v0.10.30
After that I have to make some changes to my models and at the end they look like this:
User.js
module.exports = {
schema: true,
tableName: 'users',
autoCreatedAt: false,
autoUpdatedAt: false,
attributes:
{
// username : { type: 'string', unique: true, minLength: 5, maxLength: 15 },
email : { type: 'email', required: true, unique: true },
encrypted_password : { type: 'string' },
reset_password_token: { type: 'string', defaultsTo: null},
permission_level : { type: 'integer', required: true, min: 1, max: 3, defaultsTo: 0 },
belongs_to : { type: 'string', required: true, defaultsTo: 0 },
signin_count : { type: 'integer', required: true, defaultsTo: 1 },
status_active : { type: 'boolean', required: true, defaultsTo: false },
last_signin_at : { type: 'datetime', defaultsTo: function (){ return new Date(); } },
last_signin_ip : { type: 'string', defaultsTo: '0.0.0.0' },
// Add a reference to Forms collection
forms:
{
collection: 'form',
via: 'user',
through: 'userhasform'
// dominant: true
}
}
};
Form.js
module.exports = {
schema: true,
tableName: 'forms',
attributes:
{
name : { type: 'string', required: true, unique: true },
creator : { type: 'string', unique: false },
sequence: { type: 'integer', autoIncrement: true },
// Add a reference to the owners Users
owners: {
collection: 'user',
via: 'form',
through: 'userhasform'
}
}
};
UserHasForm.js
module.exports = {
schema: true,
tableName: 'users_have_forms',
attributes:
{
to_edit : { type: 'boolean' },
to_delete : { type: 'boolean' },
user : { model: 'User', foreignKey: true, columnName: 'user_id' },
form : { model: 'Form', foreignKey: true, columnName: 'form_id' }
}
};
FormController.js
Still the same as in the question
I hope it could be useful for anybody. And once again thanks to # Alexander Arutinyants for your support!
Any question, please leave a comment!

Resources