How group by relationship attributes sailsjs - sails.js

We have two models, User and Group.
User:
module.exports = {
attributes: {
[...]
memberOfGroups: {
collection: 'group',
via: 'members'
}
}
};
Group:
module.exports = {
attributes: {
[...]
money: {type:number},
members: {
collection: 'user',
via: 'memberOfGroups',
dominant: true
}
}
};
How to group by user "memberOfGroups" ?
I wanna sum money user in each group
Example:
Group 1 : 10$,
Group 2 : 30$
How to do that with that models?

User.find().populate('memberOfGroups', {groupBy: ['money'], sum: ['money']})
Or any variant of that should work.
Please give me entire models and response JSON you want if you are not clear with my answer above.

Related

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.

Creating new Instance with a Through association Sails.js/waterline

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.

How to auto populate nested associations of sails blueprint for url /model/<id>/association

I have two models Users and Chat with many to many association.
User.js
module.exports = {
attributes: {
name: {
type : 'string'
,required : true
},
email:{
type : 'string'
,email : true
,required: true
,unique : true
},
enpassword : {
type: 'string'
},
online : {
type: 'boolean',
defaultsTo: false
},
socketid: {
type: 'string'
},
chats : {
collection: 'chat',
via: 'users',
dominant: true
}
};
Chat.js
module.exports = {
attributes: {
messages : {
collection: 'message',
via : 'chat',
dominant : true
},
users : {
collection: 'user',
via : 'chats'
},
}
};
When I call sails blueprint /user/1/chats I am getting list of chats but users association of each chat is not populated.
How can I achieve this from Sails queries ?
Great question. Here is a really easy way to do this.
First, require the following module.
var nestedPop = require('nested-pop');
Next, run your query.
getPopulatedUsers = function(req, res) {
User.find()
.populate('chats')
.then(function(users) {
return nestedPop(users, {
chats: [
'messages',
'users' // I actually wouldn't recommend populating this since you already have the users
]
}).then(function(users) {
console.log(users);
res.json(users);
});
});
}
More docs on this can be found at the following link.
https://www.npmjs.com/package/nested-pop

Sails.js Waterline: User, Role, Task models, Please give me some suggestions on my final model design

This question might be related to database schema design in sails.js waterline.
One task need to record different roles of users. For example, a Task need to know who are fooUsers and barUsers in this task. What is the best way to handle this? (foo and bar are different roles for a user)
More Context
A User will have different Roles.
The roles of the user might change in the future.
A User will only be one Role in one Task.
The Role of a User in a specific Task will never change.
A Task will have many users involved.
All tasks are the SAME type.
The total number of Roles will NOT change.
For example:
One task has four users: UserA with Role1, UserB with Role2, UserC with Role3, UserD with Role1.
In the future, UserA might have Role2 and Role3. But that doesn't affect what role of the UserA is in the above task.
My Final Design
// User.js
attributes: {
tasks: {
collection: 'task',
via: 'users'
},
roles: {
collection: 'role',
via: 'users'
},
userTaskRoles: {
collection: 'userTaskRole',
via: 'user'
}
}
// Task.js
attributes: {
users: {
collection: "user",
via: "tasks"
},
userTaskRoles: {
collection: 'userTaskRole',
via: 'task'
}
}
// Role.js
attributes: {
users: {
collection: 'user',
via: 'roles'
},
userTaskRoles: {
collection: 'userTaskRole',
via: 'role'
}
}
// UserTaskRole.js
attributes: {
task: {
model: 'task'
},
user: {
model: 'user'
},
role: {
model: 'role'
}
}
I updated this question a lot. The above is my final design. Please give me some feedback whether or not this is good or bad and are there any better design patterns?
Thanks.
You need to provide more context for your use case. Without knowing why you want to do what you want to do, I can provide this answer.
Differentiate foo / bar with a column in your tasks and reference users in a single column like so:
// User.js
attributes:{
tasks: {
collection: "task",
via: 'users'
}
}
// Task.js
attributes: {
Users: {
collection: "user",
via: "tasks"
},
type: {
type: 'string',
enum: ['foo','bar']
}
}

Sails Blueprint add to Many-to-Many record

I'm using Sails#0.10rc-7 and I'm struggling to add something to a many-to-many record.
I have multiple users and multiple groups like this:
// Group.js
module.exports = {
attributes: {
description: {
type: 'string'
},
users: {
collection: 'user',
via: 'groups'
}
}
};
//User.js
module.exports = {
attributes: {
token: {
type: 'string'
},
groups: {
collection: 'group',
via: 'users'
}
}
};
I have an existing group record and an existing user record. I want to connect the user to the group.
I can succesfully do a PUT request like this:
PUT http://127.0.0.1:1337/api/user/1
{
"groups":1
}
This also works:
PUT http://127.0.0.1:1337/api/user/1
{
"groups": [1,2]
}
But what if I don't know the existing groups?