Optional query across relation - node.js

I have two tables to handle coupon codes in my application.
The first (coupon) stores coupon codes, along with the discount they provide:
| id | code | public | discount_percent |
|----|------------|--------|------------------|
| 1 | EXAMPLE | true | 10 |
| 2 | PRIV_12981 | false | 30 |
Public indicates whether the coupon code can be claimed by anyone.
The second (user_coupon) table stores users entitled to redeem private coupons, and whether their instance of the coupon has been redeemed:
| id | coupon_id | user_id | redeemed |
|----|-----------|---------|----------|
| 1 | 2 | 1 | false |
I need to make the following query (pseudo code):
SELECT * from coupons
WHERE (
code = 'PRIV_12981'
AND (
public = true
OR (
user_coupon.user_id = 1
AND user_coupon.redeemed = false
)
)
)
This query should check whether a coupon exists for the provided code that is either public, or has a corresponding entry in the user_coupon table for the provided user_id and has not already been redeemed.
I'm using Sequelize, and have written the following query:
Coupon.findOne({
where: {
[Sequelize.Op.and]: [
{
code: 'TEST'
},
{
[Sequelize.Op.or]: [
{
public: true
},
{
[Sequelize.Op.and]: [
Sequelize.where(
Sequelize.col('user_coupons.redeemed'), false
),
Sequelize.where(
Sequelize.col('user_coupons.user_id'), 1
)
]
}
]
}
]
},
include: [{
model: UserCoupon,
as: 'user_coupons',
}]
}).then(coupon => {
console.log(coupon)
})
This generates the following SQL:
SELECT "Coupon".*,
"user_coupons"."id" AS "user_coupons.id",
"user_coupons"."coupon_id" AS "user_coupons.coupon_id",
"user_coupons"."user_id" AS "user_coupons.user_id",
"user_coupons"."redeemed" AS "user_coupons.redeemed",
"user_coupons"."created_at" AS "user_coupons.created_at",
"user_coupons"."updated_at" AS "user_coupons.updated_at"
FROM
(SELECT "Coupon"."id",
"Coupon"."code",
"Coupon"."public",
"Coupon"."discount_percent",
"Coupon"."max_discount",
"Coupon"."valid_from",
"Coupon"."invalid_after",
"Coupon"."created_at",
"Coupon"."updated_at"
FROM "public"."coupon" AS "Coupon"
WHERE ("Coupon"."code" = 'TEST'
AND ("Coupon"."public" = TRUE
OR ("user_coupons"."redeemed" = FALSE
AND "user_coupons"."user_id" = 1)))
LIMIT 1) AS "Coupon"
LEFT OUTER JOIN "public"."user_coupon" AS "user_coupons" ON "Coupon"."id" = "user_coupons"."coupon_id";
However, it throws the following error:
missing FROM-clause entry for table "user_coupons"
What have I done wrong with the Sequelize query?

I discovered the error wasn't actually in my query structure, it was due to my use of findOne, which was adding LIMIT 1 to the query, which resulted in the error. Querying with Coupon.findAll fixed this,

Related

Swapping rows for a column that has unique constraint

I'm using postgresql and sequelize to do my db operations
Suppose I have a person table like this:
id (pkey) | name | email (unique) | company
1 | john | john#x.com | abc
2 | mary | mary#y.com | abc
3 | will | NULL | xyz
I make the unique constraint like this:
const PersonData = sequelize.define(
'users',
{
....
email: {
type: Sequelize.STRING,
allowNull: false,
unique: {
args: true,
msg: 'Email already in use!',
},
},
....
},
I'm getting payload like
{{
id: 1,
email: mary#y.com
},
{
id: 2,
email: john#x.com
}}
I want to swap the emails for id 1 and id 2 here. How can I do that using sequelize?
The output I want:
id (pkey) | name | email (unique) | company
1 | john | mary#y.com | abc
2 | mary | john#x.com | abc
3 | will | NULL | xyz

<h5>json sorted ascending order by value based</h5>

Here the Json data sorted:
{ designer : 5, tester :8,developer : 10, backend :7 }
I would like to the following sorted order
{ developer :10, tester :8, backend:7, designer :5 }
In order to sort JSON objects, we have to take help of Arrays. Arrays maintain an order of insertion. Moreover, it provides native sort() method to sort array elements.
Following sortByKey() function takes JSON Object as an input and returns JSON Array which is sorted by key.
var data = [
{ designer : 5, tester :8,developer : 10, backend :7 }
]
function sortByValue(jsObj){
var sortedArray = [];
for(var i in jsObj)
{
sortedArray.push([i,jsObj[i]]);
}
sortedArray.sort(function(k,v){ return k[1] - v[1]});
sortedArray.reverse();
}
var jsObj = {};
jsObj.designer = 5
jsObj.tester = 8
jsObj.developer = 10
jsObj.backend = 7
var sortedbyValueJSONArray = sortByValue(jsObj);
console.table(sortedbyValueJSONArray);
We will create JSON Array of [jsonValue, jsonKey] from JSON Object. Then, we will sort JSON Array using sort() function. Checkout folllwing example.
------------------------------
| 0 | 10 | 'developer' |
| 1 | 5 | 'designer' |
| 2 | 7 | 'backend' |
| 3 | 8 | 'tester' |
If the order of the data is relevant for your case, you should use an array, not an object.
e.g.
[
{ "name": "developer", "value": 10 },
{ "name": "tester", "value": 8 },
{ "name": "backend", "value": 7},
{ "name": "designer", "value": 5 }
]
Ordering the elements of the array is as simple as JSON.parse(data).sort(({value: a}, {value: b}) => b - a) (in JavaScript, ES6).

Sequelize with SQL Server returns error aggregate function or GROUP BY clause even I didn't used GROUP BY

I'm built a app's backend with ExpressJs with Sequelize and SQL Server.
I've two tables, called User and Order. A user hasMany order.
I wanna list all user include their order(as count).
Here is my code:
User.findAll({
attributes: ['id'],
include: [
{
model: ProductBuy,
attributes: [
[Sequelize.fn('COUNT', Sequelize.col('Order.member_id')), 'total_active']
],
where: {status: ['Active', 'Expired']}
}
],
})
but it give me an error
Column 'User.id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I don't understand this, because I haven't used a group by yet.
But if I add a grouping like
group:['Order.member_id', 'User.id', 'Order.id']
It successfully gets the data, but the order isn't counted, it showed each order one by one in each user like this:
{
"id": 4,
"orders": [
{
"total_active": 1
},
{
"total_active": 1
}
]
},
What I want is to say User A has 4 total_active, user B has 6 total_active like that
How do I achieve this?
Table Structure
User
| id | name |
| 1 | Andrew |
| 2 | Bruce |
Order
| id | user_id | status |
| 1 | 1 | Active |
| 2 | 1 | Expired |
| 3 | 2 | Active |
Expected result
{
"data": [
{
"id": 1,
"Orders": [
{
"total_active": 2
}
]
},
{
"id": 2,
"Orders": [
{
"total_active": 1
}
]
},
]
}
Screenshot result
I think you can takeout the attribute field from ProductBuy and use it like this :
User.findAll({
attributes: [
'id'
[Sequelize.fn('COUNT', Sequelize.col('Order.member_id')), 'total_active'] // <--- HERE
],
include: [
{
model: ProductBuy,
where: {status: ['Active', 'Expired']}
}
],
group:['User.id', 'Order.id']
})
NOTE :
If its counting duplicate records you can also use DISTINCT like
this
[Sequelize.fn('COUNT', Sequelize.fn('DISTINCT',Sequelize.col('Order.member_id'))), 'total_active']
if you use COUNT with another fields you have to use GROUP BY
SELECT user_id, COUNT(*)
FROM [Order]
GROUP BY user_id

how to using a db function in where sentence via sequelize

I want to find an average value of this week and group by dayofweek, so I writen this sql, the sql is work well, but how can I achieve the same purpose via sequelize.
select dayofweek(datetime), avg(value) from Temperatures where week(datetime) = week(now()) group by dayofweek(datetime);
And here is my Temperatures table:
| field | type |
|----------|-------------|
| uuid | char(36) |
| value | float |
| datetime | datetime |
I've read the document of sequelize, but it seems that there is no clear explanation of how to using a function after where sentence, the condition is a function but not a field.
I have tried under way, but it's not work
where: {
[Sequelize.Op.eq]: [
sequelize.fn('week', Temperature.sequelize.fn('now')),
sequelize.fn('week', Temperature.sequelize.col('datetime')),
]
},
Thank your for your help!
You can use sequelize.fn for that , something like this :
[sequelize.fn('dayofweek', sequelize.col('datetime')), 'datetime']
For more details DO READ
Finally, I found the following method works well
return Temperature.findAll({
attributes: [
[Temperature.sequelize.fn('dayofweek', Temperature.sequelize.col('datetime')), 'dayofweek'],
[Temperature.sequelize.fn('AVG', Temperature.sequelize.col('value')), 'value'],
],
where: sequelize.where(sequelize.fn('week', Temperature.sequelize.fn('now')), sequelize.fn('week', Temperature.sequelize.col('datetime'))),
group: sequelize.fn('dayofweek', Temperature.sequelize.col('datetime')),
});
and if I need more conditional filters, just add an [Op.and] operator
return Temperature.findAll({
attributes: [
[Temperature.sequelize.fn('dayofweek', Temperature.sequelize.col('datetime')), 'dayofweek'],
[Temperature.sequelize.fn('AVG', Temperature.sequelize.col('value')), 'value'],
],
where: {
[Sequelize.Op.and]: [
sequelize.where(sequelize.fn('week', Temperature.sequelize.fn('now')), sequelize.fn('week', Temperature.sequelize.col('datetime'))),
]
},
group: sequelize.fn('dayofweek', Temperature.sequelize.col('datetime')),
});
here is the sql generated
SELECT dayofweek(`datetime`) AS `dayofweek`, AVG(`value`) AS `value` FROM `Temperatures` AS `Temperature` WHERE (week(now())=week(`datetime`)) GROUP BY dayofweek(`datetime`);

How to Looping inside Model.find Sailsjs

Good day all, can someone suggest me which async function should i use?,
in my case, I need an output the 1st member of each group.
*** note this is just a example of the flow of my program.
/*
Grouplist groupmember
Id | name id | name | group
1 | group1 1 | John | 1
2 | group2 2 | James | 1
3 | group3 3 | Paul | 3
4 | Angel | 2
5 | Anne | 2
6 | Jane | 3
looking for output like this
id | name | group
1 | John | 1
4 | Angel | 2
3 | Paul | 3
*/
var name = [];
Grouplist.find( function(err, grouplist){
for(var x=0;x<=grouplist.length-1; x++){
groupmember.find({id:grouplist[x].id}).limit(1).exec(
function callBack(err,results){
if(err) console.log(err);
name[x] = results.name;
})
}
})
You can get the result in one query using mongodb aggregate operators:
groupmember.aggregate(
[
{
$group:
{
_id: "$group", //grouping key
member: { $first: "$$CURRENT" } //return the first matched document
}
},
{
$project : //project the document to top-level
{
_id: "$member._id",
name: "$member.name",
group: "$member.group"
}
}
],
function(err, members){
console.log(members)
}
)

Resources