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.
Related
I am attempting to join two tables defined below in a one-to-many relationship. One Characteristic to many ReviewCharacteristics. Using the following findAll function I am attempting to avg the values in the ReviewCharacteristics table. I have verified the necessary SQL command that should function as expected. Sequelize is including ReviewCharateristic.id in the selected attributes which is causing my grouping to fail.
Is there a way to specify just the avg value and not include the id from the ReviewCharacteristic table in the selected attributes?
exports.Characteristic = db.define('characteristic', {
product_id: {
type: DataTypes.INTEGER,
allowNull: false
},
name: {
type: DataTypes.STRING(7),
allowNull: false
}
}, { underscored: true });
exports.ReviewCharacteristic = db.define('review_characteristic', {
value: {
type: DataTypes.INTEGER,
allowNull: false
}
}, { underscored: true });
exports.Characteristic.hasMany(exports.ReviewCharacteristic, { as: 'rc' });
exports.ReviewCharacteristic.belongsTo(exports.Characteristic);
Characteristic.findAll({
attributes: ['name', 'id'],
where: {
product_id: req.query.product_id
},
group: 'characteristic.id',
include: [{
model: ReviewCharacteristic,
as: 'rc',
attributes: [[Sequelize.fn('AVG', Sequelize.col('rc.value')), 'value']],
required: true
}]
});
I have also tried specifying attributes: ['characteristic.name', 'characteristic.id']
Current:
SELECT
"characteristic"."name",
"characteristic"."id",
"rc"."id" AS "rc.id",
AVG("rc"."value") AS "rc.value"
FROM "characteristic" AS "characteristic"
INNER JOIN "review_characteristic" AS "rc"
ON "characteristic"."id" = "rc"."characteristic_id"
WHERE "characteristic"."product_id" = '18078'
GROUP BY "characteristic"."id";
Expected:
SELECT
"characteristic"."name",
"characteristic"."id",
AVG("rc"."value") AS "rc.value"
FROM "characteristic" AS "characteristic"
INNER JOIN "review_characteristic" AS "rc"
ON "characteristic"."id" = "rc"."characteristic_id"
WHERE "characteristic"."product_id" = '18078'
GROUP BY "characteristic"."id";
If anyone happens to stumble across this question and is dealing with the same issue here is the solution I found.
Characteristic.findAll({
attributes: ['name', 'id', [Sequelize.fn('AVG', Sequelize.col('rc.value')), 'value']],
where: {
product_id: req.query.product_id
},
group: 'characteristic.id',
include: [{
model: ReviewCharacteristic,
as: 'rc',
attributes: [],
required: true
}]
});
The attributes must be specified as an empty array for the joined table in order to stop the addition of the ReviewCharateristic.id attribute in my case.
In my model I have a many-to-many-through association between User, Program through ProgramStaff:
User.js:
module.exports = {
attributes: {
username: {
type: 'string',
required: true,
unique: true
},
programs: {
collection: 'Program',
via: 'program',
through: 'programstaff'
}
}
}
Program.js:
module.exports = {
attributes: {
name: {
type: 'string'
},
personnel:{
collection : 'User',
via: 'user',
through: 'programstaff'
}
}
}
ProgramStaff.js:
module.exports = {
attributes: {
program: {
model: 'Program'
},
user: {
model: 'User'
},
permissions: {
type: 'integer'
}
},
tableName: 'program_staff'
};
(I need to program-staff through table to hold some sort of permissions and a user-program based, otherwise, I'd just use a regular many-to-many association).
My question is - can I create a new User' and associate with (existing)Program` using the rest or shortcuts routes?
I've tried to send
user.programs = [programId]; // doesn't do anything (or error)
//or
user.programs = [{program: progamId}]; // creates new program even though I send valid id of existing program
//or
user.programs = [{program: { id: progamId}}]; // error
But neither seem to create the ProgramStaff record.
P.S. I know I can do it with a User.create and a nested ProgramStaff.create calls within a route, or a Create and then Update rest/shortcut calls but I was wondering about a "automatic" way to do that.
I must be misunderstanding this bullet point from the save documentation:
If you have any associations on the model they will currently be populated when you call .save(). This could cause issues with memory so to prevent this, you can take advantage of an experimental feature: passing in an options argument with populate: false set. Example: .save({ populate: false }, function() {})
http://sailsjs.com/documentation/reference/waterline-orm/records/save
My code...
//Person.js
attributes: {
name: {
type: 'string'
},
pets: {
collection: 'pet',
via: 'owner'
},
}
//Pet.js
attributes: {
animal: {
type: 'string'
},
owner: {
model: 'person'
}
}
//PersonController.js
create: function(req, res) {
Person.create({
name: 'Bob'
}).populate('pets').exec(function(err, person) {
person.pets.add({animal: 'dog'})
person.save(function(err) {
console.log(person);
});
})
}
And my output is Bob with no pets.
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.
Faced serious problems here with Sails Js.
So, i have one-to-many relationship as described below (taken from official sails documentation):
// myApp/api/models/User.js
// A user may have many pets
module.exports = {
attributes: {
firstName: {
type: 'string',
columnName: "first_name"
},
lastName: {
type: 'string',
columnName: "last_name"
},
// Add a reference to Pets
pets: {
collection: 'pet',
via: 'owner'
}
}
};
and dependent model:
// myApp/api/models/Pet.js
// A pet may only belong to a single user
module.exports = {
attributes: {
breed: {
type: 'string'
//column name will be 'breed' by default
},
typeProperty: {
type: 'string',
columnName: "type_property"
},
nameProperty: {
type: 'string',
columnName: "name_property"
},
// Add a reference to User
owner: {
model: 'user'
}
}
};
When I'm calling in the code following query
User.find()
.populate('pets')
.exec(function(err, users) {
if(err) // handle error
// The users object would look something like the following
[{
id: 123,
firstName: 'Foo',
lastName: 'Bar',
pets: [{
id: 1,
// !! Only this property is loaded !!
breed: 'labrador',
// !! This property is NOT loaded !!
typeProperty: undefined,
// !! This property is NOT loaded !!
nameProperty: undefined,
user: 123
}]
}]
});
Basically, seems that sails (waterline if to be specific) is not mapping back properties, which have custom "columName" specified and differs from property name (ex. "typeProperty" stored in type_property column).
Has anyone faced this kind of problem?
In fact, I faced this problema. The property "columnName" is not working. Seems like Sails doesn't prioritizes this property over it's model's convention naming.
Try to change the model attribute name to be equal your database property.
type_property: {
type: 'string'
},
this should make your attributes to be populated. Worked here.
Column name works fine when the attribute in question is a foreign key.