Sequelize ORM include model only if active = true - node.js

how can I findAll orders with id: 1 and include items only if this item has active = true? Otherwise there will be empty array...
Order.findAll({
where: { id: 1 },
include: [
{ model: Item, where: sequelize.and({'active' : true }) }
]
}).then(function(order) {
callback(null, order);
});
This shows me only orders where are some items with active = true. I wanted to show all orders with id: 1 and items as a sub-array ...

From Sequelize Tutorial:
Adding a where clause to the include automatically makes it required
The solution is this:
Order.findAll({
where: { id: 1 },
include: [
{ model: Item,
where: sequelize.and({'active' : true }),
required: false
}
]
}).then(function(order) {
callback(null, order);
});

Related

how to set order on include aggregate function alias in sequelize?

Below is my code
export async function getMany(page: number, recordsPerPage: number, condition: any = {}, order: any, attributes: string[] = [], other: object = {}) {
try {
let { count, rows }: any = await User.findAndCountAll({
attributes: {
include: [[sequelize.literal('(SELECT SUM(reputation) FROM scores where scores.user_id = User.id)'), 'reputation']],
exclude: attributes,
},
where: condition,
distinct: true,
include: [
{
model: Skill,
as: 'skills',
attributes: ['skill'],
through: { attributes: [] },
},
],
order: order,
offset: page,
limit: recordsPerPage,
...other,
logging: console.log,
});
return { count, rows };
} catch (e) {
return false;
}
}
I want to set order by reputation field which is alias of sum function column. i want my data in highest to lowest reputation.
You can simply replace the order property with the given snippet. Worked for me.
order: [[sequelize.literal('table alias name goes here'), 'DESC']]

complicated query using sequelize

i have 3 tables .. profile, player, garage
profile has winCount , loseCount
player has username
garage has carNumber
they are all connected with each other using the id from player table (playerId in table garage and profile)
i want to get the
top 20 winCount , username , carnumber
i am trying this code
let racers = await Profile.findAll({
where: {
carRaceWinCount: {
[Op.gt]: 0
}
},
limit: 20,
order: [
['carRaceWinCount', 'DESC']
]
})
.then((races: any) => {
Garage.findAll({
where: {
playerId: races.id
},
attributes : ['carNum']
})
Player.findAll({
where :{
id : races.id
},
attributes : ['username']
})
})
and it is not working
what is the best way to get this query
If you have these associations:
Player.hasMany(Profile, { foreignKey: 'playerId' })
Player.hasMany(Garage, { foreignKey: 'playerId' })
Profile.belongsTo(Player, { foreignKey: 'playerId' })
then the query might look like this
await Profile.findAll({
where: {
winCount: {
[Op.gt]: 0
}
},
limit: 20,
include: [{
model: Player,
include: [{
model: Garage,
// this is important option to query all garames as separate queries, Otherwise you will get wrong count of profiles.
separate: true
}]
}]
order: [
['winCount', 'DESC']
]
})

Sequelize js not working as expected with Having and Group by

is there something wrong with my ORM query? I am trying to use sequelize group and having in order to filter Reservation dates that have more than 30 records:
SELECT "reservations"."reservationDate" FROM reservations, orders
WHERE "reservations"."orderId" = "orders"."orderId"
AND Orders.confirmed = true
GROUP BY "reservationDate"
HAVING COUNT("reservationDate") >= 30;
db.Reservations.findAll({
attributes: ['reservationDate'],
where: {
reservationDate: {
[Sequelize.Op.between]: [fistMonthDay, lastMonthDay],
},
},
include: [{
model: db.Orders,
attributes: [],
where: { confirmed: true }
}],
group: ['reservationDate'],
having: {
[Sequelize.fn("COUNT", Sequelize.col("reservationId"))]: {
[Sequelize.Op.gte]: 30,
}
}
})
Easy workaround:
having: Sequelize.literal(`count("reservation"."reservationId") >= 30`)

Sequelize - Select on associated table

My problem:
I am creating an route which will return some informations about a group, it has an id, an user assigned and also has some documents. I just want to show how much documents exists, in SQL would be SELECT COUNT, but how can i do this in this in sequelize?
My code:
async list(req, res){
const docGroups = await DocGroup.findAll({
raw: true,
include: [{
model: User,
as: 'userAssigned'
},
{
model: Document,
as: 'Document'
}
]
}).then(groups => {
const result = groups.map(group => {
return Object.assign(
{},
{
id: group.id,
name: group.name,
userAssinged: group['userAssigned.firstName'],
docAmount: // I want to put documents' count here
}
)
})
console.log(groups)
})
}
What is printed in console.log(groups):
[
{
id: 1,
name: 'pleaseworks',
createdAt: 2020-06-10T02:38:11.531Z,
updatedAt: 2020-06-10T02:38:11.531Z,
'userAssigned.id': 1,
'userAssigned.firstName': 'Please',
'userAssigned.lastName': 'Works',
'userAssigned.email': 'pleaseworks#gmail.com',
'userAssigned.password': '$2a$08$3BA4I4dsaQ3lsHy342344b5P41v5eHWjwqv6dve28nSdqbGvhsdS',
'userAssigned.createdAt': 2020-06-10T02:37:29.062Z,
'userAssigned.updatedAt': 2020-06-10T02:37:29.062Z,
'userAssigned.groupId': null,
'Document.id': 2,
'Document.description': 'deowkdopewkdwe',
'Document.content': 'odepodkewokodwe',
'Document.groupId': 1,
'Document.createdAt': 2020-06-10T02:43:46.005Z,
'Document.updatedAt': 2020-06-10T02:43:46.005Z
}
]
If DocGroup has many Document try something like this:
{
model: Document,
attributes: [[sequelize.fn('COUNT', sequelize.col('id')), 'docAmount']]
as: 'Document'
}

Push into a List and Pop Conditionally in Mongoose and MongoDB

I am creating a list of scores for a user in mongoDB by adding a new score 1 at a time and sorting the list. I want to remove the lowest score when the list grows larger than 5 elements.
The reason for this is because I want to store the top 5 scores of the user.
What would be the best way to do this? Is there a way to make the whole thing an atomic operation?
My code is below. I'm using NodeJS with Mongoose and MongoDB.
const maxScoresToStore = 5
var scoreEntrySchema = new Schema({
score: Number,
when: { type: Date, default: Date.now }
})
var scoreSchema = new Schema({
_userid: { type: Schema.Types.ObjectId, ref: 'Users' },
username: {type: String, index:{unique: true}},
scores: [scoreEntrySchema]
})
const scoreModel = mongoose.model("Scores", scoreSchema)
exports.addUserScore = (uid, uname, score) => {
var query = {_userid:uid, username:uname},
update = { $push : {"scores" : {$each: [{"score": score}], $sort: {"score":-1}}} }, // sorts in descending order after pushing
options = { upsert: true, new: true, setDefaultsOnInsert: true };
scoreModel.findOneAndUpdate(query, update, options).then(
(result)=>{
if(result.scores.length > maxScoresToStore)
{
// ToDo:
// result.update({$pop: {"scores" : 1 }}) // pops the last element of the list
}
}
)
}
You can use $slice operator, And your query looks like:
let score = await scoreModel.findOneAndUpdate({ _userid: uid, username: uname },
{
$push: {
scores: {
$each: [{ score: score }],
$sort: { score: -1 },
$slice: maxScoresToStore
}
}
},
{
upsert: true,
new: true,
setDefaultsOnInsert: true,
multi: true
});
[DO VOTE TO THIS ANSWER, IF ITS HELPFUL TO YOU]
You can add slice option to your update option:
update = {
$push: {
scores: { $each: [{ score: score }], $sort: { score: -1 }, $slice: maxScoresToStore }
}
}
Here is the full method code written in async/await style:
exports.addUserScore = async (uid, uname, score) => {
const query = { _userid: uid, username: uname };
const update = {
$push: {
scores: {
$each: [{ score: score }],
$sort: { score: -1 },
$slice: maxScoresToStore
}
}
};
const options = {
upsert: true,
new: true,
setDefaultsOnInsert: true,
multi: true
};
try {
let score = await scoreModel.findOneAndUpdate(query, update, options);
if (!score) res.send(404).send("Score not found");
res.send("Everything is ok");
} catch (err) {
console.log(err);
res.status(500).send("Something went wrong");
}
};
I'm not certain If this would help, but it might work
scoreModel.update(
{ "scores.5": { "$exists": 1 } },
{ "$pop": { "scores": 1 } },
{ "multi": true }
)
As you are already sorting by descending, you can check if the array length is greater than 5 by using scores.5, If this returns true then you can pop the last element using $pop.
If $exists return false then it will skip the query. you can run this update after .then() and you won't have to use if condition.
But keep in mind $pop will only remove 1 element.

Resources