Use included Models in Where Statements with Sequelize - postgresql
I am using Sequelize 4.38.0, with Postgres. I am trying to implement a search feature. But I am having trouble validating the query. I have to check if the query matches a city. It doesn't have to necessarily match.
I want to move the query to the City Model to the top level WHERE like described here: http://docs.sequelizejs.com/manual/tutorial/models-usage.html#top-level-where-with-eagerly-loaded-models But I get this error: Unhandled rejection SequelizeDatabaseError: missing an entry for the "City" table in the FROM clause
Model Definition
const Property = sequelize.define('property', {
id: {
type: type.STRING,
unique: true,
allowNull: false,
primaryKey: true
},
name: {
type: type.STRING,
allowNull: false
},
// ...
isVisible: {
type: type.BOOLEAN,
default: false
}
});
const City = sequelize.define('city', {
name: {
type: type.STRING
},
}, {
timestamps: false,
});
The Query
sequelize.sync().then(() => {
let query = 'new';
let whereStatement = {
$or: [{
name: {
$ilike: '%' + query + '%'
}
},
{
description: {
$ilike: '%' + query + '%'
}
},
// I get the Error if I uncomment this
// {
// '$City.name$': {
// $ilike: '%' + query + '%'
// }
// }
]
};
Property.findAndCountAll({
where: whereStatement,
include: [{
model: City,
where: {
name: {
$ilike: '%' + query + '%'
}
}
// I want to move this where to the upper level
// where inside the OR opertator
},
{
model: Photo
}
],
distinct: true,
order: [
['createdAt', 'DESC']
]
}).then(properties => {
console.log(properties.count)
});
});
The Actual Query has a bit more attributes. The resultant SQL is:
Executing (default): SELECT "property".*, "city"."id" AS "city.id", "city"."name" AS "city.name", "city"."provinceId" AS "city.provinceId", "photos"."id" AS "photos.id", "photos"."location" AS "photos.location", "photos"."slug" AS "photos.slug", "photos"."description" AS "photos.description", "photos"."isPrimary" AS "photos.isPrimary", "photos"."createdAt" AS "photos.createdAt", "photos"."updatedAt" AS "photos.updatedAt", "photos"."propertyId" AS "photos.propertyId" FROM (SELECT "property"."id", "property"."webSlug", "property"."name", "property"."description", "property"."direction", "property"."price", "property"."areaConstruction", "property"."areaLot", "property"."isVisible", "property"."isAvailable", "property"."isNegociable", "property"."isRural", "property"."latitude", "property"."longitude", "property"."map_radio", "property"."video_url", "property"."createdAt", "property"."updatedAt", "property"."cityId", "property"."propertyTypeId" FROM "properties" AS "property" WHERE ("property"."name" ILIKE '%My Query%' OR "property"."description" ILIKE '%My Query%' OR "property"."id" ILIKE '%My Query%' OR "properties_city"."name" ILIKE '%My Query%') AND "property"."isVisible" = true ORDER BY "property"."createdAt" DESC LIMIT 10 OFFSET 0) AS "property" LEFT OUTER JOIN "cities" AS "city" ON "property"."cityId" = "city"."id" LEFT OUTER JOIN "photos" AS "photos" ON "property"."id" = "photos"."propertyId" ORDER BY "property"."createdAt" DESC;
Update
If I use the same Models and same whereStatement but i change the findAndCountAll() function to a findAll() the search feature works propertly and I can add the '$city.name$': { $ilike: '%' + query + '%' } to the upper WHERE.
Property.findAll({
where: whereStatement,
include: [{model: City}, {model: Photo}],
distinct: true,
order: [['createdAt', 'DESC']],
}).then(properties => {
res.render('search', {
title: 'Buscar',
properties: properties,
query: query,
propertyTypeId: propertyTypeId,
propertyTypes: req.propertyTypes,
})
})
The SQL output:
Executing (default): SELECT "property"."id", "property"."webSlug", "property"."name", "property"."description", "property"."direction", "property"."price", "property"."areaConstruction", "property"."areaLot", "property"."isVisible", "property"."isAvailable", "property"."isNegociable", "property"."isRural", "property"."latitude", "property"."longitude", "property"."map_radio", "property"."video_url", "property"."createdAt", "property"."updatedAt", "property"."cityId", "property"."propertyTypeId", "city"."id" AS "city.id", "city"."name" AS "city.name", "city"."provinceId" AS "city.provinceId", "photos"."id" AS "photos.id", "photos"."location" AS "photos.location", "photos"."slug" AS "photos.slug", "photos"."description" AS "photos.description", "photos"."isPrimary" AS "photos.isPrimary", "photos"."createdAt" AS "photos.createdAt", "photos"."updatedAt" AS "photos.updatedAt", "photos"."propertyId" AS "photos.propertyId" FROM "properties" AS "property" LEFT OUTER JOIN "cities" AS "city" ON "property"."cityId" = "city"."id" LEFT OUTER JOIN "photos" AS "photos" ON "property"."id" = "photos"."propertyId" WHERE ("property"."name" ILIKE '%City_Query%' OR "property"."description" ILIKE '%City_Query%' OR "property"."id" ILIKE '%City_Query%' OR "city"."name" ILIKE '%City_Query%') AND "property"."isVisible" = true ORDER BY "property"."createdAt" DESC;
Try this it should work. include
"duplicating":false
in this
sequelize.sync().then(() => {
let query = 'new';
let whereStatement = {
$or: [{
name: {
$ilike: '%' + query + '%'
}
},
{
description: {
$ilike: '%' + query + '%'
}
},
{
'$City.name$': {
$ilike: '%' + query + '%'
}
}
]
};
Property.findAndCountAll({
where: whereStatement,
include: [{
model: City,
attributes: [],
duplicating:false
},
{
model: Photo
}
],
distinct: true,
order: [
['createdAt', 'DESC']
]
}).then(properties => {
console.log(properties.count)
});
});
Related
How to perform ilike query in array of object jsonb column using sequelize postgres
so i have this data table name: items slug name metadata blue round [ { "value": "sweet", "type": "block", } ] and i want to do something like this where: [{ [Op.or]: [ { slug: { [Op.iLike]: `%${value}%` } }, { name: { [Op.iLike]: `%${value}%` } }, { 'metadata.value': { [Op.iLike]: `%${value}%`}}, ], }] it executing ("items"."metadata"#>>'{value}') ILIKE '%sweet%') no error but give me no result thank you in advance
solved this using literal and like regex in raw SQL literal(`"items"."metadata" ## '$.* like_regex "${value}" flag "i"' `), so my query looks like this where: [{ [Op.or]: [ { slug: { [Op.iLike]: `%${value}%` } }, { name: { [Op.iLike]: `%${value}%` } }, literal(`"items"."metadata" ## '$.* like_regex "${value}" flag "i"' `), ], }]
Sequelize: Returning a single COUNT entry instead of every row in a nested include
I'm trying to grab the total number of rows (count) from a nested include in Sequelize. I've followed countless questions on here, but can't seem to get it right. Models: Brand BrandProfile (alias: brand_profile) BrandPageView (alias brand_page_view) Relationships: // Brand Brand.hasOne(models.BrandProfile, { foreignKey: 'brand_id', as: 'brand_profile' }) // BrandProfile BrandProfile.belongsTo(models.Brand, { foreignKey: 'brand_id', }) BrandProfile.hasMany(models.BrandPageView, { foreignKey: 'brand_id', as: 'brand_page_views' }) // BrandPageView BrandProfile.belongsTo(models.BrandProfile, { foreignKey: 'brand_id', }) Now when I try to normally run my query like so: const { count, rows } = await Brand.findAndCountAll({ include: [ { model: BrandProfile, as: 'brand_profile', include: [ { model: BrandPageView, as: 'brand_page_views', }, ], }, ], }) It returns the following: { id: 1, created_at: '2020-12-26T20:42:19.930Z', updated_at: '2020-12-29T20:46:58.918Z', deleted_at: null, name: 'Test brand', slug: 'test-brand', status: 'disabled', brand_profile: { created_at: '2020-12-26T20:42:19.931Z', about: [ [Object], [Object], [Object], [Object] ], cleaned_about: null, subtitle: 'subtitle test', photo: '1609477287152.jpeg', photo_config: { scale: '1.00' }, id: 1, updated_at: '2021-01-01T05:01:27.414Z', brand_id: 1, brand_page_views: [ [Object], [Object], [Object], [Object], [Object], [Object], [Object] ] }, } As you can see, brand.brand_profile.brand_page_views has a list of all objects. Now I just want to return the count, so I'm using the following query: const { count, rows } = await Brand.findAndCountAll({ include: [ { model: BrandProfile, as: 'brand_profile', include: { model: BrandPageView, as: 'brand_page_views', attributes: [], }, }, ], attributes: { include: [ [ Sequelize.fn('COUNT', Sequelize.col('brand_profile.brand_page_views.brand_id')), 'brand_page_views_count', ], ], }, group: ['brand.id'] }) I get this error: 'missing FROM-clause entry for table "brand"' The SQL it outputs is: SELECT "Brand"."id", "Brand"."created_at", "Brand"."updated_at", "Brand"."deleted_at", "Brand"."name", "Brand"."slug", "Brand"."status", COUNT("brand_profile->brand_page_views"."brand_id") AS "brand_profile.brand_page_views.count", "brand_profile"."created_at" AS "brand_profile.created_at", "brand_profile"."about" AS "brand_profile.about", "brand_profile"."cleaned_about" AS "brand_profile.cleaned_about", "brand_profile"."subtitle" AS "brand_profile.subtitle", "brand_profile"."photo" AS "brand_profile.photo", "brand_profile"."email" AS "brand_profile.email", "brand_profile"."photo_config" AS "brand_profile.photo_config", "brand_profile"."id" AS "brand_profile.id", "brand_profile"."updated_at" AS "brand_profile.updated_at", "brand_profile"."brand_id" AS "brand_profile.brand_id" FROM "brands" AS "Brand" LEFT OUTER JOIN "brand_profile" AS "brand_profile" ON "Br2021-01-05 01:32:26 [sequelize] INFO Executing (default): SELECT "Brand"."id", "Brand"."created_at", "Brand"."updated_at", "Brand"."deleted_at", "Brand"."name", "Brand"."slug", "Brand"."status", COUNT("brand_profile->brand_page_views"."brand_id") AS "brand_profile.brand_page_views.count", "brand_profile"."created_at" AS "brand_profile.created_at", "brand_profile"."about" AS "brand_profile.about", "brand_profile"."cleaned_about" AS "brand_profile.cleaned_about", "brand_profile"."subtitle" AS "brand_profile.subtitle", "brand_profile"."photo" AS "brand_profile.photo", "brand_profile"."email" AS "brand_profile.email", "brand_profile"."photo_config" AS "brand_profile.photo_config", "brand_profile"."id" AS "brand_profile.id", "brand_profile"."updated_at" AS "brand_profile.updated_at", "brand_profile"."brand_id" AS "brand_profile.brand_id" FROM "brands" AS "Brand" LEFT OUTER JOIN "brand_profile" AS "brand_profile" ON "Brand"."id" = "brand_profile"."brand_id" LEFT OUTER JOIN "brand_page_views" AS "brand_profile->braand"."id" = "brand_profile"."brand_id" LEFT OUTER JOIN "brand_page_views" AS "brand_profile->brand_page_views" ON "brand_profile"."id" = "brand_profile->brand_page_views"."brand_id" WHERE "Brand"."id" = 1 GROUP BY "brand"."id"; nd_page_views" ON "brand_profile"."id" = "brand_profile->brand_page_views"."brand_id" WHERE "Brand"."id" = 1 GROUP BY "brand"."id"; I've honestly tried this so many ways, added brand_profile->brand_page_views.brand_id, brand_profile.brand_id to the group, but to no avail.
Sequelize was not designed to support a wide variety of SQL aggregations. So in your case you should use sequelize.literal with a subquery to count child records: attributes: { include: [ [Sequelize.literal('(select COUNT(*) from brand_page_views where brand_page_views.brand_id=brand_profile.id)'), 'brand_page_views_count'], ], }, Don't forget to remove group: ['brand.id']
sequelize-typescript querying if string contains word
I m using Nestjs + sequelize-typescript + postgresql. I would like to select rows in table that content contains specific word. this is my code : async findUsers(searchedWord: string): Promise<users[]> { return await this.USER_REPOSITORY.findAll({ attributes: [ 'id', 'name' ], where: { content: '%'+ searchedWord +'%' }, order: [ [ 'id', 'ASC' ] ] }); } } but this method didn't work ! Any suggestions ?
Return updated values from sequelize raw query
I have an operation that uses a raw query to update the database, i'd like to get the rows affected by the query as the output. Is this possible? const op = (replacements, pgdb) { const query = q(replacements); const args = [query, { replacements, type: Sequelize.QueryTypes.UPDATE, raw: true }]; const results = await pgdb.query(...args); console.log(results); return results; };
Postgresql can return records with RETURNING. UPDATE T SET ... WHERE ... RETURNING *
Yes you can. I'm using Sequelize with a PostgreSQL database. My specs are as follows: "pg": "^7.12.1" "sequelize": "^5.21.2" "sequelize-cli": "^5.5.1" I ran an update query in the following code block: const query = `UPDATE bookings b SET "isPaid" = :isPaid, "paymentType" = :paymentType FROM trips t, requests r WHERE "b"."tripId" = t.id AND r.id = "t"."requestId" AND r.id = :requestId AND "r"."userId" = :userId RETURNING b.id, "isPaid", "paymentType"`; return db.sequelize.query( query, { replacements: { isPaid, paymentType, requestId, userId } } ); I logged the result: console.log('resultFromQuery', resultFromQuery); and got the following: resultFromQuery [ [ { id: 7, isPaid: true, paymentType: 'paypal' }, { id: 8, isPaid: true, paymentType: 'paypal' } ], Result { command: 'UPDATE', rowCount: 2, oid: null, rows: [ [Object], [Object] ], fields: [ [Field], [Field], [Field] ], _parsers: [ [Function: parser], [Function: parser], [Function: parser] ], _types: TypeOverrides { _types: [Object], text: {}, binary: {} }, RowCtor: null, rowAsArray: false } ] I was then able to get the rowCount as follows: console.log('resultFromQuery', resultFromQuery[1].rowCount); I was also able to get the updated rows themselves as follows (you can select the entire row like you did with '*' or specify the fields you want): console.log('resultFromQuery', resultFromQuery[0]);
How to filter objects with subquery?
Usking shiki/kaiseki to interact with the Parse REST api. I'm trying to get all As that do not have a related B for a user. I know I can get Bs for a user: parseClient.getObjects('B', { where: { user: { __type: "Pointer", objectId: "id here", className: "_User" } }, keys: 'a' }, callback) And then pluck object ids to get the bad A ids. aIds = [ Bs ].map(function (b) { return b.a.objectId }) And then query for As negative matching the id. parseClient.getObjects('A', { where: { objectId: { $nin: aIds } } }, callback) But a user can have more B's than the query limit of 200. How can I rewrite it to use a subquery? $dontSelect Nested key param doesn't work, which makes something like this fall flat. parseClient.getObjects('A', { where: { objectId: { $dontSelect: { query: { className: 'B', where: { user query }, }, key: 'a.objectId } } } }, callback) // { code: 105, error: 'invalid field name: ' } $notInQuery There doesn't seem to be a way to put $notInQuery in the root of the where or to reference the current object. parseClient.getObjects('A', { where: { $notInQuery: { className: 'B', where: { user query }, key: 'a' } } }, callback) // { code: 102, error: 'Invalid key $notInQuery for find' } Help!
It is a limitation of the Parse model, and the only way I have found around it is to adjust your schema accordingly. One way you could do it is with a relation on A that contains all related User pointers and then do a $ne against the relation field: where: { 'relationColumn' : { '$ne': { '__type': 'Pointer', 'className': '_User', 'objectId': 'user b id here' } } }