TypeOrm Joins without using QueryBuilder - postgresql

I have 3 tables
attendance
user
attendance_verification.
attendance->ManyToOne->user
attendance->OneToOne->attendanceVerification
now I want to query attendance for a specific user and want to embed complete user and attendanceVerification data in attendance object and return it.
This is what I have tried but it is giving me error Invalid syntax near where.
I have tried
const listOfAttendance = await this.attendanceRepository.find({
where: {
punchInDateTime: Between(dateFrom, dateTo),
},
order: {
id: 'ASC',
},
join: {
alias: 'attendance',
leftJoinAndSelect: {
user: 'attendance.user',
outlet: 'attendance.outlet',
punchAck: 'attendance.punchAck',
verification: 'attendance.attendanceVerification',
},
},
})
Query being parsed by TypeORM
SELECT
"attendance"."id" AS "attendance_id",
"attendance"."punchInImage" AS "attendance_punchInImage",
"attendance"."punchInDateTime" AS "attendance_punchInDateTime",
"attendance"."punchInComment" AS "attendance_punchInComment",
"attendance"."punchOutDateTime" AS "attendance_punchOutDateTime",
"attendance"."punchOutComment" AS "attendance_punchOutComment",
"attendance"."punchOutImage" AS "attendance_punchOutImage",
"attendance"."status" AS "attendance_status",
"attendance"."approvalStatus" AS "attendance_approvalStatus",
"attendance"."userId" AS "attendance_userId",
"attendance"."outletId" AS "attendance_outletId",
"attendance"."createdAt" AS "attendance_createdAt",
"user"."id" AS "user_id",
"user"."firstName" AS "user_firstName",
"user"."lastName" AS "user_lastName",
"user"."userName" AS "user_userName",
"user"."contact" AS "user_contact",
"user"."gender" AS "user_gender",
"user"."dob" AS "user_dob",
"user"."country" AS "user_country",
"user"."state" AS "user_state",
"user"."postalCode" AS "user_postalCode",
"user"."isAdmin" AS "user_isAdmin",
"user"."isEmployee" AS "user_isEmployee",
"user"."byEmail" AS "user_byEmail",
"user"."byContact" AS "user_byContact",
"user"."address" AS "user_address",
"user"."email" AS "user_email",
"user"."orgId" AS "user_orgId",
"user"."password" AS "user_password",
"user"."salt" AS "user_salt",
"user"."createdBy" AS "user_createdBy",
"user"."status" AS "user_status",
"user"."apiAccessKey" AS "user_apiAccessKey",
"user"."createdAt" AS "user_createdAt",
"user"."metaId" AS "user_metaId",
"outlet"."id" AS "outlet_id",
"outlet"."name" AS "outlet_name",
"outlet"."comment" AS "outlet_comment",
"outlet"."location" AS "outlet_location",
"outlet"."status" AS "outlet_status",
"outlet"."orgId" AS "outlet_orgId",
"outlet"."validated" AS "outlet_validated",
"punchAck"."id" AS "punchAck_id",
"punchAck"."isPunchedIn" AS "punchAck_isPunchedIn",
"punchAck"."isAck" AS "punchAck_isAck",
"punchAck"."userId" AS "punchAck_userId",
"punchAck"."dateTime" AS "punchAck_dateTime",
"punchAck"."rejectComment" AS "punchAck_rejectComment",
"punchAck"."attendanceId" AS "punchAck_attendanceId",
"verification"."id" AS "verification_id",
"verification"."status" AS "verification_status",
"verification"."punchIn" AS "verification_punchIn",
"verification"."punchOut" AS "verification_punchOut",
"verification"."supervisorId" AS "verification_supervisorId",
"verification"."attendanceId" AS "verification_attendanceId",
"verification"."comment" AS "verification_comment",
"verification"."createdAt" AS "verification_createdAt"
FROM
"tenant"."attendance" "attendance"
LEFT JOIN "tenant"."users" "user" ON "user"."id" = "attendance"."userId"
LEFT JOIN "tenant"."outlet" "outlet" ON "outlet"."id" = "attendance"."outletId"
LEFT JOIN "tenant"."punch_ack" "punchAck" ON "punchAck"."attendanceId" = "attendance"."id"
LEFT JOIN "tenant"."attendance_verification" "verification" ON
WHERE
"attendance"."punchInDateTime" BETWEEN $ 1
AND $ 2
ORDER BY
"attendance"."id" ASC
PARAMETERS:[
"2021-02-28T19:00:00.000Z",
"2021-03-31T18:59:59.000Z"
]
AttendanceEntity (Exluded some fields to make code clean)
import { ApiPropertyOptional } from '#nestjs/swagger'
import {
BaseEntity,
Column,
Entity,
ManyToOne,
OneToMany,
OneToOne,
PrimaryGeneratedColumn,
} from 'typeorm'
import { AttendanceVerification } from '#modules/attendance/attendance-verification/attendance_verification.entity'
import { Break } from '#modules/attendance/break/break.entity'
import { PunchAck } from '#modules/attendance/punch-ack/punch_ack.entity'
import { Outlet } from '#modules/outlet/outlet.entity'
import { User } from '#modules/users/users.entity'
#Entity('attendance')
export class Attendance extends BaseEntity {
#ApiPropertyOptional()
#PrimaryGeneratedColumn()
id: number
#ApiPropertyOptional()
#Column()
punchInImage: string
#ApiPropertyOptional()
#Column({ type: 'timestamp' })
punchInDateTime: Date
#ApiPropertyOptional()
#Column()
approvalStatus: string
#ApiPropertyOptional()
#Column()
userId: number
#ApiPropertyOptional()
#Column()
outletId: number
#ManyToOne(type => User, user => user.attendance)
user: User
#OneToOne(type => Outlet, outlet => outlet.attendance)
outlet: Outlet
#OneToMany(type => Break, breaks => breaks.attendance)
breaks: Break[]
#OneToMany(type => PunchAck, punchAck => punchAck.attendance)
punchAck: PunchAck[]
#OneToOne(
type => AttendanceVerification,
attendanceVerification => attendanceVerification.attendance
)
attendanceVerification: AttendanceVerification
}
All solutions I find on internet uses queryBuilder. I don't want to use that lengthy method.

From typeorm docs: https://typeorm.io/#/find-options
You can use something like:
userRepository.find({
join: {
alias: "user",
leftJoinAndSelect: {
profile: "user.profile",
photo: "user.photos",
video: "user.videos"
}
}
});
Or your example may look like:
const returnedResult = await this.attendanceRepository.findOne({
where: { userId: id, status: 'PUNCHED IN' },
join: {
alias: "attendance",
leftJoinAndSelect: {
user: "attendance.user",
outlet: "attendance.outlet",
}
},
});
From my personal experience if you'd need to write complex queries you have to work with QueryBuilder. It looks awkward on the first glance only.

Related

Can not make MoreThanOrEqual work in TypeOrm

I'm trying to make some filter, and meet the problem: MoreThanOrEqual is not working,
this.freelanceRepo.find({
join: {
alias: "freelance",
leftJoinAndSelect: {
categories: "freelance.categories"
}
},
order:{id:'ASC'},
where:{price_hours:MoreThanOrEqual(30)}//is not working
})
I tried also the query builders
this.freelanceRepo.createQueryBuilder("freelance")
.leftJoinAndSelect("freelance.categories", "categories")
.orderBy("freelance.id", "ASC")
.where("freelance.price_hours = price_hours", {price_hours:MoreThanOrEqual(filterPrice)})
.take(10)
.skip(skippedItems)
.getMany();
Here is my entity:
#Entity({name: "fl_freelance"})
export class Freelance {
#PrimaryGeneratedColumn()
id: number
#Column({default: false})
is_active: boolean
#Column({nullable:true})
price_hours: number
#OneToOne(() => User, user => user.freelance)
#JoinColumn({name: "users_id"})
users: User
#ManyToMany(() => Categories, categories => categories.freelance)
categories: Categories[]
}
have no idea what was gone wrong, thanks for your answers
Thanks for answers, I have done it that way, and it works
return await this.freelanceRepo.createQueryBuilder("freelance")
.leftJoinAndSelect("freelance.categories", "categories")
.orderBy("freelance.id", "ASC")
.where("freelance.price_hours >= :price_hours", {price_hours:10})
.take(10)
.skip(skippedItems)
.getMany();

Correct way to seed MongoDB with references via mongoose

I have three schemas, one which references two others:
userSchema
{ name: String }
postSchema
{ content: String }
commentSchema
{
content: String,
user: { ObjectID, ref: 'User' },
post: { ObjectID, ref: 'Post' }
}
How can I seed this database in a sane, scalable way? Even using bluebird promises it quickly becomes a nightmare to write.
My attempt so far involves multiple nested promises and is very hard to maintain:
User
.create([{ name: 'alice' }])
.then(() => {
return Post.create([{ content: 'foo' }])
})
.then(() => {
User.find().then(users => {
Post.find().then(posts => {
// `users` isn't even *available* here!
Comment.create({ content: 'bar', user: users[0], post: posts[0] })
})
})
})
This is clearly not the correct way of doing this. What am I missing?
Not sure about bluebird, but the nodejs Promise.all should do the job:
Promise.all([
User.create([{ name: 'alice' }]),
Post.create([{ content: 'foo' }])
]).then(([users, posts]) => {
const comments = [
{ content: 'bar', user: users[0], post: posts[0] }
];
return Comment.create(comments);
})
If you want to seed database with automatically references, use Seedgoose.
This is the easiest seeder for you to use. You don't need to write any program files, but only data files. And Seedgoose handles smart references for you. And by the way, I'm the author and maintainer of this package.
Try this it will work fine:
Note: Node Promise.all will make sure that the both query is executed properly and then return the result in Array:[Users, Posts],
If you get any error during execution of any query, it will be handle by catch block of the Promise.all.
let queryArray = [];
queryArray.push(User.create([{ name: 'alice' }]));
queryArray.push(Post.create([{ content: 'foo' }]));
Promise.all(queryArray).then(([Users, Posts]) => {
const comments = [
{ content: 'bar', user: Users[0], post: posts[0] }
];
return Comment.create(comments);
}).catch(Error => {
console.log("Error: ", Error);
})

graphql-tools, how can I use mockServer to mock a "Mutation"?

I try to use mockServer of graphql-tools to mock an Mutation.
here is my unit test:
it('should update user name correctly', () => {
mockserver
.query(
`{
Mutation {
updateUserName(id: 1, name: "du") {
id
name
}
}
}`
)
.then(res => {
console.log(res);
expect(1).to.be.equal(1);
});
});
But, got an error:
mock server test suites
✓ should get users correctly
✓ should get user by id correctly
✓ should update user name correctly
{ errors:
[ { GraphQLError: Cannot query field "Mutation" on type "Query".
at Object.Field (/Users/ldu020/workspace/apollo-server-express-starter/node_modules/graphql/validation/rules/FieldsOnCorrectType.js:65:31)
at Object.enter (/Users/ldu020/workspace/apollo-server-express-starter/node_modules/graphql/language/visitor.js:324:29)
at Object.enter (/Users/ldu020/workspace/apollo-server-express-starter/node_modules/graphql/language/visitor.js:366:25)
at visit (/Users/ldu020/workspace/apollo-server-express-starter/node_modules/graphql/language/visitor.js:254:26)
at visitUsingRules (/Users/ldu020/workspace/apollo-server-express-starter/node_modules/graphql/validation/validate.js:74:22)
at validate (/Users/ldu020/workspace/apollo-server-express-starter/node_modules/graphql/validation/validate.js:59:10)
at graphqlImpl (/Users/ldu020/workspace/apollo-server-express-starter/node_modules/graphql/graphql.js:106:50)
at /Users/ldu020/workspace/apollo-server-express-starter/node_modules/graphql/graphql.js:66:223
at new Promise (<anonymous>)
at Object.graphql (/Users/ldu020/workspace/apollo-server-express-starter/node_modules/graphql/graphql.js:63:10)
at Object.query (/Users/ldu020/workspace/apollo-server-express-starter/node_modules/graphql-tools/dist/mock.js:19:63)
at Context.it (/Users/ldu020/workspace/apollo-server-express-starter/src/mockServer/index.spec.js:95:8)
at callFn (/Users/ldu020/workspace/apollo-server-express-starter/node_modules/mocha/lib/runnable.js:383:21)
at Test.Runnable.run (/Users/ldu020/workspace/apollo-server-express-starter/node_modules/mocha/lib/runnable.js:375:7)
at Runner.runTest (/Users/ldu020/workspace/apollo-server-express-starter/node_modules/mocha/lib/runner.js:446:10)
at /Users/ldu020/workspace/apollo-server-express-starter/node_modules/mocha/lib/runner.js:564:12
at next (/Users/ldu020/workspace/apollo-server-express-starter/node_modules/mocha/lib/runner.js:360:14)
at /Users/ldu020/workspace/apollo-server-express-starter/node_modules/mocha/lib/runner.js:370:7
at next (/Users/ldu020/workspace/apollo-server-express-starter/node_modules/mocha/lib/runner.js:294:14)
at Immediate.<anonymous> (/Users/ldu020/workspace/apollo-server-express-starter/node_modules/mocha/lib/runner.js:338:5)
at runCallback (timers.js:763:18)
at tryOnImmediate (timers.js:734:5)
at processImmediate (timers.js:716:5)
message: 'Cannot query field "Mutation" on type "Query".',
locations: [Array],
path: undefined } ] }
and, I read graphql-tools interface.d.ts file.
export interface IMockServer {
query: (query: string, vars?: {
[key: string]: any;
}) => Promise<ExecutionResult>;
}
Obviously, there is no mutation function in mockServer.
Does mockServer support Mutation?
https://github.com/apollographql/graphql-tools/issues/279
It's the structure of your query. The query should be structured much like you would in GraphiQL such as:
mutation {
updateUserName(id: 1, name: "du") {
id
name
}
}
Hence, your code should look something like this, with the mutation keyword as the first thing in your query before your opening brace { :
it('should update user name correctly', () => {
mockserver
.query(`mutation {
updateUserName(id: 1, name: "du") {
id
name
}
}`)
.then(res => {
console.log(res);
expect(1).to.be.equal(1);
});
});

Sequelize.js - How to create non-trivial associations without raw SQL?

Here is my situation:
I'm using postgres 9.4, Sequelize ORM and have following models:
Service
serviceCode - primary key, string of 6 characters
serviceTitle - string
ServiceGroup
serviceCodePrefixes - array of strings that are prefixes for Service.serviceCode
serviceGroupTitle - string
Task
serviceCode - reference to Service
I need to build Task object populated with Service and ServiceGroup objects. Example:
In database:
Service {
serviceCode: '123232',
serviceTitle: 'svc title #1',
}
ServiceGroup {
serviceCodePrefix: ['12', '13', '92', ...],
serviceGroupTitle: 'svc grp title #1',
}
Task {
serviceCode: '123232',
}
Result:
Task {
service: {
serviceTitle: 'svc title #1',
},
serviceGroup: {
serviceGroupTitle: 'svc grp title #1',
},
}
The problem is that serviceCodePrefix contains not simple IDs, which can be used to create association using hasOne/belongsTo/etc., but prefix for ID.
So questions is: how this can be done without raw sql?
Turns out that right now Sequelize has experimental feature: 'on' option for 'include'. This option allows users to customize joining conditions. So my problem can be solved this way:
const Service = sequelize.define('service', {
serviceTitle: Sequelize.STRING,
serviceCode: Sequelize.STRING,
});
const ServiceGroup = sequelize.define('service_group', {
serviceGroupTitle: Sequelize.STRING,
// Array of prefixes (e.g. ['01%', '023%'])
serviceCodePrefix: Sequelize.ARRAY(Sequelize.STRING),
});
const Task = sequelize.define('task', {
taskTitle: Sequelize.STRING,
serviceCode: Sequelize.STRING,
});
Task.belongsTo(Service, { foreignKey: 'serviceCode' });
// Hack needed to allow 'include' option to work
Task.hasMany(ServiceGroup, { foreignKey: 'serviceCodePrefix', constraints: false });
// And finally
Task.findAll({
include: [
{ model: Service },
{
model: ServiceGroup,
on: [' "task"."serviceCode" LIKE ANY("serviceGroup"."serviceCodePrefix") '],
},
],
});
Not sure about the performance though.

Sailsjs Model Object Not Returning Data For Postgresql

I have the following in my Sailsjs config/adapter.js:
module.exports.adapters = {
'default': 'postgres',
postgres : {
module : 'sails-postgresql',
host : 'xxx.compute-1.amazonaws.com',
port : 5432,
user : 'xxx',
password : 'xxx',
database : 'xxx',
ssl : true,
schema : true
}
};
And in models/Movie.js:
Movie = {
attributes: {
tableName: 'movies.movies',
title: 'string',
link: 'string'
}
};
module.exports = Movie;
In my controller:
Movie.query("SELECT * FROM movies.movies", function(err, movies) {
console.log('movies', movies.rows);
});
movies.rows DOES return the correct data
However:
Movie.find({ title: 'Frozen' }, function(err, movies) {
console.log('movies', movies)
});
movies returns an EMPTY ARRAY
So it seems all connections are good because the raw query works perfectly.
Could there be something I am doing wrong with setting up the Movie.find() or with models/Movie.js?
Does the tableName attribute not support postgresql schema_name.table_name?
First off, you need to move tableName out of attributes, since it's a class-level property. Second, sails-postgresql does have some (very undocumented) support for schemas, using the meta.schemaName option:
Movie = {
tableName: 'movies',
meta: {
schemaName: 'movie'
},
attributes: {
title: 'string',
link: 'string'
}
};
module.exports = Movie;
You can give that a try, and if it doesn't work, either move your table into the public schema, or nudge the author of the schemaName support for help.