Sequelize getters do not run on model.findall( { raw:true } ) - javascript

I have a photo attribute in one of my Sequelize models. I have a getter for it.
photo: {
type: Sequelize.STRING,
get: function () {
if (this.getDataValue("photo"))
return process.env.IMAGE_PREFIX + this.getDataValue("photo")
else
return this.getDataValue("photo")
}
},
but when i want to findall records in this model with ({raw:true}) getter doesn't work.
const categories = await Category.findAll({
limit: parseInt(limit),
offset: parseInt(offset),
raw: true,
where: {
title: { [Op.like]: '%' + searchString + '%' }
}
});
if i use ({plain:true}) instead , just finds one record.

Due to newer version of sequelize as documentation itself says:
Getters wont run with instance.get({ raw: true }), use instance.get({ plain: true })
You can read more about it here

Related

Find documents with property containing dynamic keys

I'm having some doubts to filter data of some documents that haves a mixed field, I've noticed mongoose takes mixed fields not as a object, but as a array of 'any' data, or something like that, I'm on a research yet, well, my schema looks like the example below:
export const ProductSchema = createSchema({
active: Type.boolean({ default: true }),
available: Type.boolean({ default: true }),
balance: Type.number({ default: 0 }),
typeMerc: Type.mixed({ default: {} })
}, { timestamps: true });
For example, I need to find documents that my typeMerc object contains the 'ad0a' key (inside typeMerc), my documents look like this:
{
_id: ObjectId('6115b8e219f684ac15823')
active: true
available: true
balance: 12
typeMerc: {
ad0a: {
props: "faa0e"
data: "anyString"
},
f0ea: {
props: "fa45d",
data: "anyString"
}
}
}
If i try on compass the code below, I get the data correctly:
{ 'typeMerc.ad0a': { $exists: true }}
Then, on my code I've tried something like:
const searchId = 'ad0a'
Product.find({ `typeMerc.${searchId}`: { $exists: true } })
Product.find({ `typeMerc.$.${searchId}`: { $exists: true } })
Product.find({ typeMerc[`${searchId}`]: { $exists: true } })
Product.find({ typeMerc[searchId]: { $exists: true } })
But, without success.
You have to put the string inside square brackets.
const searchId = 'ad0a'
Product.find({ [`typeMerc.${searchId}`]: { $exists: true } })

How to use Sequelize associations with .then in controller

This is my ToWatch Model in toWatch-model.js file which in code has UserModel->ToWatch 1:1 relationship and has ToWatch->MovieModel 1:M relationship.
const Sequelize = require('sequelize');
const sequelize = require('./../common/db-config');
const MovieModel = require ("./movie-model");
const UserModel = require("./user-model");
const Model = Sequelize.Model;
class ToWatch extends Model{}
ToWatch.init({
id: {
type: Sequelize.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true,
field: 'id_towatch'
},
date: {
type: Sequelize.DATE,
allowNull: false,
field: 'date'
},
userId: {
type: Sequelize.INTEGER,
allowNull: false,
field: 'id_user',
references:{
model: UserModel,
key: "id"
}
},
movieId: {
type: Sequelize.INTEGER,
allowNull: false,
field: 'movie_id_towatch',
references:{
model: MovieModel,
key: "id"
}
},
},
{
sequelize,
modelName: 'towatch',
tableName: 'towatch',
timestamps: false
// options
});
//Here is the relation ************
UserModel.hasOne(ToWatch, {
foreignKey: {
type:Sequelize.DataTypes.INTEGER,
allowNull:false,
name:'fk_foreign_key_towatch'
}
});
ToWatch.belongsTo(UserModel);
ToWatch.hasMany(MovieModel, {
foreignKey: {
type:Sequelize.DataTypes.INTEGER,
allowNull:false,
name:'movie_id_towatch'
}
});
MovieModel.belongsTo(ToWatch);
module.exports = ToWatch;
I watched many tutorials, but being my first time trying to make a method that will return everything including something from my other table via ID, I wasn't sure where to put and how to put data that I need in this method, considering it has .then(data=>res.send). Tutorials were doing it other ways by fetching or using async-await, and even documentation didn't help me here. Can somebody tell me what to put and where inside this method, that is inside toWatch-controller.js file for me being able to see let's say all the movie data (title,img,date) ,as an array I think, of the getToWatch method.
const ToWatch = require('./../models/toWatch-model');
module.exports.getToWatch = (req,res) => {
ToWatch.findAll().then(toWatch => {
[WHAT DO I PUT HERE?]
res.send(toWatch);
}).catch(err => {
res.send({
status: -1,
error:err
})
})
}
I need something like this ToWatch{
color:red,
cinema:"MoviePlace",
movieId{title:"Anabel", img:"URL", date:1999.02.23}
As I understood your question right, what you trying to do is return toWatch model instances with including User and Movie models.
To do so you can pass options in findAll() method like below:
ToWatch.findAll({include: [{model: User}, {model: Movie}]})... //and rest of your code
Or alternatively to keep your code clean you can use scopes:
// below the toWatch-model.js file
ToWatch.addScope('userIncluded', {include: {model: User}})
ToWatch.addScope('movieIncluded', {include: {model: Movie}})
And in the getToWatch method:
// you can pass several scopes inside scopes method, its usefull when you want to combine query options
ToWatch.scopes('userIncluded', 'movieIncluded').findAll()... //rest of your code
For more information check out sequelize docs

Sequelize findOrCreate(...).spread is not a function

I'm using the sequelize 6. When I'm runing findOrCreate().spread it says "findOrCreate(...).spread is not a function". Here is my code:
const response = await Response.findOrCreate({
where: {
participantId,
questionId,
},
defaults: responseNewDetail,
})
return res.status(200).send({ status: 0, data: response })
This is working fine, but it does not seperate the created status and the model value.
When I'm trying to use spread:
Response.findOrCreate({
where: {
participantId,
questionId,
},
defaults: responseNewDetail,
}).spread(function(response,created){
return res.status(200).send({ status: 0, data: response })
})
It says "Response.findOrCreate(...).spread is not a function".
This is the model file(response.js):
const { Sequelize } = require("sequelize")
module.exports = (sequelize, DataTypes) =>
sequelize.define(
"Response",
{
responseId: {
type: DataTypes.INTEGER,
primaryKey: true,
allowNull: false,
field: "Response_ID",
autoIncrement: true,
},
companyId: {
type: DataTypes.INTEGER,
allowNull: false,
field: "Company_ID",
},
...
)
Response model:
const ResponseModel = require("../models/response")
const Response = ResponseModel(sequelize, DataTypes)
Does anyone know what's wrong?
Since you're using await, you can change:
const response = await Response.findOrCreate({
where: {
participantId,
questionId,
},
defaults: responseNewDetail,
})
return res.status(200).send({ status: 0, data: response })
to
const [ response, created ] = await Response.findOrCreate({
where: {
participantId,
questionId,
},
defaults: responseNewDetail,
})
return res.status(200).send({ status: 0, data: response })
and all will be well.
This doesn't address the spread not a function thing though. For that, I only noticed this when I upgraded sequelize from an older version (I've been using sequelize since version 1). Your example of spread really should be working but for whatever reason it does not anymore (it doesn't for me either). Typically the only time I'm using spread instead of just awaiting it is when I want to defer execution of something. Right or wrong, I've begun to tackle that by just wrapping the await/async version in:
setImmediate(async () => {
// async things here
});
Hope I helped, sorry if I didn't.
eleborating Brian's Answer :-
findOrCreate returns two values first model instance and second created status,
so you can use
const [model,created] = ModelName.findOrCreate();
(Model Response in your case)
to get created status.

Sequelize.js query to get total count through relationship

I'm using sequelize to get a total count through a relationship. I need it by a customerId that is in a parent table joined through a pivot table. The plain query looks something like this:
SELECT count(p.*) FROM parcels as p
LEFT JOIN orders_parcels as op ON op."parcelId" = p.id
LEFT JOIN orders as o ON op."orderId" = o.id
WHERE o."customerId"=1
This works fine. But not sure how to get the sequelize query.
Parcel.findAndCountAll();
EDIT: OrderParcel
var OrderParcel = service.sequelize.define('OrderParcel', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
}
}, {
tableName: 'orders_parcels',
freezeTableName: true,
paranoid: true
});
module.exports = OrderParcel;
var Order = require('./Order');
OrderParcel.belongsTo(Order, {
as: 'Order',
foreignKey: 'orderId'
});
var Parcel = require('../parcel/Parcel');
OrderParcel.belongsTo(Parcel, {
as: 'Parcel',
foreignKey: 'parcelId'
});
One way is to use sequelize.query:
As there are often use cases in which it is just easier to execute raw
/ already prepared SQL queries, you can utilize the function
sequelize.query.
var query = "SELECT count(p.*) FROM parcels as p" +
" LEFT JOIN orders_parcels as op ON op."parcelId" = p.id" +
" LEFT JOIN orders as o ON op."orderId" = o.id" +
" WHERE o.customerId=1;";
sequelize.query(query, { type: sequelize.QueryTypes.SELECT}).success(function(count){
console.log(count); // It's show the result of query
res.end();
}).catch(function(error){
res.send('server-error', {error: error});
});
Raw Queries docs
Assuming that you've defined the associations, you can use Model.findAndCountAll. It'd look something like this:
Parcel.findAndCountAll({
include: [{
model: OrderParcel,
required: true,
include: [{
model: Order,
where: {
customerId: idNum
}
}]
}]
}).then(function(result) {
});
I totally agree with Evan Siroky's approach yet the code has to be simplified to work properly:
Parcel.findAndCountAll({
include: [{
model: Order,
where: {
customerId: idNum
},
duplicating: false // Add this line for retrieving all objects
}]
}).then(function(result) {
console.log('Rows: ' + result.rows + ' Count: ' + result.count)
});
Remember to connect your models with belongsToMany method!

How do I select a column using an alias

How do I perform an sql query such as this?
SELECT column_name AS alias_name FROM table_name;
Example: I want the column 'first' to be selected as 'firstname'
Table.findAll({
attributes: [id,"first"]
})
.then(function(posts) {
res.json(posts);
})
Table.findAll({
attributes: ['id', ['first', 'firstName']] //id, first AS firstName
})
.then(function(posts) {
res.json(posts);
});
You need to use row.get('newname') to access columns aliased by attributes
Doing just row.newname, or row.oldname, will not work like it does for non-aliased names for some reason:
https://github.com/sequelize/sequelize/issues/10592
https://sequelize.org/v5/manual/models-usage.html documents it:
Project.findOne({
where: {title: 'aProject'},
attributes: ['id', ['name', 'title']]
}).then(project => {
// project will be the first entry of the Projects table with the title
// 'aProject' || null
// project.get('title') will contain the name of the project
})
but https://sequelize.org/master/class/lib/model.js~Model.html doesn't mention it, which is confusing:
instance.field
// is the same as
instance.get('field')
Related: Sequelize cannot access alias. Alias is undefined
Minimal runnable example:
const assert = require('assert');
const path = require('path');
const { Sequelize, DataTypes, Op } = require('sequelize');
const sequelize = new Sequelize({
dialect: 'sqlite',
storage: path.basename(__filename) + '.sqlite',
});
(async () => {
const Int = sequelize.define('Int', {
value: {
type: DataTypes.INTEGER,
},
name: {
type: DataTypes.STRING,
},
}, {});
await Int.sync({force: true})
await Int.create({value: 2, name: 'two'});
let row;
row = await Int.findOne({
where: { value: 2 },
attributes: [ 'id', [ 'value', 'newvalue' ] ],
});
assert.strictEqual(row.id, 1);
assert.strictEqual(row.value, undefined);
assert.strictEqual(row.newvalue, undefined);
assert.strictEqual(row.get('newvalue'), 2);
await sequelize.close();
})();
The generated query does exactly what we wanted then:
SELECT `id`, `value` AS `newvalue` FROM `Ints` AS `Int`
WHERE `Int`.`value` = 2 LIMIT 1;
tested on sequelize 6.5.1, sqlite3 5.0.2.
Also Sequelize supports defining column names directly on the model definition too.
Sequelize Docs On that they mention about field attribute on column definition.
ex: (Taken from Docs itself)
const { Model, DataTypes, Deferrable } = require("sequelize");
class Foo extends Model { }
Foo.init({
// You can specify a custom column name via the 'field' attribute:
fieldWithUnderscores: {
type: DataTypes.STRING,
field: 'field_with_underscores'
},
}, {
sequelize,
modelName: 'foo'
});
thanks to this answer

Categories

Resources