populate additional information of the 'through' table in sails - sails.js

I have two models and there is many to many association in between them(I am using sails.js framework). I have added the addition field in the association table. I want to populate that addition field. How do I achieve this? My models are given below:
//Store.js file
module.exports = {
autoCreatedAt: false,
autoUpdatedAt: false,
attributes: {
name: "string",
slug: "string",
imageURL: "string",
termsAndConditions: "string",
link: "string",
productID: {
collection: 'product', //This is for association with the product model
via: 'storeID',
through: 'price'
}
}
};
Below is my Product.js file
//Product.js
module.exports = {
autoCreatedAt: false,
autoUpdatedAt: false,
attributes: {
name: 'string',
storeID: {
collection: 'stores',
via: 'productID', //This is for association with the Store model
through: 'price'
}
}
};
And below is my through model Price.js
module.exports = {
autoCreatedAt: false,
autoUpdatedAt: false,
attributes: {
storeID: {
model: 'stores'
},
productID: {
model: 'product'
},
price: 'integer' //I want to populate this additional field when calling api '/product' or '/store'
}
};
How to populate the additional field price of Price table from calling the api '/product' or '/store'?

Inside the callback function after populating (exec or then depending on your implementation) Find the record in the price table and perform update on that record to change the value of price from null to whatever value you want. Share your implementation code for more detailed answer.

Related

Sequelize Unique Constraint Across Two Tables

I have three tables: survey, survey_owners (join table), users. Surveys naturally have titles and are owned by users. A user can own multiple surveys and a survey can be owned by multiple users (many-to-many relationship).
I have the unique constraint setup on the survey_owners table so there are no duplicates, but now need to figure out how to enforce a unique constraint to address the following: A user should not be able to own multiple surveys with the same title.
That being said, a unique constraint CANNOT be placed on the 'title' column of the survey table because the uniqueness should be only be applied if a user already owns a survey with an identical name.
Any ideas how to implement this in the Sequelize migration and/or model(s)?
Current migration file for survey_owners
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('survey_owners', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
surveys_id: {
type: Sequelize.INTEGER,
onDelete: 'CASCADE',
references: {
model: 'surveys',
key: 'id'
}
},
users_id: {
type: Sequelize.INTEGER,
references: {
model: 'users',
key: 'id'
}
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
field: "created_at"
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
field: "updated_at"
}
})
.then(() => {
return queryInterface.addConstraint('survey_owners', ['surveys_id', 'users_id'], {
type: 'unique',
name: 'survey_owners'
});
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('survey_owners');
}
};
Unfortunately I could not find a way inside Sequelize to handle this constraint so I am handling the logic on the submit action and checking in a JS method. Not the best way but had to move on and this is working.

addToCollection and set intermediate table column values as well

I am using Sails v1.1 -
Following the example from the "Through" associations on sails - https://sailsjs.com/documentation/concepts/models-and-orm/associations/through-associations
They defined a "through" association as basically a custom model. So this really isn't "through", it's just controlling the join table for the many to many relation.
So in the intermediate model, I added a custom attribute of isTyping seen below.
Is it possible to add to collection and set this intermediate value at same time?
For exmaple pseudocode with setIntermediate:
User.addToCollection(userId, 'pets', petId).setIntermediate('isTyping', true);
So following the example on the docs:
myApp/api/models/User.js
module.exports = {
attributes: {
name: {
type: 'string'
},
pets:{
collection: 'pet',
via: 'owner',
through: 'petuser'
}
}
}
myApp/api/models/Pet.js
module.exports = {
attributes: {
name: {
type: 'string'
},
color: {
type: 'string'
},
owners:{
collection: 'user',
via: 'pet',
through: 'petuser'
}
}
}
myApp/api/models/PetUser.js
module.exports = {
attributes: {
owner: {
model:'user'
},
pet: {
model: 'pet'
},
// I ADDED THIS INTERMEDIATE COLUMN NAME in the join table
isTyping: {
type: 'boolean',
defaultsTo: false
}
}
}
I don't know if this is right, but the way to do this is instead of using Pet.addToCollection(petId, 'owners', userId)/User.addToCollection(userId, 'pets', petId) or Pet.removeFromCollection(petId, 'owners', userId)/User.removeFromCollection(userId, 'pets', petId), is to instead do:
PetUser.create({ owner: userId, pet: petId, isTyping: true }).populate('user').populate('pet')
I'm not sure if right, and this doesn't support the array argument that addToCollection/removeFromCollection does. And you also have to massage the data in order to get a list of owners/pets with the pivot attribute of isTyping.

How do I set the foreignKey column when bulk creating instances of a model with Sequelize?

I have a one to many relationship between my Polls model and Options model, where one Poll can have multiple options.
The association is set so that Options has a pollId column, which needs to have the correct id from the poll model inserted.
var Options = sequelize.define('Options', {
name: {
type: DataTypes.STRING,
allowNull: false
},
votes: {
type: DataTypes.INTEGER,
allowNull: false
}
}, {
classMethods: {
associate: function(models) {
// associations can be defined here
Options.belongsTo(models.Polls, {
foreignKey: 'pollId',
onDelete: 'CASCADE'
});
}
}
});
I am using bulk create to create multiple options at once, like so
models.Users.findOne({
where: {uuid: user_id}
}).then((user) => {
models.Polls.create({
createdBy: user.get('name'),
userId: user_id,
voter_ids: []
}).then((poll) => {
models.Options.bulkCreate({
})
})
});
Not sure how to add the pollId option to each entry to reflect the same poll model instance, in a way that makes sense.
If you want to use bulkCreate() you have to manually add the pollId to the options. Which you could easily do: {pollId: poll.id}
But theres an even better option:
You can issue a single create() including the poll and the options. Simply add the options to the poll and specify that you want to create these too using the include option of create() See: http://docs.sequelizejs.com/manual/tutorial/associations.html#creating-elements-of-a-hasmany-or-belongstomany-association
models.Polls.create({
createdBy: user.get('name'),
userId: user_id,
voter_ids: [],
options: [
{your first option},
{another option}
]
}, {
include: [ models.Options ]
}
)

How to filter a query based on collection from many-to-many

I have two model objects. Doctors and Hospitals. The model definitions look like:
module.exports = {
schema: true,
autoUpdatedAt: true,
autoCreatedAt: true,
attributes: {
name: {
type: 'string',
required: true,
unique: true
},
hospitals: {
collection: 'hospital',
via: 'doctors',
dominant: true,
},
}
};
and
module.exports = {
schema: true,
autoUpdatedAt: true,
autoCreatedAt: true,
attributes: {
name: {
type: 'string',
required: true,
unique: true
},
doctors: {
collection: 'doctor',
via: 'hospitals',
},
}
};
How can I query doctors that are mapped to certain hospitals? I read a couple posts about through keyword, but I wasn't able to get records to persist to the through/join table. Seems like if I could query the automatic join table, I could get it to work, but I'm curious if there is an "official" way to accomplish this type of query.
My current query looks like: Doctor.find().where({'hospitals': ['548303dcf49435ec4a01f2a2','548303cbf49435ec4a01f2a0']}).populate('hospitals').exec(function (err, doctors) { ... });
The underlying db is mongo, if that matters.
I did cheat a bit but things seem to be working. That said, I am interested if there's a better way to accomplish this type of query.
I created a model object that maps to the auto created join table. So in this case, my additional model object looks like:
module.exports = {
schema: true,
autoUpdatedAt: true,
autoCreatedAt: true,
tableName: 'doctor_hospitals__hospital_doctors',
attributes: {
doctor: {
model: 'doctor',
columnName: 'doctor_hospitals'
},
hospital: {
model: 'hospital',
columnName: 'hospital_doctors'
}
}
};
Now, I query the join table directly and use the results for a sub query:
DoctorHospital.find().where({'hospital': ['548303dcf49435ec4a01f2a2','548303cbf49435ec4a01f2a0']}).exec(function(err, doctorHospitals) {
if(err) return next(err);
Doctor.find().where({'id': _.pluck(doctorHospitals, 'doctor')}).populate('hospitals').exec(function (err, doctors){
if(err) return next(err);
return res.view({
doctors: doctors
});
});
});

sails.js join tables on mongodb native id

I have two collections in mongodb database and model for each of them
App Model
module.exports = {
tableName: 'app',
attributes: {
_id : {
primaryKey: true,
unique: true,
type: 'string',
},
userId: {
model: 'user'
},
title: {
type: 'string',
required: true,
unique: true,
},
createdDate : 'string'
},
};
and User Model
module.exports = {
tableName: 'user',
attributes: {
id : {
primaryKey: true,
unique: true,
type: 'string',
collection: "app",
via : "userId"
},
password: {
type: 'string',
required: true
},
apps : {
collection: "app",
via : "userId"
}
},
};
When i use numeric values for join this collection, it works fine, but when i try do it with mongodb native id object, i get the empty result
How i call join query
User.find().populate('apps').exec(function(err, result) {});
You need to get rid of both the _id and id attribute definitions in your models. Waterline will handle the primary key fields for you automatically (normalizing them to id), so unless you need to change the field type, they can be safely left out. Also, I'm not sure what your intention was by adding collection and via to the id definition, but the primary key is never going to be an association.
Otherwise, your models look correct. If you get rid of those two attributes, things should work fine.