umstellung auf json Tabellen ...
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
import { BadRequestException, Inject, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { and, arrayContains, asc, count, desc, eq, gte, ilike, inArray, lte, ne, or, SQL, sql } from 'drizzle-orm';
|
||||
import { and, arrayContains, asc, count, desc, eq, gte, inArray, lte, or, SQL, sql } from 'drizzle-orm';
|
||||
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
||||
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||
import { Logger } from 'winston';
|
||||
import { ZodError } from 'zod';
|
||||
import * as schema from '../drizzle/schema';
|
||||
import { commercials, PG_CONNECTION } from '../drizzle/schema';
|
||||
import { commercials_json, PG_CONNECTION } from '../drizzle/schema';
|
||||
import { FileService } from '../file/file.service';
|
||||
import { GeoService } from '../geo/geo.service';
|
||||
import { CommercialPropertyListing, CommercialPropertyListingSchema } from '../models/db.model';
|
||||
@@ -24,33 +24,33 @@ export class CommercialPropertyService {
|
||||
const whereConditions: SQL[] = [];
|
||||
|
||||
if (criteria.city && criteria.searchType === 'exact') {
|
||||
whereConditions.push(sql`${commercials.location}->>'name' ilike ${criteria.city.name}`);
|
||||
whereConditions.push(sql`(${commercials_json.data}->'location'->>'name') ILIKE ${criteria.city.name}`);
|
||||
}
|
||||
if (criteria.city && criteria.radius && criteria.searchType === 'radius' && criteria.radius) {
|
||||
const cityGeo = this.geoService.getCityWithCoords(criteria.state, criteria.city.name);
|
||||
whereConditions.push(sql`${getDistanceQuery(commercials, cityGeo.latitude, cityGeo.longitude)} <= ${criteria.radius}`);
|
||||
whereConditions.push(sql`${getDistanceQuery(commercials_json, cityGeo.latitude, cityGeo.longitude)} <= ${criteria.radius}`);
|
||||
}
|
||||
if (criteria.types && criteria.types.length > 0) {
|
||||
whereConditions.push(inArray(schema.commercials.type, criteria.types));
|
||||
whereConditions.push(inArray(sql`${commercials_json.data}->>'type'`, criteria.types));
|
||||
}
|
||||
|
||||
if (criteria.state) {
|
||||
whereConditions.push(sql`${schema.commercials.location}->>'state' = ${criteria.state}`);
|
||||
whereConditions.push(sql`(${commercials_json.data}->'location'->>'state') = ${criteria.state}`);
|
||||
}
|
||||
|
||||
if (criteria.minPrice) {
|
||||
whereConditions.push(gte(schema.commercials.price, criteria.minPrice));
|
||||
whereConditions.push(gte(sql`(${commercials_json.data}->>'price')::double precision`, criteria.minPrice));
|
||||
}
|
||||
|
||||
if (criteria.maxPrice) {
|
||||
whereConditions.push(lte(schema.commercials.price, criteria.maxPrice));
|
||||
whereConditions.push(lte(sql`(${commercials_json.data}->>'price')::double precision`, criteria.maxPrice));
|
||||
}
|
||||
|
||||
if (criteria.title) {
|
||||
whereConditions.push(or(ilike(schema.commercials.title, `%${criteria.title}%`), ilike(schema.commercials.description, `%${criteria.title}%`)));
|
||||
whereConditions.push(sql`(${commercials_json.data}->>'title') ILIKE ${`%${criteria.title}%`} OR (${commercials_json.data}->>'description') ILIKE ${`%${criteria.title}%`}`);
|
||||
}
|
||||
if (user?.role !== 'admin') {
|
||||
whereConditions.push(or(eq(commercials.email, user?.email), ne(commercials.draft, true)));
|
||||
whereConditions.push(or(eq(commercials_json.email, user?.email), sql`(${commercials_json.data}->>'draft')::boolean IS NOT TRUE`));
|
||||
}
|
||||
// whereConditions.push(and(eq(schema.users.customerType, 'professional')));
|
||||
return whereConditions;
|
||||
@@ -59,7 +59,7 @@ export class CommercialPropertyService {
|
||||
async searchCommercialProperties(criteria: CommercialPropertyListingCriteria, user: JwtUser): Promise<any> {
|
||||
const start = criteria.start ? criteria.start : 0;
|
||||
const length = criteria.length ? criteria.length : 12;
|
||||
const query = this.conn.select({ commercial: commercials }).from(commercials).leftJoin(schema.users, eq(commercials.email, schema.users.email));
|
||||
const query = this.conn.select({ commercial: commercials_json }).from(commercials_json).leftJoin(schema.users_json, eq(commercials_json.email, schema.users_json.email));
|
||||
const whereConditions = this.getWhereConditions(criteria, user);
|
||||
|
||||
if (whereConditions.length > 0) {
|
||||
@@ -69,16 +69,16 @@ export class CommercialPropertyService {
|
||||
// Sortierung
|
||||
switch (criteria.sortBy) {
|
||||
case 'priceAsc':
|
||||
query.orderBy(asc(commercials.price));
|
||||
query.orderBy(asc(sql`(${commercials_json.data}->>'price')::double precision`));
|
||||
break;
|
||||
case 'priceDesc':
|
||||
query.orderBy(desc(commercials.price));
|
||||
query.orderBy(desc(sql`(${commercials_json.data}->>'price')::double precision`));
|
||||
break;
|
||||
case 'creationDateFirst':
|
||||
query.orderBy(asc(commercials.created));
|
||||
query.orderBy(asc(sql`${commercials_json.data}->>'created'`));
|
||||
break;
|
||||
case 'creationDateLast':
|
||||
query.orderBy(desc(commercials.created));
|
||||
query.orderBy(desc(sql`${commercials_json.data}->>'created'`));
|
||||
break;
|
||||
default:
|
||||
// Keine spezifische Sortierung, Standardverhalten kann hier eingefügt werden
|
||||
@@ -89,7 +89,7 @@ export class CommercialPropertyService {
|
||||
query.limit(length).offset(start);
|
||||
|
||||
const data = await query;
|
||||
const results = data.map(r => r.commercial);
|
||||
const results = data.map(r => ({ id: r.commercial.id, email: r.commercial.email, ...(r.commercial.data as CommercialPropertyListing) }));
|
||||
const totalCount = await this.getCommercialPropertiesCount(criteria, user);
|
||||
|
||||
return {
|
||||
@@ -98,7 +98,7 @@ export class CommercialPropertyService {
|
||||
};
|
||||
}
|
||||
async getCommercialPropertiesCount(criteria: CommercialPropertyListingCriteria, user: JwtUser): Promise<number> {
|
||||
const countQuery = this.conn.select({ value: count() }).from(schema.commercials).leftJoin(schema.users, eq(commercials.email, schema.users.email));
|
||||
const countQuery = this.conn.select({ value: count() }).from(commercials_json).leftJoin(schema.users_json, eq(commercials_json.email, schema.users_json.email));
|
||||
const whereConditions = this.getWhereConditions(criteria, user);
|
||||
|
||||
if (whereConditions.length > 0) {
|
||||
@@ -114,15 +114,15 @@ export class CommercialPropertyService {
|
||||
async findCommercialPropertiesById(id: string, user: JwtUser): Promise<CommercialPropertyListing> {
|
||||
const conditions = [];
|
||||
if (user?.role !== 'admin') {
|
||||
conditions.push(or(eq(commercials.email, user?.email), ne(commercials.draft, true)));
|
||||
conditions.push(or(eq(commercials_json.email, user?.email), sql`(${commercials_json.data}->>'draft')::boolean IS NOT TRUE`));
|
||||
}
|
||||
conditions.push(sql`${commercials.id} = ${id}`);
|
||||
conditions.push(eq(commercials_json.id, id));
|
||||
const result = await this.conn
|
||||
.select()
|
||||
.from(commercials)
|
||||
.from(commercials_json)
|
||||
.where(and(...conditions));
|
||||
if (result.length > 0) {
|
||||
return result[0] as CommercialPropertyListing;
|
||||
return { id: result[0].id, email: result[0].email, ...(result[0].data as CommercialPropertyListing) } as CommercialPropertyListing;
|
||||
} else {
|
||||
throw new BadRequestException(`No entry available for ${id}`);
|
||||
}
|
||||
@@ -131,31 +131,33 @@ export class CommercialPropertyService {
|
||||
// #### Find by User EMail ########################################
|
||||
async findCommercialPropertiesByEmail(email: string, user: JwtUser): Promise<CommercialPropertyListing[]> {
|
||||
const conditions = [];
|
||||
conditions.push(eq(commercials.email, email));
|
||||
conditions.push(eq(commercials_json.email, email));
|
||||
if (email !== user?.email && user?.role !== 'admin') {
|
||||
conditions.push(ne(commercials.draft, true));
|
||||
conditions.push(sql`(${commercials_json.data}->>'draft')::boolean IS NOT TRUE`);
|
||||
}
|
||||
const listings = (await this.conn
|
||||
const listings = await this.conn
|
||||
.select()
|
||||
.from(commercials)
|
||||
.where(and(...conditions))) as CommercialPropertyListing[];
|
||||
return listings as CommercialPropertyListing[];
|
||||
.from(commercials_json)
|
||||
.where(and(...conditions));
|
||||
return listings.map(l => ({ id: l.id, email: l.email, ...(l.data as CommercialPropertyListing) }) as CommercialPropertyListing);
|
||||
}
|
||||
// #### Find Favorites ########################################
|
||||
async findFavoriteListings(user: JwtUser): Promise<CommercialPropertyListing[]> {
|
||||
const userFavorites = await this.conn
|
||||
.select()
|
||||
.from(commercials)
|
||||
.where(arrayContains(commercials.favoritesForUser, [user.email]));
|
||||
return userFavorites;
|
||||
.from(commercials_json)
|
||||
.where(arrayContains(sql`${commercials_json.data}->>'favoritesForUser'`, [user.email]));
|
||||
return userFavorites.map(l => ({ id: l.id, email: l.email, ...(l.data as CommercialPropertyListing) }) as CommercialPropertyListing);
|
||||
}
|
||||
// #### Find by imagePath ########################################
|
||||
async findByImagePath(imagePath: string, serial: string): Promise<CommercialPropertyListing> {
|
||||
const result = await this.conn
|
||||
.select()
|
||||
.from(commercials)
|
||||
.where(and(sql`${commercials.imagePath} = ${imagePath}`, sql`${commercials.serialId} = ${serial}`));
|
||||
return result[0] as CommercialPropertyListing;
|
||||
.from(commercials_json)
|
||||
.where(and(sql`(${commercials_json.data}->>'imagePath') = ${imagePath}`, sql`(${commercials_json.data}->>'serialId')::integer = ${serial}`));
|
||||
if (result.length > 0) {
|
||||
return { id: result[0].id, email: result[0].email, ...(result[0].data as CommercialPropertyListing) } as CommercialPropertyListing;
|
||||
}
|
||||
}
|
||||
// #### CREATE ########################################
|
||||
async createListing(data: CommercialPropertyListing): Promise<CommercialPropertyListing> {
|
||||
@@ -163,10 +165,10 @@ export class CommercialPropertyService {
|
||||
data.created = data.created ? (typeof data.created === 'string' ? new Date(data.created) : data.created) : new Date();
|
||||
data.updated = new Date();
|
||||
CommercialPropertyListingSchema.parse(data);
|
||||
const convertedCommercialPropertyListing = data;
|
||||
delete convertedCommercialPropertyListing.id;
|
||||
const [createdListing] = await this.conn.insert(commercials).values(convertedCommercialPropertyListing).returning();
|
||||
return createdListing;
|
||||
const { id, email, ...rest } = data;
|
||||
const convertedCommercialPropertyListing = { email, data: rest };
|
||||
const [createdListing] = await this.conn.insert(commercials_json).values(convertedCommercialPropertyListing).returning();
|
||||
return { id: createdListing.id, email: createdListing.email, ...(createdListing.data as CommercialPropertyListing) };
|
||||
} catch (error) {
|
||||
if (error instanceof ZodError) {
|
||||
const filteredErrors = error.errors
|
||||
@@ -183,7 +185,7 @@ export class CommercialPropertyService {
|
||||
// #### UPDATE CommercialProps ########################################
|
||||
async updateCommercialPropertyListing(id: string, data: CommercialPropertyListing, user: JwtUser): Promise<CommercialPropertyListing> {
|
||||
try {
|
||||
const [existingListing] = await this.conn.select().from(commercials).where(eq(commercials.id, id));
|
||||
const [existingListing] = await this.conn.select().from(commercials_json).where(eq(commercials_json.id, id));
|
||||
|
||||
if (!existingListing) {
|
||||
throw new NotFoundException(`Business listing with id ${id} not found`);
|
||||
@@ -191,7 +193,7 @@ export class CommercialPropertyService {
|
||||
data.updated = new Date();
|
||||
data.created = data.created ? (typeof data.created === 'string' ? new Date(data.created) : data.created) : new Date();
|
||||
if (existingListing.email === user?.email || !user) {
|
||||
data.favoritesForUser = existingListing.favoritesForUser;
|
||||
data.favoritesForUser = (<CommercialPropertyListing>existingListing.data).favoritesForUser || [];
|
||||
}
|
||||
CommercialPropertyListingSchema.parse(data);
|
||||
const imageOrder = await this.fileService.getPropertyImages(data.imagePath, String(data.serialId));
|
||||
@@ -200,9 +202,10 @@ export class CommercialPropertyService {
|
||||
this.logger.warn(`changes between image directory and imageOrder in listing ${data.serialId}: ${difference.join(',')}`);
|
||||
data.imageOrder = imageOrder;
|
||||
}
|
||||
const convertedCommercialPropertyListing = data;
|
||||
const [updateListing] = await this.conn.update(commercials).set(convertedCommercialPropertyListing).where(eq(commercials.id, id)).returning();
|
||||
return updateListing;
|
||||
const { id: _, email, ...rest } = data;
|
||||
const convertedCommercialPropertyListing = { email, data: rest };
|
||||
const [updateListing] = await this.conn.update(commercials_json).set(convertedCommercialPropertyListing).where(eq(commercials_json.id, id)).returning();
|
||||
return { id: updateListing.id, email: updateListing.email, ...(updateListing.data as CommercialPropertyListing) };
|
||||
} catch (error) {
|
||||
if (error instanceof ZodError) {
|
||||
const filteredErrors = error.errors
|
||||
@@ -220,7 +223,7 @@ export class CommercialPropertyService {
|
||||
// Images for commercial Properties
|
||||
// ##############################################################
|
||||
async deleteImage(imagePath: string, serial: string, name: string) {
|
||||
const listing = (await this.findByImagePath(imagePath, serial)) as unknown as CommercialPropertyListing;
|
||||
const listing = await this.findByImagePath(imagePath, serial);
|
||||
const index = listing.imageOrder.findIndex(im => im === name);
|
||||
if (index > -1) {
|
||||
listing.imageOrder.splice(index, 1);
|
||||
@@ -228,31 +231,21 @@ export class CommercialPropertyService {
|
||||
}
|
||||
}
|
||||
async addImage(imagePath: string, serial: string, imagename: string) {
|
||||
const listing = (await this.findByImagePath(imagePath, serial)) as unknown as CommercialPropertyListing;
|
||||
const listing = await this.findByImagePath(imagePath, serial);
|
||||
listing.imageOrder.push(imagename);
|
||||
await this.updateCommercialPropertyListing(listing.id, listing, null);
|
||||
}
|
||||
// #### DELETE ########################################
|
||||
async deleteListing(id: string): Promise<void> {
|
||||
await this.conn.delete(commercials).where(eq(commercials.id, id));
|
||||
await this.conn.delete(commercials_json).where(eq(commercials_json.id, id));
|
||||
}
|
||||
// #### DELETE Favorite ###################################
|
||||
async deleteFavorite(id: string, user: JwtUser): Promise<void> {
|
||||
await this.conn
|
||||
.update(commercials)
|
||||
.update(commercials_json)
|
||||
.set({
|
||||
favoritesForUser: sql`array_remove(${commercials.favoritesForUser}, ${user.email})`,
|
||||
data: sql`jsonb_set(${commercials_json.data}, '{favoritesForUser}', array_remove((${commercials_json.data}->>'favoritesForUser')::jsonb, ${user.email}))`,
|
||||
})
|
||||
.where(sql`${commercials.id} = ${id}`);
|
||||
.where(eq(commercials_json.id, id));
|
||||
}
|
||||
// ##############################################################
|
||||
// States
|
||||
// ##############################################################
|
||||
// async getStates(): Promise<any[]> {
|
||||
// return await this.conn
|
||||
// .select({ state: commercials.state, count: sql<number>`count(${commercials.id})`.mapWith(Number) })
|
||||
// .from(commercials)
|
||||
// .groupBy(sql`${commercials.state}`)
|
||||
// .orderBy(sql`count desc`);
|
||||
// }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user