Combined WHERE clause for all INCLUDEs - javascript

i want to perform this query in sequelize models:
SELECT Cou.country_id,cou.country_name, Sta.state_id, Sta.state_name, Dis.district_id,
Dis.district_name,Cit.city_id, Cit.city_name,Loc.location_id,Loc.location_name,Sub_Loc.sub_location_id,Sub_Loc.sub_location_name,Prop.property_id,Prop.property_name,Prop.hp_builders_id,
Bud.builders_id,Bud.builders_name
FROM hp_country Cou
INNER JOIN hp_state Sta ON Cou.country_id = Sta.hp_country_id
INNER JOIN hp_district Dis ON Sta.state_id = Dis.hp_state_id
INNER JOIN hp_city Cit ON Dis.district_id = Cit.hp_district_id
INNER JOIN hp_location Loc ON Cit.city_id = Loc.hp_city_id
INNER JOIN hp_sub_location Sub_Loc ON Loc.location_id = Sub_Loc.hp_location_id
INNER JOIN hp_property Prop ON Sub_Loc.sub_location_id=Prop.hp_sub_location_id
LEFT JOIN hp_builders Bud ON Prop.hp_builders_id=Bud.builders_id
where (Cou.country_status=1 AND Sta.state_status=1 AND Cou.country_id=1)
AND (Dis.district_name LIKE '%ky%' OR Cit.city_name LIKE '%ky%' OR Loc.location_name LIKE '%ky%' OR Sub_Loc.sub_location_name LIKE '%ky%' OR Prop.property_name LIKE '%ky%')
I tried to write in this manner but i did not achieved my target query :
Included more nested models because of all are inner joins and i tried with required is true in include box.how to do [OR] condition for columns which is in different tables
hp_country.findAll({
attributes: ['country_id', 'country_name'],
where: {
country_status: 1,
country_id: 1
},
include: [{
model: hp_state,
attributes: ['state_id', 'state_name'],
where: {
state_status: 1,
},
include:[{
model: hp_districts,
attributes: ['district_id', 'district_name'],
where: {
district_status: 1
},
include:[{
model: hp_city,
attributes: ['city_id', 'city_name'],
where: {
city_status: 1,
city_name :{
$like: '%ma%'
}
},
include:[{
model: hp_location,
attributes: ['location_id', 'location_name'],
where: {
location_status: 1,
location_name :{
$like: '%ma%'
}
},
include:[{
model: hp_sub_location,
attributes: ['sub_location_id', 'sub_location_name'],
where: {
sub_location_status: 1,
sub_location_name :{
$like: '%ma%'
}
}
}]
}]
}]
}]
}]
})

I tried to fix your query, but didn't test it. If you face any problem just notify me:
hp_country.findAll({
attributes: ['country_id', 'country_name'],
where: {
//main AND condition
$and: [
//first joint condition
{
$and: [
{ country_status: 1 },
{ country_id: 1 },
Sequelize.literal("Sta.state_status = 1") //I put 'state_status' here because it was a joint condition to be true
],
},
//second joint condition
{
$or: [
Sequelize.literal("Dis.district_name LIKE '%ky%'"),
Sequelize.literal("Cit.city_name LIKE '%ky%'"),
Sequelize.literal("Loc.location_name LIKE '%ky%' "),
Sequelize.literal("Sub_Loc.sub_location_name LIKE '%ky%'"),
Sequelize.literal("Prop.property_name LIKE '%ky%'")
]
}
]
},
include: [
{
model: hp_state,
attributes: ['state_id', 'state_name']
},
{
model: hp_districts,
attributes: ['district_id', 'district_name']
},
{
model: hp_city,
attributes: ['city_id', 'city_name']
},
{
model: hp_location,
attributes: ['location_id', 'location_name']
},
{
model: hp_sub_location,
attributes: ['sub_location_id', 'sub_location_name']
}
]
})

Related

Sequelize include nested column: Unknown column in 'where clause'

I am trying to use the Sequelize ORM's feature that allows referring the nested column from the included Models (See Sequelize Docs: Complex where clauses at the top-level). In the docs it states that, I can use $nested.column$ syntax.
The following is what I was trying to do:
let where = { memberId };
if (req.query.search) {
const like = { [Op.like]: `%${req.query.search}%` };
where = {
...where,
[Op.or]: [
{ '$bookItem.serial$': like },
{ '$bookItem.book.name$': like },
{ '$bookItem.book.ISBNCode$': like },
],
};
}
const options = {
where,
include: [
{
model: models.BookItem,
as: 'bookItem',
required: false,
include: [
{
model: models.Book,
as: 'book',
attributes,
required: false,
},
],
},
],
});
const transactions = await models.Borrow.findAll(options);
However, for the code above, I am getting the following error:
"Unknown column 'bookItem.serial' in 'where clause'"
What am I missing?
Full DB Schema: https://dbdiagram.io/d/5e08b6aaedf08a25543f79cb
Is bookitem a Table? Or a Database?
bookItem.serial either represents db.tbl or tbl.column
bookItem.book.name can only represent db.tbl.column
Since bookItem seems to be a database name, then serial must be a table name. At that point, "tablename LIKE ..." is a syntax error.
In your linked documentation books has no name column, change $bookItem.book.name$ to $bookItem.book.title$, and try adding right: true below required: false to create an inner join.
I have corrected this error on my side. Initially, I am writing this query
but now I have rearranged the query and it works
WRONG QUERY
let where = {
[op.and]: [
{ id: partner_id },
{ [op.or]: [
{ '$customers.customer_name$': { [op.like]: '%' + query + '%'} },
{ '$customers.email$': { [op.like]: '%' + query + '%'} },
]},
],
};
// const where = {
// id: partner_id
// }
return await this.deliveryBoys.findOne({
attributes: [
['id', 'partner_id'],
'delivery_boy_name',
'email',
'profile_picture',
'phone_number',
'fcm_token',
],
include: [
{
model: this.customers,
as: 'customers',
attributes: ['id', 'customer_name', 'email', 'profile_picture', 'phone_number'],
require: true,
where: {
customer_name: {
[op.like]: '%' + query + '%'
}
},
include: [
{
model: this.company,
as: 'company',
},
{
model: this.address,
as: 'address',
required: false,
where: {
status: 1
}
}
]
},
],
where,
});
FINAL WORKING QUERY
let where = {
[op.and]: [
{ '$deliveryBoys.id$': partner_id },
{ [op.or]: [
{ '$customers.email$': { [op.like]: '%' + query + '%'} },
{ '$customers.customer_name$': { [op.like]: '%' + query + '%'} },
{ '$customers.phone_number$': { [op.like]: '%' + query + '%'} },
{ '$company.name$': { [op.like]: '%' + query + '%'} },
]},
],
};
return await this.customers.findAll({
attributes: ['id', 'customer_name', 'email', 'profile_picture', 'phone_number'],
include:[
{
model: this.deliveryBoys,
as: 'deliveryBoys',
attributes: ['id','delivery_boy_name','phone_number','email','profile_picture','status',],
where:{
id: partner_id
}
},
{
model: this.company,
as: 'company',
},
{
model: this.address,
as: 'address',
required: false,
where: {
status: 1
}
}
],
where
});

MongoDB: Project only the items that was queried for in the array?

I have a user document, each user has an array of objects
Given an array of item tags, I need to find the user whose item array has the item-tag, and return the entire user object except the items array, in which I only want to return the first item tags that existed in the tagArray that was used for the intial query.
//user document
{
user: 'John',
items: [ObjectId('ABC'), ObjectId('123') ...]
}
//item document
{
_id: ObjectId('ABC'),
tag: 'some-unique-id'
},
{
_id: ObjectId('DEF'),
tag: 'some-unique-tag'
}
Users have a 1-to-N relationship with items, the items may repeat within the User's items array.
This is what I current have, which returns the entire user object, but also all the items within the array.
const tagArray = [ 'some-unique-id', 'some-unique-tag']
items.aggregate([
{ $match: { 'tag': { $in: tagArray } }},
{ $lookup: {
from: "users",
localField: "tag",
foreignField: '_id',
as: 'userInfo'
}
},
{
$project: {??} //<--- I'm pretty sure I'm missing something in the project
])
Outcome that I have now:
{
_id: ObjectId('ABC'),
tag: 'some-unique-id'
userInfo : [ {user: 'John', items: [ObjectId('ABC'), ObjectId('123') ...] }]
}
What I want to achieve:
{
_id: ObjectId('ABC'),
tag: 'some-unique-id'
userInfo : [ {user: 'John', items: [ObjectId('ABC')]} ]
}
Edit:
There is a similar question here : Retrieve only the queried element in an object array in MongoDB collection
However in my case, I need the filter condition to be "one of the the tags that is in the tagArray.
Any suggestion or pointers would be appreciated, thank you!
I don't know if I understood well what you need, but I think this is a good start (maybe you can modify it by yourself):
Test data:
// users collection
[
{
user: "John",
items: [
ObjectId("5a934e000102030405000002"),
ObjectId("5a934e000102030405000003")
]
}
]
// items collection
[
{
_id: ObjectId("5a934e000102030405000002"),
tag: "some-unique-id"
},
{
_id: ObjectId("5a934e000102030405000009"),
tag: "some-unique-tag"
}
]
}
Query:
db.users.aggregate([
{
$lookup: {
from: "items",
localField: "items",
foreignField: "_id",
as: "userInfo"
}
},
// create new fields inside the userInfo array
{
$project: {
"userInfo.user": "$user",
"userInfo.items": "$items",
"tag": {
$arrayElemAt: ["$userInfo.tag", 0]
}
}
},
// filter the userInfo.items field, based on _id field
// it's important to use $arrayElemAt here
{
$addFields: {
"userInfo.items": {
$filter: {
input: {
$arrayElemAt: [
"$userInfo.items",
0
]
},
as: "i",
cond: {
$in: [
"$$i",
[
"$_id"
]
]
}
}
}
}
}
])
Result:
[
{
"_id": ObjectId("5a934e000102030405000002"),
"tag": "some-unique-id",
"userInfo": [
{
"items": [
ObjectId("5a934e000102030405000002")
],
"user": "John"
}
]
}
]

Value added to array when using $project

I am writing an aggregation pipeline to return a win ratio. When I use $sum the value is output from $facet $project within an array. This has me confused. To solve the issue I simply run $sum on the arrays when I calculate the winRatio, which works fine. How do I use $project without it adding values into an array?
Round.aggregate([
{
$match: {
$and: query,
},
},
{
$facet: {
wins: [
{
$match: {
winner: user,
},
},
{
$group: {
_id: { user: '$scores.player', game: '$game' },
value: { $sum: 1 }, // value *not* within array
},
},
],
rounds: [
{
$unwind: '$scores',
},
{
$match: {
'scores.player': user,
},
},
{
$group: {
_id: { user: '$scores.player', game: '$game' },
value: { $sum: 1 }, // value *not* within array
},
},
],
},
},
{
$project: {
_id: '$rounds._id',
rounds: '$rounds.value', // value within an array
wins: '$wins.value', // value within an array
winRatio: { ... },
},
},
]);
Schema:
const schema = new mongoose.Schema(
{
game: { type: mongoose.Schema.ObjectId, required: true },
scores: [
{
player: { type: mongoose.Schema.ObjectId, ref: 'User', required: true },
playerName: { type: String }, // denormalise
score: { type: Number, required: true },
},
],
winner: { type: mongoose.Schema.ObjectId, required: true },
datePlayed: { type: Date },
},
{ timestamps: true },
);
Your asking why $sum 'works' and $project dosent.
Lets start off by understand the output of the $facet phase.
{
"wins" : [
{
"_id" : {
"user" : [
"player1",
"player2"
],
"game" : 1.0
},
"value" : 2.0
}
],
"rounds" : [
{
"_id" : {
"user" : "player1",
"game" : 1.0
},
"value" : 3.0
}
]
}
As we can see each document result is an array, even though you grouped at the end, imagine each result as its own aggregation, that return value is always an array (either empty or not depending on results).
so when you $project on $rounds.value you're telling mongo to keep the value field for each of the results in the array. in our case its only one but still.
$sum on the other hand is an accumulative operator, from the docs:
With a single expression as its operand, if the expression resolves to an array, $sum traverses into the array to operate on the numerical elements of the array to return a single value.
a quick fix to your 'issue' is just to add $sum while projecting:
{
$project: {
_id: '$rounds._id',
rounds: {$sum: '$rounds.value'},
wins: {$sum: '$wins.value'},
winRatio: { ... },
},
},

Sequelize conditional inclusion of where clause nodejs

I have this code, which has multiple where clause:
Time_Sheet_Details.findAll({
include: [
{
model: timesheetNotesSubcon,
required: false,
attributes:["note","file_name", "id", "working_hrs", "timestamp", "has_screenshot", "notes_category"]
},
{
model: Timesheet,
attributes:["id","leads_id","userid"],
where: {leads_id: filters.leads_id}, // Client
include:[
{
model: Lead_Info, attributes:["id","fname","lname","email","hiring_coordinator_id","status"],
where: {hiring_coordinator_id: {$in: filters.sc_id}}, // SC
include:[{
model: adminInfoSchema,
required: false,
attributes:["admin_id","admin_fname", "admin_lname", "admin_email", "signature_contact_nos", "signature_company"],
}]
},
{
model:Personal_Info,attributes:["userid","fname","lname","email"],
where: {userid: filters.subcon_id}, // Subcon
}
]
}],
where: {
reference_date: filters.reference_date
},
order:[
["id","DESC"]
],
offset:((page-1)*limit),
limit : limit,
subQuery:false
}).then(function(foundObject){
willFulfillDeferred.resolve(foundObject);
});
The where clause is the one with the comment Client, SC and Subcon. However, what is the best approach if those where clause is optional? I am using that for search filter. So if filters.leads_id is null then the where: {leads_id: filters.leads_id}, // Client should not be included in the query. Same with the others. The only solution I can think of is repeat those code blocks for each scenario of not null parameters but that's to repetitive and not practical.
Any other approach or solutions?
If I understand correctly, I think as a first step, you should define your respective where clauses, conditionally upon wether or not each specific search criteria is set:
const clientWhere = filters.leads_id ? {leads_id: filters.leads_id} : {}
const scWhere = filters.sc_id ? {hiring_coordinator_id: {$in: filters.sc_id}} : {}
const subconWhere = filters.subcon_id ? {userid: filters.subcon_id} : {}
So at this point if a search option isn't set, there'll just be an empty object as the where clause.
Next, use those pre-defined where clause objects in your query:
Time_Sheet_Details.findAll({
include: [
{
model: timesheetNotesSubcon,
required: false,
attributes:["note","file_name", "id", "working_hrs", "timestamp", "has_screenshot", "notes_category"]
},
{
model: Timesheet,
attributes:["id","leads_id","userid"],
where: clientWhere, // Client
include:[
{
model: Lead_Info, attributes:["id","fname","lname","email","hiring_coordinator_id","status"],
where: scWhere, // SC
include:[{
model: adminInfoSchema,
required: false,
attributes:["admin_id","admin_fname", "admin_lname", "admin_email", "signature_contact_nos", "signature_company"],
}]
},
{
model:Personal_Info,attributes:["userid","fname","lname","email"],
where: subconWhere, // Subcon
}
]
}],
where: {
reference_date: filters.reference_date
},
order:[
["id","DESC"]
],
offset:((page-1)*limit),
limit : limit,
subQuery:false
}).then(function(foundObject){
willFulfillDeferred.resolve(foundObject);
});

querying on where association in sequelize?

where clause use in sequelize in inner joins.
My query is
SELECT Cou.country_id,cou.country_name, Sta.state_id, Sta.state_name
FROM hp_country Cou
INNER JOIN hp_state Sta ON Cou.country_id = Sta.hp_country_id
WHERE (Cou.country_status=1 AND Sta.state_status=1 AND Cou.country_id=1)
AND (Sta.state_name LIKE '%ta%');
I wrote in sequelize code is
hp_country.findAll({
where: {
'$hp_state.state_status$': 1
},
include: [
{model: hp_state}
]
})
The error it's producing is:
SELECT `hp_country`.`country_id`, `hp_country`.`country_name`, `hp_country`.`country_status`, `hp_country`.`created_date`, `hp_country`.`update_date` FROM `hp_country` AS `hp_country` WHERE `hp_state`.`state_status` = 1;
Unhandled rejection SequelizeDatabaseError: ER_BAD_FIELD_ERROR: Unknown column 'hp_state.state_status' in 'where clause'
Your Sequelize code should look like:
hp_country.findAll({
attributes: ['country_id', 'country_name'],
where: {
country_status: 1,
country_id: 1
},
include: [{
model: hp_state,
attributes: ['state_id', 'state_name'],
where: {
state_status: 1,
state_name: {
$like: '%ta%'
}
}
}]
});
To select only some attributes, you can use the attributes option.
where clause should be moved inside the include statement because the condition you use relates to the hp_state model.
hp_country.findAll({
where: {
//main AND condition
$and: [
//first joint condition
{
$and: [
{ country_status: 1 },
{ country_id: country_id },
Sequelize.literal("hp_states.state_status = 1"),
Sequelize.literal("`hp_states.hp_districts`.`district_status`=1"),
Sequelize.literal("`hp_states.hp_districts.hp_cities`.`city_status`=1"),
Sequelize.literal("`hp_states.hp_districts.hp_cities.hp_locations`.`location_status`=1"),
Sequelize.literal("`hp_states.hp_districts.hp_cities.hp_locations.hp_sub_locations`.`sub_location_status`=1"),
Sequelize.literal("`hp_states.hp_districts.hp_cities`.`city_name` LIKE '%"+city+"%'")
]
},
{
$or: [
Sequelize.literal("`hp_states.hp_districts.hp_cities.hp_locations`.`location_name` LIKE '%"+query+"%'"),
Sequelize.literal("`hp_states.hp_districts.hp_cities.hp_locations.hp_sub_locations`.`sub_location_name` LIKE '%"+query+"%'"),
Sequelize.literal("`hp_states.hp_districts.hp_cities.hp_locations.hp_sub_locations.hp_property`.`property_name` LIKE '%"+query+"%'"),
Sequelize.literal("`hp_states.hp_districts.hp_cities.hp_locations.hp_sub_locations.hp_property.hp_builder`.`builders_name` LIKE '%"+query+"%'")
]
}
]
},
attributes: ['country_id', 'country_name'],
required:true,
include: [
{
model: hp_state,
attributes: ['state_id', 'state_name'],
required:true,
include: [
{
model: hp_district,
attributes: ['district_id', 'district_name'],
required:true,
include: [
{
model: hp_city,
attributes: ['city_id', 'city_name'],
required:true,
include: [
{
model: hp_location,
attributes: ['location_id', 'location_name'],
required:true,
include: [
{
model: hp_sub_location,
attributes: ['sub_location_id', 'sub_location_name'],
required:true,
include: [
{
model: hp_property,
attributes: ['property_id', 'property_name'],
required: true,
include: [
{
model:hp_builders,
attributes: ['builders_id', 'builders_name'],
required: true
}
]
}
]
}]
}]
}]
}
]
}
]
})

Categories

Resources