Average of grouped data in column using Sequelize - average

I'm trying to find the average of a 'ratings' column for all the data that has the same 'VenueId'. I have it working with raw code but need to adapt it in Sequelize. The working raw code is
SELECT venueId, AVG(rating) AS average FROM reviews GROUP BY VenueId
The code I've tried is
Review.findAll({
order: [[Sequelize.fn('AVG', Sequelize.col('rating'))]]
}).then(function(Venues) {})
I'm getting an error:
Executing (default): SELECT id, review, rating, createdAt, updatedAt,
VenueId FROM Reviews AS Review ORDER BY max(rating); Unhandled
rejection SequelizeDatabaseError: UNKNOWN_CODE_PLEASE_REPORT:
Expression #1 of ORDER BY contains aggregate function and applies to
the result of a non-aggregated query.
Any ideas?

This might need some tweaking as I don't know your models, but I suggest you try the following:
Review.findAll({
attributes: ['venueId', [models.sequelize.fn('AVG', models.sequelize.col('venue_id')), 'venueIdCount']],
group: 'venue_id'
order: [[models.sequelize.fn('AVG', models.sequelize.col('venue_id')), 'DESC']]
}).then(function() {
//Do something
}})
Important to notice: When I use "venueId" I mean the name of the attribute on your model, and when I use "venue_id" I mean the name of the column in your database table. They might be the same or different, so feel free to tweak it.

You must do this
Review.findAll({
attributes: ['venueId', [models.sequelize.fn('AVG',
models.sequelize.col('rating')), 'ratingAvg']],
group: ['venueId'],
order: [[models.sequelize.fn('AVG', models.sequelize.col('rating')), 'DESC']]
}).then(function() {
//Do something
}})
Or this with async/await
async(() => {
return await(Review.findAll({
attributes: ['venueId', [models.sequelize.fn('AVG',
models.sequelize.col('rating')), 'ratingAvg']],
group: ['venueId'],
order: [[models.sequelize.fn('AVG', models.sequelize.col('rating')), 'DESC']]}))
})

Related

how to sort associated tables in sequelze?

I want to return an associated table, but sort those items. Nothing complicated like sorting by an associated table. Just sort the associated elements.
public static async findWith(finder: any): Promise<Race | null> {
const race = await Race.findOne({
where: finder,
include: [
{
model: Rocket,
order: ['id', 'desc']
}
]
})
return race
}
I tried a few options,
order: ['id', 'desc']
order: [['id', 'desc']]
order: [[Rocket, 'id', 'desc']]. // nested
which are all accepted without error, but the sorting isn't applied.
research:
https://github.com/sequelize/sequelize/issues/4553
https://sequelize.org/docs/v6/core-concepts/assocs/
other question
I'm using sequelize-typescript with v6 and postgres.
It is mentioned in sequelize documentation that if you want to apply ORDER clauses to eager loaded models, you must use the top level order option. Therefore, you should write it outside the include option like this
const race = await Race.findOne({
where: finder,
include: [
{
model: Rocket
}
],
order: [[Rocket, 'id', 'desc']]
})

Mongoose $push to array inside subdocument

I have a companies model which has payment info stored like this:
let paymentSchema = new Schema({
date: Date,
chargeId: String
amount: Number,
receipt_url: String,
currency: String,
type: String
});
paymentInfo: {
lastPayment: {type: paymentSchema},
paymentHistory: [{type: paymentSchema}],
paymentMethod: paymentMethod,
customerId: customerId
}
)};
The paymentInfo is a subdocument and I am using schema there for some validation purposes.
While trying to update the a company's info using the following query, it updates everything except the paymentHistory :
Companies.updateOne(
{_id: companyId},
{
paymentInfo: {
paymentMethod: paymentMethod,
customerId: customerId,
lastPayment : paymentObject,
$push: {paymentHistory: paymentObject }
}
One could say why save the same paymentObject in lastPayment and also push it to paymentHistory - that's required for a feature we need and that's not causing any trouble.
I also tried the following update query and it gives a conflict error - updates in the following code is a valid object and without $push it works fine
{$set: updates, $push: {'paymentInfo.paymentHistory': paymentObject}}
Conflict Error :
{"driver":true,"name":"MongoError","index":0,"code":40,"errmsg":"Updating the path 'paymentInfo' would create a conflict at 'paymentInfo'"}
What do I want?
I want to update the company document in a single database call - looking at that error it seems like I am supposed to make 2 calls, one for $set and other for $push? Is there anything wrong with the first update query??
Possible Solution
One way to solve this is to take out the paymentHistory out of paymentInfo so that it's part of main company schema - that solves the issue but I want to keep all payment related stuff inside paymentInfo.
Any help would be appreciated. Thanks

elemMatch for fields in different levels

I'm asking this question after trying solve this problem all day long.
I want to get my users address by userId and by addressId. Since I receive data from post requests, I need to ensure that the query contains both userId and addressId in order to avoid security problems. The result of the query below returns all addresses from my user, instead of just the address containing the correct addressId.
async getUserAddress(_id: string, addressId: string) {
const user = await this.userModel.findOne(
{
_id,
addresses: {
$elemMatch: {
addressId: Types.ObjectId(addressId)
}
}
}
)
if (!user) {
throw new NotFoundException();
}
return user.addresses[0];
}
Since is impossible to use $elemMatch in different document levels, or even at the top level of the query, I find no better way to make this query without using filters. Are there any insights?
Tks in advance

POSTGRES check if a provided value is present in the column of type array

I am trying to check whether a value if present in the column, which is of type array(column_name text[]).
As a server i am using hapi and trying to check the same from hapi through query.
Query which i have written is(query is not working):
where: { column_name: { $in: { provided_value} }}
The column contains the values as:
{1234, 3456}
The values are of type text.
Can someone please help me to identify the issue.
Thanks in advance.
Sequelize does not support this use of ANY, they seem to only support its use like IN.
Instead, you should try the contains operator (#>):
import {Op} from "sequelize";
MyModel.findAll({
where: {
column_name: {
[Op.contains]: [provided_value]
}
}
});
This will generate something like
SELECT * FROM "MyModel" WHERE column_name #> '{"provided_value"}';

Sort populated record in sails waterline

I created a Sails application with two models Publication and Worksheet. They are having a one-to-one relationship. Sails-postgresql is the adapter I'm using. I'm using waterline orm to fire query to the database. I'm When I am trying to load publications data along with worksheet and then sort the records depending on a field in the Worksheet using sort() I'm getting an error.
My model is:
Publication.js
module.exports = {
attributes: {
id: {
type: 'integer'
unique: true
},
worksheetId: {
type: 'integer',
model : 'worksheet'
},
status: {
type: 'string',
defaultsTo: 'active',
in : ['active', 'disabled'],
}
}
}
Worksheet.js
module.exports = {
attributes: {
id: {
type: 'integer',
unique: true
},
name: 'string',
orderWeight: {
type: 'integer',
defaultsTo: 0
}
}
}
So now I want to load all the publication where status is "active" and populate worksheet in the data.
So I'm executing the query:
Publication.find({
where: {
status: 'active'
}
})
.populate('worksheetId').limit(1)
.exec(function (error, publications) {
})
And I'm getting a data like :
{
id : 1,
status : "active",
worksheetId : {
id : 1
name : "test",
orderWeight : 10
}
}
So till now it's all working fine. Now I want to increase the limit to 10 and want to sort the data depending on "orderWeight" which is in the populated data. Initially I sorted the whole data depending on publication id and the query worked.
Publication.find({
where: {
status: 'active'
}
})
.populate('worksheetId').sort('id ASC').limit(10)
.exec(function (error, publications) {
})
So I fired similar query to sort the data on "orderWeight"
Publication.find({
where: {
status: 'active'
}
})
.populate('worksheetId').sort('worksheetId.orderWeight ASC').limit(10)
.exec(function (error, publications) {
})
And this query is giving me error that worksheetId.orderWeight is not a column on the publication table. So I want to fire this sort query on the populated data not on the publication table.
Please let me know how I can get my expected result.
Apart from sort() method I also want to run some find command to the populated data to get those publication where the worksheet name matches with certain key as well.
Basically, what you're trying to do, is query an association's attribute. This has been in the waterline roadmap since 2014, but it's still not supported, so you'll have to figure out a workaround.
One option is to query the Worksheet model, and populate the Publication, since sails doesn't let you query across models without using raw queries (i.e. .sort('worksheetId.orderWeight ASC') doesn't work). Unfortunately, you might have to move the active flag to the Worksheet. For example:
Worksheet.find({
status: 'active'
})
.populate('publication') // you should also add publication to Worksheet.js
.sort('orderWeight ASC')
.limit(10)
Alternatively, you could combine Worksheet and Publication into one model, since they're one-to-one. Probably not ideal, but sails.js and Waterline make it very difficult to work with relational data - I'd estimate that half of the queries in the project I'm working on are raw queries due to sails' poor support of postgres. The framework is pretty biased towards using MongoDB, although it claims to "just work" with any of the "supported" DBs.