typeorm geometry type Undefined type error - postgresql

everyone. I am creating a project that uses geometry data using postgresql postgis this time. So I want to declare geometry in the column and use it, but there's an error. Could you tell me why there is an error?
Multiple official documents were checked, but no method was found.
Commenting the coordinate column will create the code normally.
import {
Column,
CreateDateColumn,
Entity,
JoinColumn,
ManyToOne,
PrimaryGeneratedColumn
} from 'typeorm';
import { Location_Group } from './location_group.entity';
import { Geometry } from 'geojson';
import { Field, ID, Int, ObjectType } from '#nestjs/graphql';
#ObjectType()
#Entity()
export class Location {
#Field(() => ID)
#PrimaryGeneratedColumn('increment')
id: number;
#Field(() => String)
#Column({ type: 'varchar' })
name: string;
#Field()
#Column({
type: 'geometry',
nullable: true,
spatialFeatureType: 'Point',
srid: 4326
})
coordinate: Geometry;
#Field(() => Int)
#Column({ type: 'int' })
order_number: number;
#Field()
#CreateDateColumn({ type: 'timestamptz' })
created_at: Date;
#Field(() => Location_Group)
#ManyToOne(
() => Location_Group,
(location_group) => location_group.location
)
#JoinColumn([{ name: 'location_group_id', referencedColumnName: 'id' }])
location_group: Location_Group;
}

There is someone who wants me to share the scalar I made, so I write it here. I hope this code helps you.
import { GraphQLScalarType } from 'graphql';
export const GeoJSONPoint = new GraphQLScalarType({
name: 'GeoJSONPoint',
description: 'Geometry scalar type',
parseValue(value) {
return value;
},
serialize(value) {
return value;
},
parseLiteral(ast) {
const geometryData = {
type: '',
coordinates: []
};
for (const i in ast['fields']) {
if (ast['fields'][i]['name']['value'] == 'type') {
if (ast['fields'][i]['value']['value'] != 'point') {
return null;
}
geometryData.type = ast['fields'][i]['value']['value'];
}
if (ast['fields'][i]['name']['value'] == 'coordinate') {
for (const j in ast['fields'][i]['value']['values']) {
geometryData.coordinates.push(
parseFloat(
ast['fields'][i]['value']['values'][j]['value']
)
);
}
}
}
return geometryData;
}
});

Related

Error: Cannot query across many-to-many for property on updating

I am try to update many to may relation.
export class CreateProductDto {
#ApiProperty()
#IsString()
description: string;
#ApiProperty()
#IsString()
name: string;
#ApiProperty({ isArray: true })
#IsNumber({}, { each: true })
#IsArray()
categoryIds: number[];
}
export class UpdateProductDto extends PartialType(CreateProductDto) {}
export class ProductsService {
constructor(
#InjectRepository(Product)
private productRepository: Repository<Product>,
private categoriesService: CategoriesService,
) {}
async update(id: number, updateProductDto: UpdateProductDto) {
let categories: Category[] = undefined;
if (updateProductDto.categoryIds) {
categories = await Promise.all(
updateProductDto.categoryIds.map(
async (id) => await this.categoriesService.findOneOrFail(id),
),
);
delete updateProductDto.categoryIds;
}
await this.productRepository.update(
{ id },
{ ...updateProductDto, categories },
);
return await this.findOneOrFail(id);
}
async findOneOrFail(id: number) {
const product = await this.productRepository.findOne({ id });
if (product) {
return product;
}
throw new BadRequestException(`Product is not present`);
}
}
#Entity()
export class Product extends BaseEntity {
#Column()
description: string;
#Column()
name: string;
#ManyToMany(() => Category, (object) => object.products, {
cascade: true,
eager: true,
})
#JoinTable()
categories: Category[];
}
#Entity()
export class Category extends BaseEntity {
#Column()
name: string;
#ManyToMany(() => Product, (object) => object.categories)
products: Product[];
}
Finally when i try to call ProductsService.update with this payload it
"categoryIds": [ 2 ]
i got an error like this
Error: Cannot query across many-to-many for property categories
Can some please help me to update many to many
In your Category Entity add the relation id of Product and use save method instead of update when you update your entity.
#Entity()
export class Category extends BaseEntity {
#Column()
name: string;
#ManyToMany(() => Product, (object) => object.categories)
products: Product[];
// Add this
#Column()
productId: string;
}
To solve problemes like i use.
async dummyUpdate(objetUpdateDto: ObjectUpdateDto): Promise<TypeReturn> {
const { idObjectToUpdate, colum1ToUpate, colum2ToUpate } = objetUpdateDto;
try {
const objectToUpdate = await this.repositoryOfEntity.findOne(idObjectToUpdate);
idObjectToUpdate.colum1ToUpate = colum1ToUpate;
idObjectToUpdate.colum2ToUpate = colum2ToUpate;
await this.repositoryOfEntity.save(objectToUpdate);
return objectToUpdate,
} catch(err) {
throw new ConflictException("your message" + err);
}
}

NestJS Mongodb Compoud Index

I am currently writing mongodb schema in nest js.
My schema is below
{
_id: {
filed1: string,
filed2: string,
field3: string,
field4: {
filed5: int
}
}
filed1: ~
...
...
Is there a way to create nestJs schema for the collection?
Is there a way to set the index for the _id field?
I've tried the following, but it didn't work.
import { DateTimeScalar } from '#/common/scalars/dateTime.scalar';
import { Field, Float, ID, Int, ObjectType } from '#nestjs/graphql';
import { Prop, raw, Schema, SchemaFactory } from '#nestjs/mongoose';
import { Document } from 'mongoose';
export type Document = MongooseClass & Document;
#ObjectType()
export class filed4 {
#Field(() => Int)
filed5: number;
}
#ObjectType()
export class compundId {
#Field(() => String)
filed1: string;
#Field(() => String)
filed2: string;
#Field(() => String)
filed3: string;
#Field(() => field4)
field4: field5;
}
#ObjectType()
#Schema({ collection: 'collection' })
export class MongooseClass {
#Field(() => ID, { nullable: false })
#Prop(
raw({
filed1: { type: String },
filed2: { type: String },
filed3: { type: string },
}),
)
_id: compundId;
filed ...
}
const MongooseSchema = SchemaFactory.createForClass(MongooseClass);
MongooseSchema.index({ _id: { filed1: 1, filed5: 1 } });
MongooseSchema.index({ filed2: 1, filed3: 1 });
MongooseSchema.index({ filed5: 1 });
export default ExcuteRateInfoMongooseSchema;
After checking the documentation, I couldn't find an answer to that question. please help me

How can I use the point type coordinate information of postgis in graphql using mutations?

I created a mutation to insert new data into the postgresql called location. The column coordinate must receive and store data, for example, ST_GeomFromGeoJSON ('{ "type": "Point", "coordinates": [-48.23456,20.12345]}').
However, graphql is not working, so I don't know where to modify it. I think it's because the scalar called GeoJSONPoint that I made is not working properly. Could you tell me how to create a scalar if graphql puts the data above?
GeoJSONPoint Scalar
import { GraphQLScalarType, Kind } from 'graphql';
export const GeoJSONPoint = new GraphQLScalarType({
name: 'GeoJSONPoint',
description: 'Geometry scalar type',
parseValue(value) {
return value;
},
serialize(value) {
return value;
},
parseLiteral(ast) {
if (ast.kind === Kind.OBJECT) {
console.log(ast);
return new Object(ast);
}
return null;
}
});
location.entity
import {
Column,
CreateDateColumn,
Entity,
JoinColumn,
ManyToOne,
PrimaryGeneratedColumn
} from 'typeorm';
import { Location_Group } from './location_group.entity';
import { Geometry } from 'geojson';
import { Field, ID, Int, ObjectType } from '#nestjs/graphql';
import { GeoJSONPoint } from 'src/scalar/geoJSONPoint.scalar';
#ObjectType()
#Entity('location')
export class Location {
#Field(() => ID)
#PrimaryGeneratedColumn('increment')
id: number;
#Field(() => String)
#Column({ type: 'varchar' })
name: string;
#Field(() => GeoJSONPoint)
#Column({
type: 'geometry',
nullable: true,
spatialFeatureType: 'Point',
srid: 4326
})
coordinate: Geometry;
#Field(() => Int)
#Column({ type: 'int' })
order_number: number;
#Field()
#CreateDateColumn({ type: 'timestamptz' })
created_at: Date;
#Field(() => Location_Group)
#ManyToOne(
() => Location_Group,
(location_group) => location_group.location
)
#JoinColumn([{ name: 'location_group_id', referencedColumnName: 'id' }])
location_group: Location_Group;
}
resolver
#Mutation(() => Location)
async createLocation(
#Args('data') data: LocationDataInput
): Promise<Location> {
console.log(data);
return await this.locationService.setLocation(data);
}
I solved this problem. First of all, we divided the values entered by parseLiteral in scalar into
{type: '', coordinates: []}
and removed the foreign key column.

NestJS Insert a Comment into a user blog post

I have an app where an user can create a list of Recipes and each Recipe can have multiple comments that many users can post.
This is what im trying to do:
I have a comments Enitity:
import {
Entity,
PrimaryGeneratedColumn,
Column,
BeforeUpdate,
ManyToOne,
JoinColumn,
ManyToMany,
} from 'typeorm';
import { UserEntity } from 'src/user/models/user.entity';
import { RecipeEntity } from 'src/recipe/model/recipe-entry.entity';
import { User } from 'src/user/models/user.interface';
#Entity('comments_entry')
export class CommentsEntity {
#PrimaryGeneratedColumn()
id: number;
#Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
createdAt: Date;
#Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
updatedAt: Date;
#BeforeUpdate()
updateTimestamp() {
this.updatedAt = new Date();
}
#ManyToOne(
type => UserEntity,
user => user.username,
)
author: UserEntity;
#Column()
recipe_id: number;
#Column()
author_id: number;
#ManyToOne(
type => RecipeEntity,
recipe => recipe.comment,
)
#JoinColumn({ name: 'recipe_id', referencedColumnName: 'id' })
comment: RecipeEntity;
}
Linked to a Recipe entity:
import {
Entity,
PrimaryGeneratedColumn,
Column,
BeforeUpdate,
ManyToOne,
JoinColumn,
OneToMany,
JoinTable,
ManyToMany,
} from 'typeorm';
import { UserEntity } from 'src/user/models/user.entity';
import { CommentsEntity } from 'src/comments/model/comments.entity';
#Entity('recipe_entry')
export class RecipeEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
title: string;
#Column()
slug: string;
#Column('text', { array: true, nullable: true })
ingr: string[];
#Column({ default: '' })
description: string;
#Column({ default: '' })
body: string;
#Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
createdAt: Date;
#Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
updatedAt: Date;
#BeforeUpdate()
updateTimestamp() {
this.updatedAt = new Date();
}
#Column({ nullable: true })
headerImage: string;
#Column({ nullable: true })
publishedDate: Date;
#Column({ nullable: true })
isPublished: boolean;
#Column()
user_id: number;
#ManyToOne(
type => UserEntity,
user => user.recipeEntries,
)
#JoinColumn({ name: 'user_id', referencedColumnName: 'id' })
author: UserEntity;
#Column({ default: 0 })
totalWeight: number;
#Column('text', { array: true, default: '{}' })
dietLabels: string[];
#Column({ default: 0 })
calorieQuantity: number;
#Column({ default: 0 })
proteinQuantity: number;
#Column({ default: 0 })
carbQuantity: number;
#Column({ default: 0 })
fatQuantity: number;
#Column({ default: 0 })
sugarQuantity: number;
#Column('text', { array: true, nullable: true })
likes: string[];
#Column({ default: false, nullable: true })
isLiked: boolean;
#OneToMany(
type => CommentsEntity,
comments => comments.comment,
)
comment: CommentsEntity[];
}
Linked to an User entity:
import {
Entity,
PrimaryGeneratedColumn,
Column,
BeforeInsert,
OneToMany,
} from 'typeorm';
import { UserRole } from './user.interface';
import { RecipeEntity } from 'src/recipe/model/recipe-entry.entity';
import { CommentsEntity } from 'src/comments/model/comments.entity';
#Entity()
export class UserEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#Column({ unique: true })
username: string;
#Column({ unique: true })
email: string;
#Column({ select: false })
password: string;
#Column({ type: 'enum', enum: UserRole, default: UserRole.USER })
role: UserRole;
#Column({ nullable: true })
profileImage: string;
#Column({ default: false, nullable: true })
favourite: boolean;
#OneToMany(
type => RecipeEntity,
recipeEntity => recipeEntity.author,
)
recipeEntries: RecipeEntity[];
#OneToMany(
type => CommentsEntity,
recipeEntryEntity => recipeEntryEntity.author,
)
commentEntries: CommentsEntity[];
#BeforeInsert()
emailToLowerCase() {
this.email = this.email.toLowerCase();
}
}
As an user i can post recipes. But im failing to add comments on specific recipes.
2 errors:
When i create a Recipe with some hardcoded comments, the users and recipe table gets filled but the comments_entry table is empty.
And im failing to implement the method to add comments to a specific recipe.
Controller:
#UseGuards(JwtAuthGuard)
#Post('recipe/:id')
createComment(
#Param() params,
#Body() comment: string,
#Request() req,
): Observable<RecipeEntry> {
const user = req.user;
const id = params.id;
return this.recipeService.createComment(user, id, comment);
}
createComment(id: number, commentEntry: string): Observable<RecipeEntry> {
return from(this.findOne(id)).pipe(
switchMap((recipe: RecipeEntry) => {
const newComment = recipe.comment.push(commentEntry);
return this.recipeRepository.save(newComment);
}),
);
}
Type 'Observable<DeepPartial[]>' is not assignable to type 'Observable'.
Property 'comment' is missing in type 'DeepPartial[]' but required in type 'RecipeEntry'.ts(2322)
recipe-entry.interface.ts(18, 3): 'comment' is declared here.
Any help?
Can't build working example but may be would helpful:
Redo your relations (I simplified entities for example, but you should use full:) ):
#Entity('comments_entry')
export class CommentsEntity {
#Column()
authorId: number;
#ManyToOne(type => UserEntity, user => user.id)
author: UserEntity;
#ManyToOne(type => RecipeEntity, recipe => recipe.id)
recipe: RecipeEntity;
}
#Entity('recipe_entry')
export class RecipeEntity {
#Column()
authorId: number;
#ManyToOne(type => UserEntity, user => user.id)
author: UserEntity;
#OneToMany(type => CommentsEntity, comment => comment.recipe)
comments: CommentsEntity[];
}
#Entity('user_entry')
export class UserEntity {
#OneToMany( type => RecipeEntity, recipe => recipe.author)
recipes: RecipeEntity[];
#OneToMany(type => CommentsEntity, comment => comment.author)
comments: CommentsEntity[];
}
RecipeDto something like:
RecipeDto: {
authorId: number | string,
comments: CommentDto[],
****other recipe data
}
create new Recipe:
createRecipe(recipeDto: RecipeDto): Observable<RecipeEntry> {
const { comments, authorId } = recipeDto;
if(comments) {
const commentPromises = comments.map(async comment => {
comment.authorId = authorId;
return await this.commentRepository.save(comment);
});
recipeDto.comments = await Promise.all(commentPromises);
}
return await this.recipeRepository.save(recipeDto);
}
If I understood correctly, you are trying that:
One User --> Many Recipes
One User --> Many Comments
One Recipe --> Many Comments
Your entities seems right.
Normally a typeorm repository returns a promise and not an observable.
You need to convert it to an Observable.
And at the moment you are trying to store a comment in the recipeRepo. You should save the whole recipe. And before you have to save the comment in the comment repo (if you are not working with cascades).
Something like this:
createComment(id: number, commentEntry: string): Observable<RecipeEntry> {
return from(this.findOne(id)).pipe(
switchMap((recipe: RecipeEntry) => {
return from(this.commentRepository.save(newComment)).pipe(
map(com: Comment) => {
recipe.comment.push(com);
return from(this.recipeRepository.save(recipe));
}
)
);
}
If you enable cascades, you can do this in only one call.

How to update or create entities(with relations) via graphql and typeorm

I use typeorm, type-graphql and postgres, apollo-graphql-express at my project. I have some troubles with creating or updating entities via typeorm.
Example: I made queries and mutations(by my task explanation) such as
//Mutation
registerRestaurant(name: String, address: String, phone: String, type: Int, restaurant_key: String, headquarter: Int): Restaurant
//Mutation
updateRestaurant(restaurantId: Int!, name: String, address: String, phone: String, type: Int, restaurant_key: String, headquarter: Int): Restaurant
Restaurant entity has headquarter property which returns Headquarter entity(for relation cases), here is an trouble, that then i can't update or create Restaurant entity using id as an argument in mutation. (example: in mutation update restaurant i need to change headquarter with id = int, but Restaurant entiry doesn't allow me to do that because of JoinColumn i think)).
I will be very happy if you pay attention to this, if some questions about my code, i will answer, THANKS!
#ObjectType()
#Entity("restaurants")
export class Restaurant extends BaseEntity {
#Field(() => Int)
#PrimaryGeneratedColumn()
id: number;
#Field()
#Column("text", { default: '' })
name: string;
#Field()
#Column("text", { default: '' })
address: string;
#Field()
#Column("text", { default: '' })
phone: string;
#Field(() => Int)
#Column("int", { default: 0 })
type: number;
#Field()
#Column("text", { default: '' })
restaurant_key: string;
#OneToOne(() => Headquarter)
#Field(() => Headquarter)
#JoinColumn()
headquarter: Headquarter;
}
#Resolver()
export class RestaurantResolver {
#Query(() => Restaurant)
async getRestaurant(
#Arg("restaurantId", { nullable: false }) restaurantId: number
) {
console.log(await Restaurant.findOne(restaurantId, { relations:
["headquarter"] }));
const restaurant = await Restaurant.findOne(restaurantId, { relations:
["headquarter"] });
if (restaurant) {
return restaurant;
} else throw new Error("restaurant not found");
};
#Query(() => [Restaurant])
async getRestaurants() {
try {
return await Restaurant.find({ relations: ["headquarter"] });
} catch (error) {
console.log(error);
throw new Error('restaurants not found');
}
};
#Mutation(() => Restaurant)
async registerRestaurant(
#Arg("name", { nullable: false }) name: string, // nullable: false means
REQUIRED FIELD
#Arg("address", { nullable: false }) address: string,
#Arg("phone", { nullable: false }) phone: string,
#Arg("type", { nullable: false }) type: number,
#Arg("restaurant_key", { nullable: false }) restaurant_key: string,
// #Arg("headquarter", { nullable: false }) headquarter: number
) {
const restaurant = new Restaurant();
restaurant.name = name || restaurant.name;
restaurant.address = address || restaurant.address;
restaurant.phone = phone || restaurant.phone;
restaurant.type = type || restaurant.type;
restaurant.restaurant_key = restaurant_key ||
restaurant.restaurant_key;
// restaurant.headquarter = headquarter || restaurant.headquarter;
//??? how to update when in Restaurant Entity is OnetoOne and
JoinColumn
try {
return await Restaurant.save(restaurant);
} catch (error) {
console.log(error);
return error.message;
};
};
#Mutation(() => Restaurant)
async updateRestaurant(
#Arg("restaurantId", { nullable: false }) restaurantId: number,
#Arg("name", { nullable: true }) name?: string,
#Arg("address", { nullable: true }) address?: string,
#Arg("phone", { nullable: true }) phone?: string,
#Arg("type", { nullable: true }) type?: number,
#Arg("restaurant_key", { nullable: true }) restaurant_key?: string,
#Arg("headquarter", { nullable: true }) headquarter?: number
) {
console.log("phone",phone);
const restaurantToUpdate = await Restaurant.findOne(restaurantId);
console.log(restaurantToUpdate);
if (restaurantToUpdate) {
restaurantToUpdate.name = name || restaurantToUpdate.name;
restaurantToUpdate.address = address || restaurantToUpdate.address;
restaurantToUpdate.phone = phone || restaurantToUpdate.phone;
restaurantToUpdate.type = type || restaurantToUpdate.type;
restaurantToUpdate.restaurant_key = restaurant_key ||
restaurantToUpdate.restaurant_key;
restaurantToUpdate.headquarter = headquarter ||
restaurantToUpdate.headquarter; // type number is not assignable to type
Headquarter
return Restaurant.save(restaurantToUpdate);
} else {
throw new Error("restaurant not found");
};
};
};