Sequelize mysql order by concat of two fields - express

My DB has a users table that has a first_name,last_name columns, either of them maybe empty.
I also have a UI dashboard that displays a table with a full_name column that is a concatenation of the two fields, this column is sortable.
I am using sequelize with a mysql database to fetch the users, how can I order by full_name (And trim the spaces of the concatenated field)

The SQL solution
You can order by multiple columns
SELECT * FROM user ORDER BY last_name ASC, first_name ASC
OR you first concatenate the two fields and order by full_name (but keep in mind this might be slow)
SELECT *, TRIM(CONCAT(first_name,' ', last_name)) full_name FROM user ORDER BY full_name ASC
The Sequelize solution
const { Sequelize, DataTypes } = require('sequelize')
const User = sequelize.define('User', {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true
},
first_name: {
type: DataTypes.STRING(100)
},
last_name: {
type: DataTypes.STRING(100)
},
//virtual attribute for concatenation of first_name and last_name
full_name: {
type: DataTypes.VIRTUAL,
get() {
const firstName = this.getDataValue('first_name');
const lastName = this.getDataValue('last_name');
return [firstName, lastName].join(' ').trim();
}
},
});
User.findAll({
/** Order by multiples columns **/
//order: [['first_name', 'DESC'], ['last_name', 'ASC']],
/** Order by the two fields concatenated and trimed */
order: Sequelize.literal("trim(concat(first_name,' ', last_name)) ASC")
}).then(users => {
users.forEach(user=> {
console.log(user.get());
})
})

Related

Typeorm - Converting SQL Left Join one to one relation into typeorm query builder

I have following entities in a postgresql database using Typeorm:
#Entity('issuer')
export class Issuer {
#PrimaryColumn()
issuer_id: string
#OneToOne(() => UserData, { cascade: true })
#JoinColumn({ name: 'issuer_id', referencedColumnName: 'public_address' })
u_data: UserData
#BeforeInsert()
newid() {
this.issuer_id = this.u_data.public_address
}
...remaining columns...
}
#Entity('user_data')
export class UserData {
#PrimaryColumn({ type: 'varchar', unique: true })
email: string
#Column({ type: 'varchar', nullable: false, unique: true })
public_address: string
...remaining columns...
}
Above in the Issuer entity, I am doing a small trick to be able to make a key both primary and foreign, issuer_id column, which is primary key of Issuer and foreign key of UserData which refers to public_address column of UserData. I wanna join both entities, and I am able to do it with the following query:
SELECT *
FROM user_data
LEFT OUTER JOIN issuer ON issuer.issuer_id = user_data.public_address
WHERE user_data.email = $1
I am unable to convert this simple SQL code into Typeorm query builder. Here is my failed attempt:
await this.userRepository
.createQueryBuilder('user')
.leftJoin('user.public_address', 'issuer')
.where('user.email = :email', { email })
.getOne()
Here is the error I am getting:
TypeORMError: Relation with property path public_address in entity was not found.
It seems when trying to left join (right join doesn't exist on typeorm) from an entity that has no direct relation to its relative, leftJoinAndSelect function should be used with a condition:
return await this.userRepo
.createQueryBuilder('user')
.leftJoinAndSelect(Issuer, 'issuer', 'user.public_address = issuer.issuer_id')
.where('user.email = :email', { email })
.getRawOne()

TypeORM The results of the conditional query were not match

I set the where condition in the find statement, but the results of the conditional query where not match.
The find query:
async getRegister() {
const result = await this.registerModel.findOne({
where: {
session: 1,
user: 2,
},
select: ['id', 'created_time'],
});
return result;
}
The register entity:
#Entity('register')
export class Register {
#PrimaryGeneratedColumn()
id: number;
#Column({
type: 'text',
nullable: true,
unique: true,
})
no: string;
……
#ManyToOne(type => User, user => user.registers)
user: User;
#OneToOne(type => Session)
#JoinColumn()
session: Session;
}
The formatted SQL statement:
query: SELECT DISTINCT "distinctAlias"."Register_id" AS "ids_Register_id" FROM (SELECT "Register"."id" AS "Register_id", "Register"."created_time" AS "Register_created_time" FROM "register" "Register" INNER JOIN "session" "Register__Register_session" ON "Register__Register_session"."id"="Register"."sessionId" AND ("Register__Register_session"."deleted_time" IS NULL) INNER JOIN "user" "Register__Register_user" ON "Register__Register_user"."id"="Register"."userId" AND ("Register__Register_user"."deleted_time" IS NULL) WHERE "Register"."deleted_time" IS NULL) "distinctAlias" ORDER BY "Register_id" ASC LIMIT 1
query: SELECT "Register"."id" AS "Register_id", "Register"."created_time" AS "Register_created_time" FROM "register" "Register" INNER JOIN "session" "Register__Register_session" ON "Register__Register_session"."id"="Register"."sessionId" AND ("Register__Register_session"."deleted_time" IS NULL) INNER JOIN "user" "Register__Register_user" ON "Register__Register_user"."id"="Register"."userId" AND ("Register__Register_user"."deleted_time" IS NULL) WHERE ( "Register"."deleted_time" IS NULL ) AND ( "Register"."id" IN (7) )
I could not find the parameter set
where: {
session: 1,
user: 2,
},
When I use sql, I get the right results.
await this.registerModel.query(
`select id, created_time from register where userId = ${userId} and sessionId = ${sessionId}`
);
I have fixed this issuse.
First modified the registered entity
#Entity('register')
export class Register {
#PrimaryGeneratedColumn()
id: number;
#Column({
type: 'text',
nullable: true,
unique: true,
comment: '准考证号',
})
no: string;
……
#ManyToOne(type => User, user => user.id)
user: User;
#ManyToOne(type => Session, session => session.id)
session: Session;
}
Last modified the find query:
const result = await this.registerModel.findOne({
where: {
user: {
id: userId,
},
session: {
id: sessionId,
},
},
select: ['id', 'created_time'],
});
return result;

Prisma how can I update only some of the models fields in update()

I have a Prisma model with lets say 10 fields.
Think User model with firstname, lastname, address, e-mail , phone, mobile, age etc.
I am trying to write a update method for this, where I most of the times only want to update some or only 1 of the fields. Not the whole User. If the field is not sent with the request, I want to keep the value from the db.
What would the best practice be for this. Should I check for all fields to be in the req object?
How could I write this for prisma?
Example on how I would like it to work:
req = {firstname: 'Bob', email: 'bob#bob.bob', etc}
const updateUser = await prisma.user.update({
where: {
email: 'viola#prisma.io',
},
data: {
req.firstname ? (email: req.firstname) : null,
req.email ? (email: req.email) : null,
req.address? (email: req.address) : null,
},
})
Or should I check for values to be present in req and build the data object in 10 versions:
let customDataObject = {}
if (req.firstname) {
customDataObject.firstname = req.firstname
}
if (req.email) {
customDataObject.email= req.email
}
const updateUser = await prisma.user.update({
where: {
email: 'viola#prisma.io',
},
data: customDataObject,
})
The undefined property is used in Prisma to do exactly what you're trying to achieve. Basically, when a field is assigned undefined it means ignore this and do nothing for this field. You can learn more about this in the article about null and undefined in the docs.
This is what your update query should look like.
// assuming email, firstname and address fields exist in your prisma schema.
const updateUser = await prisma.user.update({
where: {
email: 'viola#prisma.io',
},
data: {
firstname: req.firstname || undefined, // if req.firstname is falsy, then return undefined, otherwise return it's value.
email: req.email || undefined,
address: req.address || undefined
},
})

count with where query to code by use sequelize

i want change this query to code by use sequelize and node.js depend on where
select COUNT(gender) as genderCount from customers where gender = "male"
but i don't know how to add where
this original code:
Customer.findAll({
attributes: {
include: [[Sequelize.fn("COUNT", Sequelize.col("gender")), "genderCount"]] }
}).then(data => {
res.send(data)
})
For "sequelize": "^5.21.3":
Option 1.
const customers = await Customer.findAll({
attributes: [[Sequelize.fn('COUNT', Sequelize.col('gender')), 'genderCount']],
where: {
gender: 'male',
},
raw: true,
});
The execution result:
Executing (default): SELECT COUNT("gender") AS "genderCount" FROM "customers" AS "Customer" WHERE "Customer"."gender" = 'male';
[ { genderCount: '3' } ]
Option 2. You can use Model.count().
Count the number of records matching the provided where clause.
const customers = await Customer.count({ col: 'gender', where: { gender: 'male' } });
The execution result:
Executing (default): SELECT count("gender") AS "count" FROM "customers" AS "Customer" WHERE "Customer"."gender" = 'male';
The above examples base on data records in the database:
i solve it from query:
select COUNT(gender) as genderCount from customers where gender = "male"
to code sequelize :
await Customer.findAll({
attributes: {
include: [[Sequelize.fn("COUNT", Sequelize.col('gender')), "genderCount"]] ,
},
where:[{"gender":'male'}]
}).then(data => {
res.send(data)
})

Specifying key with $add?

I would like to index customers in the data store using their email address as key, to enforce uniqueness.
I'm initialising my connection as follows:
var ref = new Firebase("https://[myApp].firebaseio.com/");
$scope.customers = $firebase(ref.child("customers")).$asArray();
And here's my controller:
function customerCtrl($scope) {
$scope.addNewMember = function (firstName, lastName, email) {
$scope.customers.$add({
firstName: firstName,
lastName: lastName,
email: email
});
}
}
Of course, this generates a unique key like -Jbd4KJ_CrFqlpnqbGVu. Is there a way to set the key manually instead, so that my data will look like this?
{
'bob.smith#example.com': {
firstName: 'Bob',
lastName: 'Smith'
},
'jane.doe#example.com': {
firstName: 'Jane',
lastName: 'Doe'
},
...
}
Or, am I thinking about this the wrong way?
You don't need a collection (to use $asArray) if you are working with a specific path with a key you already know. You'd probably be ahead to work directly with a Firebase reference in that case.
var ref = new Firebase("https://[myApp].firebaseio.com/");
$scope.customers = $firebase(ref.child("customers")).$asArray();
$scope.addNewMember = function(firstName, lastName, email) {
var escapedEmail = escapeEmailAddress(email);
ref.child('customers/'+escapedEmail).set({firstName: firstName, lastName: lastName, email: email});
};
function escapeEmailAddress(email) {
return (email||'').replace(/\./g, ',');
}
The data will automatically be synchronized into $scope.customers in real-time.
If you're set on doing this through the synchronized array, then manually declare the $id and use $save instead:
$scope.addNewMember = function (firstName, lastName, email) {
var rec = {
firstName: firstName,
lastName: lastName,
email: email,
$id: escapeEmailAddress(email)
};
$scope.customers.push(rec);
$scope.customers.$save(rec);
};