sequelize: Not include model if where conditon of included model matches - node.js

I want to get all RecurringEvents that have no excludeDates for today.
I have following models:
Recurring events
const RecurringEvent = sequelize.definge('recurringEvent,{
id: {type: Sequelize.INTEGER, primaryKey:true},
title: Sequelize.STRING
});
And ExcludeDates with a foreign key recurringEventId
const ExcludeDate = sequelize.define('exclude_date',{
id: {type: Sequelize.INTEGER, primaryKey:true},
recurringEventId: Sequelize.INTEGER,
date: Sequelize.DATE
});
As a relationship i defined
RecurringEvent.hasMany(ExcludeDate, {foreignKey: 'recurringEventId'});
I can get all my RecurringEvents including the the excludeDates with
RecurringEvent.findAll({include:[{model:ExcludeDate}]});
That will give me an output like:
[
{
"id": 1,
"title": "Event1",
"exclude_dates": [
{
"id": 1,
"date": "2019-02-13",
"recurringEventId": 1,
},
{
"id": 2,
"date": "2019-02-14",
"recurringEventId": 1,
}
]
Now i would like to get the Recurring events but only if there is no exclude date for today.
so far i have tried
RecurringEvent.findAll({
include: [{
model: ExcludeDate,
where: {
date: {
[Op.ne]: moment().format('YYYY-MM-DD')
}
}
}]
})
But that only leaves out the ExcludeDate entry with the current Date like that :
[
{
"id": 1,
"title": "Event1",
"exclude_dates": [
{
"id": 2,
"date": "2019-02-14",
"recurringEventId": 1,
}
]
How can i exclude the whole RecurringEvent if and ExcludeDate for it is set for today?
Edit:
I also read in the docs
To move the where conditions from an included model from the ON condition to the top level WHERE you can use the '$nested.column$'
So i have tried this:
RecurringEvent.findAll({where:{
'$exclude_dates.date$':{
[Op.ne]: moment().format('YYYY-MM-DD')
}
},
include: [{model: ExcludeDate}]
})
But without any luck, i'm still getting RecurringEvents just without the one exclude date in the exclude_dates property

Try to add required: true, to your include model to make inner join instead of left join. e.g.
RecurringEvent.findAll({
include: [{
model: ExcludeDate,
required: true,
where: {
date: {
[Op.ne]: moment().format('YYYY-MM-DD')
}
}
}]
})

Related

Adding order clause in queries with nested association in Sequelize

I trying to fetch the assessment details for a class in which assessment_test model has association with student_assessment_test model(hasMany). I want the fetched result ordered according to the marks obtained by student. I am trying the below method but it doesn't seem to be working. Please let me know the correct method for such queries.
const assessmentDetails = await Entity.assessmentTest.findOne({
where: { id: assessmentId, status: 1 },
attributes: [
'id',
'sectionId',
'assessmentDate',
'assessmentTitle',
'totalPoints',
],
include: [
{
association: 'assessmentTestDetails',
where: { status: 1 },
attributes: ['marksObtained', 'percentage', 'testStatus'],
required: true,
order: [['marksObtained', 'ASC']],
include: [
{
association: 'student',
where: { status: 1 },
attributes: ['studentName'],
required: true,
},
],
},
],

How to get Sequelize results WHERE MAX timestamps_of_multiple_nested_objects are greater than a certain date?

I have one main object that I need query results for, based on the timestamps of multiple HasMany note objects related to the main table. There are up to 3 different types of note objects that can be attached to one main object.
With Sequelize, I need to find the max timestamp for each note, then return a member if all of those timestamps are greater than x days old.
Both attempts below seem like they should work. This first tells me I'm using MAX wrong:
main_object.findAll({
include: [
{
model: this.database.note_type1,
as: 'note_type1',
required: false,
},
{
model: this.database.note_type2,
as: 'note_type2',
required: false,
},
],
where: {
[Sequelize.Op.or]: [
Sequelize.where([Sequelize.fn('max', Sequelize.col('note_type1.updated_at')), 'max_updated_at']),
{
'max_updated_at': {
[Sequelize.Op.lt]: max_date,
},
},
Sequelize.where([Sequelize.fn('max', Sequelize.col('note_type2.updated_at')), 'max_updated_at']),
{
'max_updated_at': {
[Sequelize.Op.lt]: max_date,
},
},
],
},
});
I've also tried ordering the includes by timestamp and limiting to 1, which says "column updated_at doesn't exist":
main_object.findAll({
include: [
{
model: this.database.note_type1,
as: 'note_type1',
required: false,
order: [
['updated_at', 'DESC'],
],
limit: 1,
},
{
model: this.database.note_type2,
as: 'note_type2',
required: false,
order: [
['updated_at', 'DESC'],
],
limit: 1,
},
],
where: {
[Sequelize.Op.or]: [
{
'$note_type1.updated_at$': { [Sequelize.Op.lt]: max_date },
},
{
'$note_type2.created_at$': { [Sequelize.Op.lt]: max_date },
},
],
},
)};
With this last solution, I understand Sequelize may not allow ordering this way - however, the one solution I've found does not let me limit or max, it simply orders.

Mongoose populate 3 deep nested schema with JSON response

I have a find() query that when executed, I can see the json with the nested schemas that I want to see except for the 'artista' attribute only displays the id, instead of the properties I want. See below:
{
"total": 1,
"ordenes": [
{
"artpieces": [
{
"_id": "60c1388f30316c02b9f6351f",
"artista": "60c055736c7ca511055a0e1a",
"nombre": "LILIES"
},
{
"_id": "60c12fca30316c02b9f63519",
"nombre": "GERNICA",
"artista": "60c136bf30316c02b9f6351b"
}
],
"_id": "60c414f9ea108a14ef75a9fb",
"precio": 3000,
"usuario": {
"_id": "609c0068e67e68",
"nombre": "Arturo Filio"
}
}
]
}
The query I use to get the json above:
const [total, ordenes] = await Promise.all([
Orden.countDocuments(),
Orden.find()
.populate("usuario", "nombre")
.populate("artpieces", ["nombre","artista","nombre"])
]);
res.json({
total,
ordenes
});
It's an order schema that has artpieces. Each artpiece (I called it 'producto'), has a name a genre, an author/artist and the user which the order belongs to.
my Schema for the orden.js:
const { Schema, model } = require('mongoose');
const OrdenSchema = Schema({
artpieces: [
{
type: Schema.Types.ObjectId,
required: true,
ref: 'Producto'
}
],
estado: {
type: Boolean,
default: true,
required: true
},
usuario: {
type: Schema.Types.ObjectId,
ref: 'Usuario',
required: true
},
precio: {
type: Number,
required: true
}
})
OrdenSchema.methods.toJSON = function () {
const { __v, estado, ...data} = this.toObject();
return data;
}
module.exports = model('Orden', OrdenSchema);
Last thing I want to mention, I know for a fact that I have the code necessary in the artista.js model to display the name of the artist because I have a similar query to display all the artpieces with each artpiece have a genre and an artist.
That example looks like so (to give context):
{
"total": 4,
"productos": [
{
"precio": 0,
"_id": "60c12fca30316c02b9f63519",
"nombre": "GERNICA",
"categoria": {
"_id": "60c04e3605d3c10ed10389e4",
"nombre": "NEO CUBISMO"
},
"artista": {
"_id": "60c136bf30316c02b9f6351b",
"nombre": "PICASSO"
},
"usuario": {
"_id": "609c8c0068e67e68",
"nombre": "Arturo Filio"
}
}
]
}
What am I doing wrong that I can't get my json result at the top look like the json at the bottom, where the artist attribute is?
Also just to point out, I have checked how to nest populate methods in order SO posts including the path and the ref to the Schema and still haven't been able to get the expected result.

How to add a field from associated model using Sequelize?

I'm making a web API using Node, Express, and Sequelize. I have models Users and Teams (shown below). Users has a teamId that references Teams.id, and there is an association between the two to reflect that.
User definition
const User = sequelize.define('User', {
id: {
autoIncrement: true,
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true
},
displayName: {
type: DataTypes.STRING,
allowNull: false
},
teamId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'Team',
key: 'id'
}
}
}
Team definition
const Team = sequelize.define('Team', {
id: {
autoIncrement: true,
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true
},
name: {
type: DataTypes.STRING,
allowNull: false
}
}
Association
User.belongsTo(Team, { as: 'team', foreignKey: 'teamId' });
Query
Users.findAll({
include: [
{
model: Team,
as: 'team'
}
]
});
As expected, this returns a list of User objects, with the added "team" property the respective Team objects embedded like so:
[
{
"id": 1,
"displayName": "John Smith",
"teamId": 1,
"team": {
"id": 1,
"name": "My Awesome Team"
}
}
]
My goal is to return User objects, but instead of embedding the entire Team object under the team property, I'd like to add just the name of the team as the value of the property, like this:
[
{
"id": 1,
"displayName": "John Smith",
"teamId": 1,
"team": "My Awesome Team"
}
]
Is there a way to accomplish this with Sequelize?
Well, through typing up my question I thought of better ways to search for the answer, and I think I found one right off the bat...
I found this StackOverflow question where the asker was doing what I am trying to do.
The solution is to use attributes.include and Sequelize.col() to include the attribute I want, and in my include options, use attributes: [] to hide the Team objects.
Going off the examples I included in the original post, this is what works for me, giving me nearly the exact output I wanted:
Users.findAll({
attributes:{
include: [[Sequelize.col(team.name), 'teamName']]
}
include: [
{
model: Team,
as: 'team',
attributes: []
}
]
});
Output
[
{
"id": 1,
"displayName": "John Smith",
"teamId": 1,
"teamName": "My Awesome Team"
}
]
Since the association uses the alias "team", I had to name my property something different, and went with "teamName" which is perfectly fine and probably preferable since it is more descriptive.

Sequelize ORM return a weird response after inner join tables in nodejs

I use sequelize orm to manage my data base (mysql).
I make a inner join that work good but the problem that the table that join return a weird variable.
this is my code
const getReports = id => {
return new Promise((resolve, reject) => {
models.Report.findAll({
where: { companyID: [513603324, 515490704, 511493827] },
include: [{
model: models.Reports_type,
attributes:["name"],
required: true
}],
raw: true
})
.then(result => {
resolve(result);
})
.catch(err => {
reject(err);
});
});
};
The output is
[
{
"id": 8,
"creatorUserID": 1,
"currentUserEditorID": 1,
"companyID": 511493827,
"stageID": 1,
"scenarioID": 1,
"typeID": 1,
"year": 2020,
"deadLine": "2019-10-30T22:00:00.000Z",
"createdAt": "2019-10-29T08:31:19.000Z",
"updatedAt": "2019-10-29T08:31:19.000Z",
"Reports_type.name": "excelent",
"companyName": "energy",
}
]
The problem is i get it weird like this:
"Reports_type.name"
I want the output be:
"name"
This topic has been covered before - see this.
To avoid the prefix, attributes must be specified in the main model rather than the included model. The example below should produce all fields in Report plus Reports_type.name. Note: the alias of Reports_type may be a little different than I've guessed - if you get a "field does not exist", find the correct alias from the generated SQL.
models.Report.findAll({
where: { companyID: [513603324, 515490704, 511493827] },
include: [{
model: models.Reports_type,
attributes:[], // suppress here
required: true
}],
raw: true,
attributes: {
include: [[Sequelize.col("reports_types.name"), "name"]] // include here; table alias may be a little different!
}
})

Resources