Sequelize Op.or with two Op.and - node.js

I need get WHERE query like this:
WHERE (x = 1 AND y = 2) OR (x = 2 AND y = 1)
In sequelize i write:
where: {
[Op.or]: {
[Op.and]: {
ownerId: candidate.recipientId,
recipientId: candidate.senderId,
},
[Op.and]: {
recipientId: candidate.recipientId,
ownerId: candidate.senderId,
},
},
},
But i get:
WHERE (("Chat"."recipient_id" = 2 AND "Chat"."owner_id" = 1))

Try this way:
where: {
[Op.or]: [{
ownerId: candidate.recipientId,
recipientId: candidate.senderId,
},{
recipientId: candidate.recipientId,
ownerId: candidate.senderId,
}]
}

Related

Mongoose: Calculate the number of grouped documents

I have this mongoose query:
const filter_stage = {
$match: {
category: "LOGIN",
},
};
const active_users_group_stage = {
$group: {
_id: "$user",
number_of_logins: { $sum: 1 },
},
};
const pipeline = [filter_stage, active_users_group_stage];
const active_users_stats = await History.aggregate(pipeline);
response.active_users_stats = active_users_stats;
const number_of_active_users = active_users_stats.length;
This is the History model:
const HistorySchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: "users",
},
category: {
type: String,
enum: ["LOGIN","LOGOUT"],
required: true,
},
date: {
type: Date,
default: Date.now,
},
});
And it returns something like this:
[
{
"_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"number_of_logins": 45
},
{
"_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"number_of_logins": 36
},
{
"_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"number_of_logins": 26
},
{
"_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"number_of_logins": 18
},
{
"_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"number_of_logins": 18
},
]
Instead of calculating the number of active users like this:
const number_of_active_users = active_users_stats.length;
I would like to calculate it inside the aggregate pipeline.
Is this possible?
You need $group all with _id: null to sum all active_users and add all documents into the users array as the last stage.
{
$group: {
_id: null,
number_of_active_users: {
$sum: 1
},
users: {
$push: "$$ROOT"
}
}
}
As the active_users_stats result will return an array with only one document,
...
const all_group_stage = {
$group: {
_id: null,
number_of_active_users: {
$sum: 1
},
users: {
$push: "$$ROOT"
}
},
};
const pipeline = [filter_stage, active_users_group_stage, all_group_stage];
const active_users_stats = await History.aggregate(pipeline);
response.active_users_stats = active_users_stats[0].users;
const number_of_active_users = active_users_stats[0].number_of_active_users;

Getting items that referenced an object id

// PRODUCT MODEL
const productSchema = new mongoose.Schema(
{
category: {
type: mongoose.Schema.Types.ObjectId,
ref: "Category",
},
name: {
type: String,
required: true,
},
price: {
type: Number,
required: true,
default: 0,
},
colors: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Color",
},
],
sizes: {
type: Array,
default: [
{ id: 1, size: "XS" },
{ id: 2, size: "S" },
{ id: 3, size: "M" },
{ id: 4, size: "L" },
{ id: 5, size: "XL" },
{ id: 6, size: "XXL" },
],
},
},
{ timestamps: true }
);
const Product = mongoose.model("Product", productSchema);
export default Product;
// CATEGORY MODEL
const CategorySchema = new mongoose.Schema(
{
name: String,
},
{ timestamps: true, toJSON: true, toObject: true }
);
CategorySchema.virtual("items").get(async function () {
return await CategorySchema.aggregate([
{
$lookup: {
from: "Product",
localField: "category",
foreignField: "_id",
as: "items",
},
},
]);
});
const Category = mongoose.model("Category", CategorySchema);
export default Category;
Hello, please i'm trying to get list of items that referenced each categoryid on the product model, i want to add the result as a virtual field called "count" from the category model and not the product model. i'm getting this error "TypeError: Cannot use 'in' operator to search for 'transform' in true".
i want to get the result the way it was done in this example from mongodb doc about inventory and orders but i'm getting "items: {}". docs.mongodb.com/manual/reference/operator/aggregation/lookup
router.get(
"/counts",
catchAsync(async (req, res) => {
const stats = await Category.aggregate([
{
$lookup: {
from: "products",
localField: "_id",
foreignField: "category",
as: "count",
},
},
{
$project: {
name: 1,
image: 1,
count: {
$cond: {
if: { $isArray: "$count" },
then: { $size: "$count" },
else: "NA",
},
},
},
},
]);
if (!stats) {
return res.status(404).json("No data found");
}
res.status(200).json(stats);
})
);
For anyone that is trying to achieve the result i wanted, i used Category.aggregate on the category route instead of creating a virtual field from the category model.

Column not exist in order by clause

I'm using Sequelize to query in to a table like this:
const students = await User.findAll({
attributes: ['id', [Sequelize.literal(`"firstName" || ' ' || "lastName"`), 'name']],
where: {
[Op.or]: [
{
firstName: {
[Op.iLike]: `%${search}%`
}
},
{
lastName: {
[Op.iLike]: `%${search}%`
}
},
Sequelize.literal(`"firstName" || ' ' || "lastName" ILIKE '%${search}%'`)
]
},
order: Sequelize.literal(`"organization_users"."firstName" ${NATURAL_SORT} ${sort}`),
limit,
offset: limit * (page - 1),
distinct: true
});
And it work ok but if I include some model, the column "firstName" is not exist:
const students = await User.findAll({
attributes: ['id', [Sequelize.literal(`"firstName" || ' ' || "lastName"`), 'name']],
where: {
[Op.or]: [
{
firstName: {
[Op.iLike]: `%${search}%`
}
},
{
lastName: {
[Op.iLike]: `%${search}%`
}
},
Sequelize.literal(`"firstName" || ' ' || "lastName" ILIKE '%${search}%'`)
]
},
include: [
{ model: ProgressGrade, as: 'student_progress_grade', required: true, where: { courseId } },
{
model: CourseRole,
as: 'course_roles',
attributes: [],
where: { roleName: 'Student' },
required: true
},
{ model: Course, as: 'courses', attributes: [], where: { id: courseId }, required: true }
],
order: Sequelize.literal(`"organization_users"."firstName" ${NATURAL_SORT} ${sort}`),
limit,
offset: limit * (page - 1),
distinct: true
});
Error message:
error: SequelizeDatabaseError: column organization_users.firstName
does not exist
This is the first time I run into this problem, I did many search but hopeless. Anyone have an idea about how Sequelize work here?
I'm using Sequelize 5.21.5 running on Nodejs 13.8.0, PostgreSQL 10.12
Please set subQuery property to false as follows -
const students = await User.findAll({
attributes: ['id', [Sequelize.literal(`"firstName" || ' ' || "lastName"`), 'name']],
where: {
[Op.or]: [
{
firstName: {
[Op.iLike]: `%${search}%`
}
},
{
lastName: {
[Op.iLike]: `%${search}%`
}
},
Sequelize.literal(`"firstName" || ' ' || "lastName" ILIKE '%${search}%'`)
]
},
include: [
{ model: ProgressGrade, as: 'student_progress_grade', required: true, where: { courseId } },
{
model: CourseRole,
as: 'course_roles',
attributes: [],
where: { roleName: 'Student' },
required: true
},
{ model: Course, as: 'courses', attributes: [], where: { id: courseId }, required: true }
],
order: Sequelize.literal(`"organization_users"."firstName" ${NATURAL_SORT} ${sort}`),
limit,
offset: limit * (page - 1),
distinct: true,
subQuery: false
});
I hope it helps!

How to get data only if date is more than 45 days in aggregate mongoose?

I want to get data based on date which is not exceeded 45 days only. I am using aggregation and I have a column postdate so I want to get data only if postdate is within 45 days.
below code postdate is column name:
const baseStages = [
{
$lookup: {
from: CrewbiesJobs.collection.name,
localField: "jobId",
foreignField: "jobId",
as: "job_docs"
}
},
{
$unwind: "$job_docs"
},
{
$project: {
_id: 1,
jobTitle: 1,
jobId: 1,
// jobDescription: 1,
postedDate: 1,
filter1: 1,
filter2: 1,
filter3: 1,
createdAt: 1,
updatedAt: 1
}
},
{ $match: filter },
{ $sort: { postedDate: -1 } }
];
const jobsStages = [
...baseStages,
{ $skip: query.skip },
{ $limit: query.limit }
];
let jobsAggregate = await Jobs.aggregate(jobsStages);
And here is my mongoose schema:
let jobsSchema = new mongoose.Schema(
{
jobTitle: String,
jobId: {
type: Schema.Types.String,
unique: true,
dropDups: true,
ref: "Jobs"
},
jobDescription: String,
postedDate: String,
filter1: [{ label: String, value: String }],
filter2: [{ label: String, value: String }],
filter3: [{ label: String, value: String }]
},
{ timestamps: true }
);

How to server side filtering each field of a table in sequelize (nodejs orm)

I have some trouble to find out how i have to do the filtering part in sequelize ORM.
I already did, server-side pagination and sorting, Following this tutorial for Angular https://blog.angular-university.io/angular-material-data-table/
I'am using Sequelize 5.8.5 but not in typescript.
But, in my case, the backend is in nodejs using Sequelize. And i want to do a search of the filter string parameter on each fields of my joined table.
If "string" like field1 OR "string" like field2 OR .... etc
Here is my router.get in sequelize.
router.get('', auth.checkToken, function(req, res) {
let options = {
include: [{
model: models.intervention,
attributes: ['id','start_date','end_date','agent_id','state_id'],
required: false,
include: [{
model: models.agent,
required: false,
},
{
model: models.state,
required: false,
},
{
model: models.intervention_participation,
required: false,
include: [{
model: models.agent,
attributes: ['id','access_token','agent_type_id','firstname','lastname','mail','password'],
required: false,
include: [{
model: models.shift_do,
attributes: ['id','shift_id','agent_id','shift_date'],
required: false,
on: {
'agent_id': {
[Op.eq]: Sequelize.col('intervention->agent.id')
},
'shift_date': {
[Op.eq]: Sequelize.col('intervention.start_date')
},
},
include: [{
model: models.shift,
required: false,
}]
}]
}]
},
{
model: models.operating_range_do,
required: false,
include: [{
model: models.operating_range,
required: false,
}]
},
{
model: models.equipment_intervention,
required: false,
include: [{
model: models.equipment,
required: false,
include: [{
model: models.equipment_type,
required: false,
include: [{
model: models.work_field,
required: false,
}]
},
{
model: models.equipment_location,
required: false,
include: [{
model: models.structure,
required: false,
},
{
model: models.local,
required: false,
},
{
model: models.place,
required: false,
}]
}]
}]
},
{
model: models.intervention_helper,
required: false,
include: [{
model: models.external_actor,
required: false,
}]
}]
},
{
model: models.work_type,
required: false
},
{
model: models.di_externe,
required: false
}]
};
if (req.query.filter) {
options.where = {
$or: [
{ id: { $like: req.query.filter}},
{ title: { $like: req.query.filter}},
{ description: { $like: req.query.filter}},
{ start_date: { $like: req.query.filter}},
]
};
}
if (req.query.sort) {
options.order = [['id', req.query.sort || 'DESC']];
}
if (req.query.page && req.query.pageSize) {
options.offset = req.query.page * req.query.pageSize;
options.limit = parseInt(req.query.page, 10) + parseInt(req.query.pageSize, 10);
}
models.intervention_maincourante
.findAll(options)
.then(all => {
res.send(all);
});
});
its this part who don't work :
if (req.query.filter) {
options.where = {
$or: [
{ id: { $like: req.query.filter}},
{ title: { $like: req.query.filter}},
{ description: { $like: req.query.filter}},
{ start_date: { $like: req.query.filter}},
]
};
}
I also tried this way because sometimes the alias for operators didn't works? But it didn't change anything.
[Op.or]: [
{ id: { [Op.like]: "%" + req.query.filter + "%"}},
{ title: { [Op.like]: "%" + req.query.filter + "%"}},
{ description: { [Op.like]: "%" + req.query.filter + "%"}},
{ start_date: { [Op.like]: "%" + req.query.filter + "%"}},
]
Then i tried :
options.where = {
[Op.or]: [{
title: {
[Op.like]: "%" + req.query.filter + "%",
},
description: {
[Op.like]: "%" + req.query.filter + "%",
},
start_date: {
[Op.like]: "%" + req.query.filter + "%",
}
}]
};
What's in the documentation :
{
[Op.or]: [
{
title: {
[Op.like]: 'Boat%'
}
},
{
description: {
[Op.like]: '%boat%'
}
}
]
}
Thanks a lot, if someone know how to do it.
As you suggested in the comments that query doesn't logs with filter. So assuming there should be any error.
Try to catch the error and log it to identify the issue
models.intervention_maincourante
.findAll(options)
.then(all => {
res.send(all);
}).catch(err => {
console.log("err", err);
});
with the help of this, you will be identifying the exact root cause.
Update:
Based on the error, Please check your like clause.
Your like clause should be something like this. Also, make sure you are referring right column names.
options.where = {
[Op.or]: [{
title: {
[Op.like]: `%${req.query.filter}%`,
},
description: {
[Op.like]: `%${req.query.filter}%`,
},
start_date: {
[Op.like]: `%${req.query.filter}%`,
}
}]
};
In Sequelize ORM when you use $like operator you can pass the search string directly or wrapped inside % OR _. if you want to match some part of the string you can use $substring operator.
if your query is a plain string like this 'examplematch' $like operator will look for an exact match.
Refer this doc https://sequelize.org/master/manual/querying.html#operators
It's working.
const whereStatement = {}
const { status, startDate, endDate, search } = req.query;
if (status) {
whereStatement.status = { [Op.eq]: status };
}
if (startDate && endDate) {
whereStatement.createdAt = { [Op.between]: [startDate, endDate] }
}
if (search) {
whereStatement.username = { [Op.like]: `%${search}%` }
}
let Datefilter = "";
if (startDate && endDate) {
Datefilter =
startDate && endDate ? {
createdAt: {
$gte: moment(startDate).startOf("day").toDate(),
$lte: moment(endDate).endOf("day").toDate(),
},
} : {};
console.log("startDateto", Datefilter);
} else if (startDate) {
console.log("startDate");
Datefilter = startDate
? {
createdAt: {
$gte: moment(startDate).startOf("day").toDate(),
$lte: moment(new Date()).endOf("day").toDate(),
},
} : {};
}
feedback = await feedbackModel.paginate({
where: whereStatement,
Datefilter
});

Resources