I'm having a problem using typegoose,
I have class like:
class UserType1 extends Typegoose
implements User{
#prop({required: true, unique: true})
_username: string | undefined;
#prop({required: true})
_password: string | undefined;
constructor(username:string, password: string){
this._username = username;
this._password = password;
}
async saveToDB(): Promis<void>{ // This method is refrenced inside User Interface
const model: ModelType<UserType1> = getModelForClass(UserType1);
await model.create(this);
}
<Other methods inside class>
}
Then I use the above code sequence like this:
const u: User = new UserType1("username", "password");
u.saveToDB();
then, a new record is saved inside UserType1 collection,
but it's empty. non of the _username, _password or other props are saved inside the record. Inside of it there's only _id and a variable called "__v" (which I don't know where it's come from.
from what i got as an answer in the comments about not using an unmaintained version, i would recommend to write your example in this way:
class UserType1 implements User {
#prop({ required: true, unique: true })
public _username!: string;
#prop({ required: true })
public _password!: string;
static async default(this: ReturnModelType<typeof UserType1>, username: string, password: string): Promise<void> {
await this.create({ _username: username, _password: password });
}
// <Other methods inside class>
}
const UserModel = getModelForClass(UserType1);
// somewhere in an async code block
await UserModel.default("user1", "somePassword");
Okay, So I left my code and started a new clean one, and It was working perfectly,
so I started investigating the difference between codes until I found out this small problem with importing packages, and it was unrelated to my code:
Instead of doing
import {prop} from "#typegoose/typegoose"
I was doing:
import {prop} from "typegoose"
Since it didn't give me any warning a worked without errors, I never realized this was the problem.
Hope this he
Related
I have followed NestJS mongo docs to create a schema (without ID) from a regular class. Since this class has other general uses (unrelated to mongoose), I would like to have regular methods on that class as well:
#Schema({ _id: false })
export class Location {
#Prop()
lat: number;
#Prop()
lon: number;
regularMethod() { return something based on this.lat, this.lon }
}
export const LocationSchema = SchemaFactory.createForClass(Location);
export type CatDocument = HydratedDocument<Cat>;
#Schema()
export class Cat {
#Prop({ type: [LocationSchema] })
locations: Location[];
}
export const CatSchema = SchemaFactory.createForClass(Cat);
The problem is that if I query such an object from the db, regularMethod doesn't exist since the queried object is actually a Document based on Location, rather than Location. The document only exposes methods that were defined by the schema.methods, which is not what I need.
MyService {
constructor(#InjectModel(Cat.name) catModel: Model<CatDocument>) {}
async findLocations(catId: string) {
const cat = await catModel.findOneById(catId);
cat.location.forEach(loc => loc.regularMethod()) // no such method
}
}
Is there some obvious way to "cast" Location to the original class to have access to those methods?
I have UserEntity and AddressEntity, they are related as OneToOne, that's one user may have only one address. UserEntity has fields firstName, secondName, address. AddressEntity has fields country and city.
If I wanted to update UserEntity without doing it to its relations I would do this:
await entityManager.getRepository(UserEntity)
.createQueryBuilder('users')
.update(UserEntity)
.set(updateUserObject)
.where('users.id = :userId', { userId })
.execute();
where updateUserObject is formed from a request body. That's to say, if I need to update firstName, the object would look like this: { firstName: 'Joe' }. Now what is unclear is how to use that builder if I have the following updateUserObject:
{
firstName: "Bob",
address: {
"city": "Ottawa"
}
}
The official documentation does not address such cases.
You can achieve this using preload and save methods.
Update your UserEntity similar to below:
#Entity('user')
export class UserEntity {
...
#OneToOne(
() => AddressEntity,
{
// Make sure that when you delete or update a user, it will affect the
// corresponding `AddressEntity`
cascade: true,
// Make sure when you use `preload`, `AddressEntity` of the user will also
// return (This means whenever you use any kind of `find` operations on
// `UserEntity`, it would load this entity as well)
eager: true
}
)
#JoinColumn()
address: AddressEntity;
}
Now using entityManager, you can update all the fields that you want using the following way:
const partialUserEntity = {
id: userId,
firstName: "Bob",
address: {
"city": "Ottawa"
}
};
const userRepository = await entityManager.getRepository(UserEntity);
// Here we load the current user entity value from the database and replace
// all the related values from `partialUserEntity`
const updatedUserEntity = userRepository.preload(partialUserEntity);
// Here we update (create if not exists) `updatedUserEntity` to the database
await userRepository.save(updatedUserEntity);
However, you need to make sure that your UserEntity has an AddressEntity associated always. Otherwise, you will have to generate an id for AddressEntity like below before you execute save method.
/*
* If `updatedUserEntity.address.id` is `undefined`
*/
// `generateIDForAddress` is a function which would return an `id`
const generatedIDForAddress = generateIDForAddress();
const partialUserEntity = {
id: userId,
firstName: "Bob",
address: {
"id": generatedIDForAddress,
"city": "Ottawa"
}
};
Please note that under the hood, typeorm will run UPDATE statements separately for UserEntity and AddressEntity. This is just an encapsulation of multiple join statements (when executing preload method) and update statements (when executing save method) such that the developer can easily implement this scenario.
Hope this helps you. Cheers 🍻!
lets say you have an interface like this:
import { Get, QueryParam } from 'routing-controllers';
// ...
#Get('/students')
async getStudents(
#QueryParam('count') count?: number,
): Promise<void> {
console.log(count);
}
How do you ensure count is an int and not a float, for example? Something like this is not valid:
#IsInt() #QueryParam('count') count?: number,
IsInt can only be used on a class property, eg for a body model, not for a single parameter value. But according to. this https://github.com/typestack/routing-controllers#auto-validating-action-params it is possible:
This technique works not only with #Body but also with #Param,
#QueryParam, #BodyParam and other decorators.
I had missed this in the docs: https://github.com/typestack/routing-controllers#inject-query-parameters By injecting all of the QueryParams instead of individual QueryParam, you can validate them as a class model:
enum Roles {
Admin = "admin",
User = "user",
Guest = "guest",
}
class GetUsersQuery {
#IsPositive()
limit: number;
#IsAlpha()
city: string;
#IsEnum(Roles)
role: Roles;
#IsBoolean()
isActive: boolean;
}
#Get("/users")
getUsers(#QueryParams() query: GetUsersQuery) {
// here you can access query.role, query.limit
// and others valid query parameters
}
Also, make sure you don't use barrel-imports to import Enums, or open-api-generator will produce an error that the enum is undefined and not and object; eg avoid this: import { Roles } from '../..'
How to get entity table name ? (ex: member-pre-sale-detail)
I want to set table comment
// Seeder: Clear & set Comment
export default class ClearAllSeed implements Seeder {
public async run(factory: Factory, connection: Connection): Promise<void> {
const deleteEntities = [
{table: OrderHead, comment: '訂單/主表'},
]
for(const entity of deleteEntities){
await connection
.createQueryBuilder()
.delete()
.from(entity.table)
.execute();
await connection
// >>>> but table name is MemberPreSaleDetail not member-pre-sale-detail
.query(`alter table ${entity.table.name} comment '${entity.comment}'`);
}
}
}
// Sampel Entity
#Entity('member-pre-sale-detail')
export class MemberPreSaleDetail {
#PrimaryGeneratedColumn({unsigned: true})
id?: number;
#Column({comment: '幾批(整批)', type: 'mediumint', default: 0})
batchQty: number;
}
Expected behavior
get the 'member-pre-sale-detail' string
Environment
Nest version: 7.0.7
For Tooling issues:
- Node version: v14.5.0
- Platform: Mac
I am guessing you are using TypeORM. In that case:
You could get the entity metadata by calling connection.getMetadata(MemberPreSaleDetail).
This method returns an EntityMetadata, which has name, tableName and givenTableName properties. For your usecase I guess you could simply use givenTableName.
I have a project which uses TypeORM and I have the following two entities:
#Entity("Email")
export class EmailEntity extends BaseEntity implements IEmail {
#Column({length: 100}) to: string;
#Column({length: 100}) from: string;
#Column({length: 255}) subject: string;
#Column({nullable: true}) html: string;
#Column({nullable: true}) text: string;
}
and
#Entity("QueuedEmail")
export class QueuedEmailEntity extends BaseEntity implements IQueuedEmail {
#OneToOne(email => EmailEntity, {nullable: false, cascadeAll: true})
#JoinColumn()
email: EmailEntity;
#Column() retryCount: number;
#Column() status: QueuedEmailStatus;
constructor() {
super();
this.retryCount = 0;
this.status = QueuedEmailStatus.QueuedForDispatch;
}
}
BaseEntity is an abstract class which has an id column with the #PrimaryGeneratedColumn.
I have the following code which updates the status on a QueuedEmail:
const queuedEmailEntityArray: QueuedEmailEntity[] =
await this.queuedEmailRepository.createQueryBuilder("queuedEmail")
.where("queuedEmail.status = :queuedEmailStatus", {queuedEmailStatus: QueuedEmailStatus.QueuedForDispatch})
.innerJoinAndSelect("queuedEmail.email", "email")
.orderBy("queuedEmail.id", "DESC")
.getMany();
queuedEmailEntityArray.forEach(async (value, index) => {
let queuedEmailEntity: QueuedEmailEntity = queuedEmailEntityArray.pop();
queuedEmailEntity.status = QueuedEmailStatus.AttemptingToDispatch;
await this.queuedEmailRepository.persist(queuedEmailEntity);
});
Now, the select correctly loads the array or QueuedEmailEntity objects, the forEach runs but throws the following exception:
query failed: DELETE FROM "Email" WHERE "id"=$1 -- PARAMETERS: [1]
error during executing query:error: update or delete on table "Email" violates foreign key constraint "fk_d438362cf2adecbcc5b17f45606" on table "QueuedEmail"
The debug query output shows that TypeORM is updating the status field but also updating the emailId and then attempting to delete the EmailEntity record.
UPDATE "QueuedEmail" SET "status"=$1, "emailId"=$2 WHERE "id"=$3 -- PARAMETERS: [1,1,1]
DELETE FROM "Email" WHERE "id"=$1 -- PARAMETERS: [1]
I don't understand why TypeORM would need to update the emailId field when I have not changed the reference, much less why it would try to delete a record that is still referenced. Can anyone see what is wrong with the above code?