Sequelize.js - Persist model with association - node.js

I'm trying to save an entity with the following models and save method but it only creates the parent entity. See:
var Project = database.define('projects', {
name : {
type : Sequelize.STRING,
allowNull : false,
unique : true
},
archived : {
type : Sequelize.BOOLEAN,
allowNull : false,
defaultValue : false
}
});
var Sprint = database.define('sprints', {
name : {
type : Sequelize.STRING,
allowNull : false,
unique : true
},
start : {
type : Sequelize.DATE
},
end : {
type : Sequelize.DATE
}
});
Project.hasMany(Sprint, {
as : 'sprints'
});
Sprint.belongsTo(Project);
The save method in Express:
var project = req.body;
models.Project.create(project).then(function(project){
return res.json(project);
}, function(err) {
return next(err);
});
The req.body is something like:
{
archived : false,
name : "Projeto 1",
sprints : [{
end : "2016-02-25T03:00:00.000Z",
name : "Sprint 1",
start : "2016-02-19T02:00:00.000Z"
}
]
}
The result is one record of Project created in database. Nothing more.
Sprint entry is also new, I was expecting it could be created together with Project.
Any ideas?
Thanks

Ok, now it saves Project and it's Sprints:
var project = req.body;
models.Project.create(project, {
include : [{
model : models.Sprint,
as : 'sprints'
}
]
}).then(function (project) {
return res.json(project);
}, function (err) {
return next(err);
});

Related

Unable to update mongodb document with nested object property from node api

I have the following mongodb document:
{
"_id" : ObjectId("5ee95b41ca023a3deb252ef2"),
"uid" : "jdoe",
"name" : "John Doe",
"employee_hire_date" : "2012-06-20",
"three_month_review_target" : "2012-09-20",
"six_month_review_target" : "2012-12-20",
"three_month_review_status" : {
"is_scheduled" : null,
"is_team_member_emailed" : null,
"is_review_executed" : null,
},
"six_month_review_status" : {
"is_scheduled" : null,
"is_team_member_emailed" : null,
"is_review_executed" : null,
}
}
I would like to update the three_month_review_status.is_scheduled nested property to true. I am using a put request to accomplish this:
const mongoose = require('mongoose');
const Reviews = require('../modules/reviews/models/reviews');
module.exports = (app) => {
app.put('/api/reviews/isScheduled', async (req, res) => {
console.log('body', req.body)
console.log('uid', req.body.uid)
console.log('is_scheduled', req.body.three_month_review_status.is_scheduled)
Reviews.findOneAndUpdate( { 'uid': req.body.uid }, { $set: { 'three_month_review_status.is_scheduled': req.body.is_scheduled }}, (err, result) => {
if (err) {
console.log('error', err)
}
else {
console.log('updated', result);
res.status(200).send(result);
}
} )
});
}
To test this, I execute this PUT request through Postman with the following request body:
{
"uid": "jdoe",
"three_month_review_status": {
"is_scheduled": "true"
}
}
However, when the request gets executed, the other two nested objects are removed and is_scheduled remains null. This is the document after the request is executed:
{
"_id" : ObjectId("5ee95b41ca023a3deb252ef2"),
"uid" : "jdoe",
"name" : "John Doe",
"employee_hire_date" : "2012-06-20",
"three_month_review_target" : "2012-09-20",
"six_month_review_target" : "2012-12-20",
"three_month_review_status" : {
"is_scheduled" : null
},
"six_month_review_status" : {
"is_scheduled" : null,
"is_team_member_emailed" : null,
"is_review_executed" : null,
}
}
What am I doing wrong? Here is my reviewsSchema for more context:
const { Schema, model } = require('mongoose');
const reviewsSchema = new Schema({
uid: String,
name: String,
employee_hire_date: String,
three_month_review_target: String,
six_month_review_target: String,
three_month_review_status: {
is_scheduled: Boolean,
is_team_member_emailed: Boolean,
is_review_executed: Boolean,
},
six_month_review_status: {
is_scheduled: Boolean,
is_team_member_emailed: Boolean,
is_review_executed: Boolean,
},
})
const Reviews = model('review', reviewsSchema);
module.exports = Reviews;
In Mongoose you don't need to specify the $set. Also based on the sample JSON that you send from the postman instead of req.body.is_scheduled you need to provide req.body.three_month_review_status.is_scheduled in the query. Also, need to add {new: true} if you want the findOneAndUpdate to return the updated document
So you can update the query like
Reviews.findOneAndUpdate(
{ uid: req.body.uid },
{
"three_month_review_status.is_scheduled":
req.body.three_month_review_status.is_scheduled,
},
{ new: true },
(err, result) => {
if (err) {
console.log("error", err);
} else {
console.log("updated", result);
res.status(200).send(result);
}
}
);

How to get all the documents before a particular document in mongodb?

I am implementing a chat module. I have messages in each conversation and when a particular user opens the conversation, I want to edit all the messages sent by others as read.
The message document looks like this:
{
"_id" : ObjectId("593a2a8fd957d847ba45eb6b"),
"senderId" : ObjectId("590c53a85fba594a59fe3d0f"),
"clientMessageId" : "02598F68-39BD-407A-ADDF-717148CB071C",
"conversationId" : ObjectId("592e7fab801be524e0df2591"),
"messageType" : "text",
"message" : "Yo",
"created" : NumberInt(1496984208),
"isRead" : false,
"isDelivered" : false,
"isDeleted" : false,
"isEdited" : false
}
The mongo code is as below:
let filter = {
conversationId: ObjectId(conversationId), //To get the conversation
senderId: {
$ne: ObjectId(userId) //All the messages sent by others
},
isRead: false, //unread
_id: {
$lte: ObjectId(lastMessageId) //All the messages before the given messageid
}
};
let update = {
$set: {
isRead: true,
readTime: createdTime
}
};
connection.collection("messagesv2").updateMany(filter, update, (err, result) => {
if (err) {
console.log(err);
}
console.log(result)
This is not working. Can anyone point out why or how to go about implementing the same ?

Updating multiple sub-documents with Mongoose and Node

I have a Model wich contains an array of sub-documents. This is a Company:
{
"_id" : ObjectId(":58be7c236dcb5f2feff91ac0"),
"name" : "sky srl",
"contacts" : [
{
"_id" : ObjectId("58be7c236dcb5f2feff91ac2"),
"name": { type: String, required: true },
"company" : ObjectId("58be7c236dcb5f2feff91ac0"),
"email" : "sky#gmail.com",
"chatId" : "",
"phone" : "123456789",
"name" : "John Smith"
},
{
"_id" : ObjectId("58be7f3a6dcb5f2feff91ad3"),
"company" : ObjectId("58be7f3a6dcb5f2feff91ad1"),
"email" : "beta#gmail.com",
"chatId" : "",
"phone" : "987654321",
"name" : "Bill Gaset"
}
],
"__v" : 1
}
I have several companies, and I want to update the field chatId of all the contacts in all the companies, that matches the phone I am searching for.
My Schema definitions (simplified, for focusing on question):
var contactSchema = new Schema({
[...]
phone: { type: String, required: true },
email: { type: String },
chatId: { type: String },
company: Schema.Types.ObjectId,
});
var companySchema = new Schema({
name: { type: String, required: true },
type: { type: String, default: "company" },
contacts: [contactSchema]
});
I tried
var conditions = { "contacts.phone": req.body.phone };
var partialUpdate = req.body; //it contains 'req.body.phone' and 'req.body.chatId' attributes
Company.find(conditions).then(
function (results) {
results.map( function(companyFound) {
companyFound.contacts.forEach(function (contactContainer){
if (contactContainer.phone == partialUpdate.phone) {
contactContainer.chatId = partialUpdate.chatId;
Company.save();
companyFound.save();
contactContainer.save();
results.save();
}
//not sure of what to save, so i save everything
companyFound.save();
contactContainer.save();
results.save();
});
});
});
following this answer; but it doesn't works. It does not save anything, what I'm doing wrong?
I have never done this before, but worth a try.
Maybe you need to use $elemMatch.
// find the companies that have contacts having the phone number
Company.find().where('contacts', { $elemMatch: { phone: req.body.phone }}).exec(function (err, companies) {
if (err) {
console.log(err);
return;
}
// see if you can at least get the query to work
console.log(companies);
async.eachSeries(companies, function updateCompany(company, done) {
// find and update the contacts having the phone number
company.contacts.forEach(function (contact, i, arr) {
if (contact.phone == req.body.phone) {
arr[i].chatId = req.body.chatId;
}
});
company.save(done);
}, function allDone (err) {
console.log(err);
});
});
Note, I am using async.js to do async operations on multiple items.
Honestly, I would have simply made contacts an array of Contact references -- much easier to query and update.
Just for the records: I did this to make it work without async.js:
Company.find().where('contacts', { $elemMatch: { phone: req.body.phone } })
.exec(function (err, companies) {
if (err) {
console.log(err);
return;
}
console.log("companies: " + JSON.stringify(companies, null, 4));
companies.forEach(function (company) {
company.contacts.map(function (contact, i, arr) {
if (contact.phone == req.body.phone) {
arr[i].telegramChatId = req.body.telegramChatId;
}
});
company.save();
},
function allDone(err) {
console.log(err);
});
});`

How to update a JSON field that wasn't created in Sails.js

I have a Model : Place, where when I create the record in the Mongodb database I set some field such as :
place_name, description, capacity and in the JSON place_address all the attributes instead of latitude and longitude.
In the PlaceController.js, I have an update method where I want to update in the JSON place_address the latitude and longitude field which weren't set.
The only way to do that is to create a new JSON place_address where I get all the previous parameters and I set the latitude and longitude.
Is there an easier way to do that like : (but the following code doesn't work)
In PlaceController.js
Place.update(id, {
place_address.latitude : req.param('latitude'),
place_address.longitude : req.param('longitude')
})
For a better understanding here is the Model Place.js :
module.exports = {
attributes: {
place_name : {
type : 'string',
required : true,
},
description : {
type : 'string',
required : true,
},
capacity : {
type : 'string',
required : true,
},
place_address : {
type : 'json',
required : true,
},
office_hours: {
type : 'array'
},
conditions: {
type : 'json'
},
functions: {
type : 'array'
},
owners: {
type : 'array'
}
}
};
And here is the Controller PlaceController.js :
update: function (req,res) {
var id = req.params.id;
var placeJSON = {street_number: req.param('street_number'), street_name: req.param('street_name'), additional_info: req.param('additional_info'), postcode: req.param('postcode'), town: req.param('town'), country: req.param('country')};
if (!id) {
console.log("No id specified.");
return res.send("No id specified.",500);
}
Place.update(id, {
place_name : req.param('place_name'),
description : req.param('description'),
capacity : req.param('capacity'),
place_address : placeJSON
}).exec(function (err, updatedPlace) {
if (err) {
res.redirect('/place/edit');
}
if(!updatedPlace) {
res.redirect('/place/edit');
}
//res.redirect('/place');
res.redirect('/place/show/'+id);
});
}
populate sails function helps you express the association between collections, in your cases the Place and Coordinate can be made like this:
// create new place and coordinates
Place.create(placeParams).then((place) => {
Coordinate.create(coordianteParams).then((coordinate) => {
Place.findOne(lesson.id).populate('coordinates').then((place) => {
res.json(place);
})
})
// get place and coordinates
Place.find({}}.popupale('coordinates').then((places) => {
res.json(places)
});
this approach enables to update separately the coordinates and re-attach them to the parent (place collection in this case)

sequelize : Create new entry with existing foreign key

I create 2 models :
userPreferenceCategories = sequelize.define("userPreferenceCategories", {
id : {
type : Sequelize.INTEGER,
autoIncrement : true,
primaryKey : true,
allowNull : false
},
name : {
type : Sequelize.STRING,
allowNull : false
}
}, {
indexes: [
{
unique : true,
fields : ["name"]
}
],
freezeTableName : true
});
and
userPreferenceCategoryTypes = sequelize.define("userPreferenceCategoryTypes", {
id : {
type : Sequelize.INTEGER,
autoIncrement : true,
primaryKey : true,
allowNull : false
},
name : {
type : Sequelize.STRING,
allowNull : false
}
}, {
indexes: [
{
unique : true,
fields : ["name", "userPreferenceCategoryId"]
}
],
freezeTableName : true
});
With relation :
userPreferenceCategoryTypes.belongsTo(userPreferenceCategories, {
onDelete : "cascade",
onUpdate : "cascade",
foreignKey : {
field : "userPreferenceCategoryId",
allowNull : false,
}
});
one time, I create an userPreferenceCategories entry :
userPreferenceCategories.upsert({name : "cat1"});
in second time, I would like create an userPreferenceCategoryTypes associate at "cat1" like this :
userPreferenceCategoryTypes.upsert({
name : "type1",
userPreferenceCategory : {
name : "cat1"
}
}, {
include: [ {
model : userPreferenceCategories
} ]
});
but this don't work with message :
Unhandled rejection SequelizeValidationError: notNull Violation: userPreferenceCategoryId cannot be null
In fact, sequelize does not seem to add userPreferenceCategoyId attribut on sql request. How can I insert new entry in userPreferenceCategoryTypes this foreignKey of cat ? I don't execute that on series.
EDIT 1 :
I found a solution but I'm not sure if there had better : /
userPreferenceCategory.find({where: {name: "cat1"}}).then(function (cat) {
UserPreferenceCategoryType.create({
name : "type1",
userPreferenceCategoryId : cat.getDataValue("id")
});
});
It seems it's the only solution
No, because when you do a create with include, it will create the
associated instance, not associate it.

Resources