sequelize - Where clause with associations - javascript

In sequelize I have boards and users setup, with a many to many association as follows
User.hasMany(Board, {through: BoardUsers});
Board.hasMany(User, {through:BoardUsers});
Is there anyway that, using a where clause I can find users that belong to one of a list of boards. For example, lets says I have 3 boards and I would like to find the first 20 users (using the default sort order) who belong to either board 1 or 3. Is there a way to do this without executing separate finds for each board, then manually combining the results.
I would love to be able to do something like:
User.findAll({where:{board: [1,3]}});
but I can't seem to find a way to do this.
As far as I can tell the equivalent SQL would be something like:
SELECT * FROM `users` WHERE id IN (SELECT userID FROM boardusers WHERE boardId IN (1,3))
But I would love to be able to do it through the ORM.

While I'm not sure if you can do this directly, you can always query for Boards and eagerly fetch users.
Something along these lines:
Board.findAll({where: {id: [1,3]}}, { include: [ User ] })

Quite a late response but just had this same question, and here's how I got it to work in Sequelize 3.24.0 (something like this);
User.findAll({ include: [{ model: Board, where: { $in: [1,3] }}] });

Related

Sequelize run hook once after include

I'm new to Sequelize and try to achieve the following:
Assume I have a very simple database with 3 Models/Tables:
Person, Group and Category.
Person has a Many-To-One relation to Group (1 Person can be in 1 Group, 1 Group holds multiple people) & Group has a Many-To-One relation to Category (1 Group has 1 Category, 1 Category can be applied to multiple Groups).
Because I don't want to save the whole Category in my database, but only a short string, I have a mapper in the backend in my app.
Let's say my Category-Mapper looks like this:
//category.mapper.js
module.exports = Object.freeze({
cat1: "Here is the String that should be sent to and displayed by the FrontEnd",
cat2: ".....",
});
So basically, in my database I save "cat1" as the category and every time I get one or more Categories via Sequelize from the database, I want to go into my mapper, resolve the short string to the long string and send it to the Frontend, so I wrote the following code:
//category.model.js
const categoryMapper = require("../mapper/category.mapper");
Category.afterFind((models) => {
if(!Array.isArray(models)) {
models = [models];
}
models.forEach(model => {
model.name = categoryMapper[model.name];
});
});
This works great when I call Category.findAll()..., but does not trigger when I include the Category as in this example:
Group.findAll({
include: [Category]
})
There is this rather old GitHub Issue referencing this behavior, where someone published some code to make sure the hooks run on include. See here.
I tried implementing the referenced code into my project, but when I do, the hook for Category runs twice in my following code:
Person.findAll({
include: [{
model: Group,
include: [Category]
}]
})
My assumption is, that, with the code from the GitHub-Issue comment, my hook gets triggered every time the relationship is detected and the code runs. Therefore the hook runs once after including Group, because Group has a relationship to Category and a second time when Category is actually included, which breaks my mapping function because the second time it tries to resolve the long string, which doesn't work.
I'm looking for a solution that basically runs my hooks once and only once, namely when the actual include for my model triggers, regardless of on what level the include happens.
Sorry for the lengthy post, but I did not find any solution to my problem online, but don't believe what I am trying to achieve is very exotic or specific to my project only.
If there is a better solution I am not seeing, I'm open to suggestions and new approaches.
Thanx in advance!

How do I retrieve all records that are NOT in a many to many association using Sequelize

Sequelize gives you the ability to define a many to many association between tables which adds some extra functionality to a Model instance.
I have a Users table and I have defined a self-association on the table like so:
User.belongsToMany(models.User, { through: 'Friends', as: 'friends', foreignKey: 'userId' });
This gives the instance of the User model a couple of extra methods like user.getFriends(). So far so good.
What I want to do is to get all users who aren't friends of our instance. Something like user.getNonFriends(). Would that be possible using Sequelize?
A quick solution I can think of is, you could get the list of friends of the user A from the database. Using that result you can get the friends list that is not in the user's A list. Here is an example in code
const friends = user.getFriends();
const friendIds friends.map(friend => friend.id)
Friend.findAll({ where: {
id: { $notIn: [...friendIds] }
}
})

Node.js/Bookshelf - Mapping model where multiple columns in a table reference the ID of another table

I am using node.js with bookshelf as an ORM. I am a serious novice with this technology.
I have a situation where I have several columns in a database table. For the sake of this question, these columns shall be named 'sold_by_id', 'signed_off_by_id' and 'lead_developer_id', and are all columns that will reference a User table with an ID.
In other words, different User's in the system would at any point be associated with three different roles, not necessarily uniquely.
Going forward, I would need to be able to retrieve information in such ways as:
let soldByLastName = JobTicket.soldBy.get('last_name');
I've tried searching around and reading the documentation but I'm still very uncertain about how to achieve this. Obviously the below doesn't work and I'm aware that the second parameter is meant for the target table, but it illustrates the concept of what I'm trying to achieve.
// JobTicket.js
soldBy: function() {
return this.belongsTo(User, 'sold_by_id');
},
signedOffBy: function() {
return this.belongsTo(User, 'signed_off_by_id');
},
leadDeveloper: function() {
return this.belongsTo(User, 'lead_developer_id');
}
Obviously I would need a corresponding set of methods in User.js
I'm not sure where to start, can anyone point me in the right direction??
Or am I just a total idiot? ^_^
Your definitions look right. For using them it will be something like:
new JobTicket({ id: 33 })
.fetch({ withRelated: [ 'soldBy', 'signedOffBy' ] })
.then(jobTicket => {
console.log(jobTicket.related('soldBy').get('last_name');
});
Besides that I would recommend you to use the Registry plugin for referencing other models. That eases the pains of referencing models not yet loaded.

Bookshelf.js / Knex.js nested where in single query

How can I combine queries of related models in bookshelf.js?
As it stands, the ORM fires two separate queries to the database which I'd like to combine. Coming from sequelize it was possible like this:
Model.find({
where: {
someField: 'someValue'
},
include: [{
model: OtherModel,
as: 'otherModel',
where: {
someOtherField: 'someOtherValue' <--- part of the same query
}
}]
})
My current setup in bookshelf.js (relationships between Model and OtherModel are set):
Model
.where({ someField: 'someValue' })
.fetch({ withRelated: [{
otherModel: q => q.where({
someOtherField: 'someOtherValue'
})
}] });
This works, except the knex.js debugger shows two separate queries executed against the database. I expect bookshelf to be smart enough to build SQL that joins in a single query.
Is this something that can be avoided through configuration or any other means?
This took me a long time to wrap my head around, but the multiple query behavior is by design. Bookshelf favors multiple un-joined round trips over joins, but doesn't N+1. It queries once per table. By breaking the queries up, it's possible to run some in parallel if there are multiple joins to a single table due to the asynchronous nature of node. It's a tradeoff that doesn't make sense for deeply nested relationships, but may for data models with many 1st generation relationships. I've never checked whether there is any sort of transaction or row locking.
You can force the join using Knex (http://knexjs.org/#Builder-join) and mix it into your bookshelf code, but for all but the most time sensitive and optimized applications, you probably won't notice the overhead unless your latency to the DB is poor.
If you're doing really complex queries where the joins are required for performance, I might suggest using just knex or knex.raw instead of bookshelf.

Foreign key conditions in sailsjs Waterline ORM

I have defined two models like below, what i need to do is to retrieve the unique neighborhoods that belong to active partners (partner.status=1), i can retrieve the grouped neighborhoods like this
Locations.find({groupBy: [ 'neighborhood' ],sum['id']}, function(err,locations){});
and then match against a retrieved list of active Partners or backwards (ie first getting the active partners with the locations for each partner and pushing them to an array after verifying they are not already in there) or just by a custom sql query (which i am trying to stay away from)
but... i want to know if there is some kind of way to do it with the ORM as i have seen model.find().populate('model') doesn't accept parameters beyond the desired model not the where in the find method accept foreign keys conditions.
Partner:{
attributes:{
businessName:{
type:'string'
},
status:{
type:'INT'
}
locations:{
model:'Locations'
via:'partner_id'
}
}
}
Locations:{
attributes:{
partner_id:{
model:'Partners',
type:'INT'
},
neighborhood:{
type:'STRING'
}
}
}
Thanks in advance!
Is this what you looking for?
Locations.find({where: {value1: key1}})
.populate('partners', {where: {value2: key2}})
.exec(function(err, locations){
// Now you've got what you need
});
P/S: Please checkout SailsJS v0.10.x documentation on GitHub. Personally I use this: https://github.com/balderdashy/sails-docs/tree/0.10
The documents are not up-to-date though :-)

Categories