207 lines
10 KiB
TypeScript
207 lines
10 KiB
TypeScript
import { Inject, Injectable } from '@nestjs/common';
|
|
import {
|
|
BusinessListing,
|
|
CommercialPropertyListing,
|
|
ListingCriteria,
|
|
ListingType,
|
|
ImageProperty,
|
|
ListingCategory,
|
|
ResponseBusinessListing
|
|
} from '../models/main.model.js';
|
|
import { convertStringToNullUndefined } from '../utils.js';
|
|
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
|
import { Logger } from 'winston';
|
|
import { EntityData, EntityId, Schema, SchemaDefinition } from 'redis-om';
|
|
import { SQL, eq, ilike, sql } from 'drizzle-orm';
|
|
import { BusinessesJson, PG_CONNECTION, businesses_json, commercials_json } from '../drizzle/schema.js';
|
|
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
|
import * as schema from '../drizzle/schema.js';
|
|
import { PgTableFn, PgTableWithColumns, QueryBuilder } from 'drizzle-orm/pg-core';
|
|
|
|
@Injectable()
|
|
export class ListingsService {
|
|
|
|
constructor(@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
|
|
@Inject(PG_CONNECTION) private conn: NodePgDatabase<typeof schema>,) {
|
|
}
|
|
private buildWhereClause(criteria: ListingCriteria):SQL {
|
|
const finalSql = sql`1=1`;
|
|
finalSql.append(criteria.type?sql` AND data->>'type' = ${criteria.type}` : sql``)
|
|
finalSql.append(criteria.state ? sql` AND data->>'state' = ${criteria.state}` : sql``)
|
|
finalSql.append(criteria.minPrice ? sql` AND CAST(data->>'price' AS NUMERIC) >= ${parseFloat(criteria.minPrice)}` : sql``)
|
|
finalSql.append(criteria.maxPrice ? sql` AND CAST(data->>'price' AS NUMERIC) < ${parseFloat(criteria.maxPrice)}` : sql``)
|
|
finalSql.append(criteria.realEstateChecked !== undefined ? sql` AND CAST(data->>'realEstateIncluded' AS BOOLEAN) = ${criteria.realEstateChecked}` : sql``)
|
|
finalSql.append(criteria.title ? sql` AND LOWER(data->>'title') LIKE LOWER('%' || ${criteria.title} || '%')` : sql``)
|
|
|
|
return finalSql
|
|
}
|
|
// ##############################################################
|
|
// Listings general
|
|
// ##############################################################
|
|
private async findListings(table: typeof businesses_json | typeof commercials_json, criteria: ListingCriteria ,start = 0, length = 12): Promise<any> {
|
|
const whereClause = this.buildWhereClause(criteria)
|
|
const [data, total] = await Promise.all([
|
|
(await this.conn.select({ id:table.id, data: table.data }).from(table).where(whereClause).offset(start).limit(length)).map(e=>{
|
|
const ret = e.data as any
|
|
ret.id = e.id
|
|
return ret
|
|
}),
|
|
this.conn.select({ count: sql`count(*)` }).from(table).where(whereClause).then((result) => Number(result[0].count)),
|
|
]);
|
|
return { total, data };
|
|
}
|
|
async findById(id: string, table: typeof businesses_json | typeof commercials_json): Promise<BusinessListing|CommercialPropertyListing> {
|
|
const result = await this.conn.select({ data: table.data }).from(table).where(sql`${table.id} = ${id}`)
|
|
return result[0].data as BusinessListing
|
|
}
|
|
|
|
async findListingsByCriteria(criteria: ListingCriteria, table: typeof businesses_json | typeof commercials_json): Promise<{ data: Record<string, any>[]; total: number }> {
|
|
const start = criteria.start ? criteria.start : 0;
|
|
const length = criteria.length ? criteria.length : 12;
|
|
return await this.findListings(table, criteria, start, length)
|
|
|
|
}
|
|
async findByPriceRange(minPrice: number, maxPrice: number, table: typeof businesses_json | typeof commercials_json): Promise<BusinessesJson[]> {
|
|
return this.conn.select().from(table).where(sql`${table.data}->>'price' BETWEEN ${minPrice} AND ${maxPrice}`);
|
|
}
|
|
|
|
async findByState(state: string, table: typeof businesses_json | typeof commercials_json): Promise<BusinessesJson[]> {
|
|
return this.conn.select().from(table).where(sql`${table.data}->>'state' = ${state}`);
|
|
}
|
|
|
|
async findByUserId(userId: string, table: typeof businesses_json | typeof commercials_json): Promise<BusinessesJson[]> {
|
|
return this.conn.select().from(table).where(sql`${table.data}->>'userId' = ${userId}`);
|
|
}
|
|
async findByTitleContains(title: string, table: typeof businesses_json | typeof commercials_json): Promise<BusinessesJson[]> {
|
|
return this.conn.select().from(table).where(sql`${table.data}->>'title' ILIKE '%' || ${title} || '%'`);
|
|
}
|
|
async createListing(data: BusinessListing, table: typeof businesses_json | typeof commercials_json): Promise<BusinessesJson> {
|
|
const newListing = { data, created: data.created, updated: data.updated, visits: 0, last_visit: null }
|
|
const [createdListing] = await this.conn.insert(table).values(newListing).returning();
|
|
return createdListing as BusinessesJson;
|
|
}
|
|
|
|
async updateListing(id: string, data: BusinessListing | CommercialPropertyListing, table: typeof businesses_json | typeof commercials_json): Promise<BusinessesJson> {
|
|
const [updateListing] = await this.conn.update(table).set(data).where(eq(table.id, id)).returning();
|
|
return updateListing as BusinessesJson;
|
|
}
|
|
|
|
async deleteListing(id: string, table: typeof businesses_json | typeof commercials_json): Promise<void> {
|
|
await this.conn.delete(table).where(eq(table.id, id));
|
|
}
|
|
|
|
// ##############################################################
|
|
// Images for commercial Properties
|
|
// ##############################################################
|
|
|
|
async updateImageOrder(id: string, imageOrder: ImageProperty[]) {
|
|
const listing = await this.findById(id, commercials_json) as unknown as CommercialPropertyListing
|
|
listing.imageOrder = imageOrder;
|
|
await this.updateListing(listing.id, listing, commercials_json)
|
|
}
|
|
async deleteImage(id: string, name: string,) {
|
|
const listing = await this.findById(id, commercials_json) as unknown as CommercialPropertyListing
|
|
const index = listing.imageOrder.findIndex(im => im.name === name);
|
|
if (index > -1) {
|
|
listing.imageOrder.splice(index, 1);
|
|
await this.updateListing(listing.id, listing, commercials_json)
|
|
}
|
|
}
|
|
async addImage(id: string, imagename: string) {
|
|
const listing = await this.findById(id, commercials_json) as unknown as CommercialPropertyListing
|
|
listing.imageOrder.push({ name: imagename, code: '', id: '' });
|
|
await this.updateListing(listing.id, listing, commercials_json)
|
|
}
|
|
// async getCommercialPropertyListingById(id: string): Promise<CommercialPropertyListing>{
|
|
// return await this.commercialPropertyListingRepository.fetch(id) as unknown as CommercialPropertyListing;
|
|
// }
|
|
// async getBusinessListingById(id: string) {
|
|
// return await this.businessListingRepository.fetch(id)
|
|
// }
|
|
// async getBusinessListingByUserId(userid:string){
|
|
// return await this.businessListingRepository.search().where('userId').equals(userid).return.all()
|
|
// }
|
|
// async deleteBusinessListing(id: string){
|
|
// return await this.businessListingRepository.remove(id);
|
|
// }
|
|
// async deleteCommercialPropertyListing(id: string){
|
|
// return await this.commercialPropertyListingRepository.remove(id);
|
|
// }
|
|
// async getAllBusinessListings(start?: number, end?: number) {
|
|
// return await this.businessListingRepository.search().return.all()
|
|
// }
|
|
// async getAllCommercialListings(start?: number, end?: number) {
|
|
// return await this.commercialPropertyListingRepository.search().return.all()
|
|
// }
|
|
// async findBusinessListings(criteria:ListingCriteria): Promise<any> {
|
|
// // let listings = await this.getAllBusinessListings();
|
|
// // return this.find(criteria,listings);
|
|
// const from=criteria.start?criteria.start:0
|
|
// const size=criteria.length?criteria.length:24
|
|
// this.logger.info(`start findBusinessListings: ${JSON.stringify(criteria)}`);
|
|
// const result = await this.redis.ft.search('business:index','*',{LIMIT:{from,size}});
|
|
// this.logger.info(`start findBusinessListings: ${JSON.stringify(criteria)}`);
|
|
// return result
|
|
// }
|
|
// async findCommercialPropertyListings(criteria:ListingCriteria): Promise<any> {
|
|
// let listings = await this.getAllCommercialListings();
|
|
// return this.find(criteria,listings);
|
|
// }
|
|
// async deleteAllBusinessListings(){
|
|
// const ids = await this.getIdsForRepo(this.schemaNameBusiness.name);
|
|
// this.businessListingRepository.remove(ids);
|
|
// }
|
|
// async deleteAllcommercialListings(){
|
|
// const ids = await this.getIdsForRepo(this.schemaNameCommercial.name);
|
|
// this.commercialPropertyListingRepository.remove(ids);
|
|
// }
|
|
// async getIdsForRepo(repoName:string, maxcount=100000){
|
|
// let cursor = 0;
|
|
// let ids = [];
|
|
// do {
|
|
// const reply = await this.redis.scan(cursor, {
|
|
// MATCH: `${repoName}:*`,
|
|
// COUNT: maxcount
|
|
// });
|
|
// cursor = reply.cursor;
|
|
// // Extrahiere die ID aus jedem Schlüssel und füge sie zur Liste hinzu
|
|
// ids = ids.concat(reply.keys.map(key => key.split(':')[1]).filter(id=>id!='index'));
|
|
// } while (cursor !== 0);
|
|
// return ids;
|
|
// }
|
|
|
|
// async find(criteria:ListingCriteria, listings: any[]): Promise<any> {
|
|
// listings=listings.filter(l=>l.listingsCategory===criteria.listingsCategory);
|
|
// if (convertStringToNullUndefined(criteria.type)){
|
|
// console.log(criteria.type);
|
|
// listings=listings.filter(l=>l.type===criteria.type);
|
|
// }
|
|
// if (convertStringToNullUndefined(criteria.state)){
|
|
// console.log(criteria.state);
|
|
// listings=listings.filter(l=>l.state===criteria.state);
|
|
// }
|
|
// if (convertStringToNullUndefined(criteria.minPrice)){
|
|
// console.log(criteria.minPrice);
|
|
// listings=listings.filter(l=>l.price>=Number(criteria.minPrice));
|
|
// }
|
|
// if (convertStringToNullUndefined(criteria.maxPrice)){
|
|
// console.log(criteria.maxPrice);
|
|
// listings=listings.filter(l=>l.price<=Number(criteria.maxPrice));
|
|
// }
|
|
// if (convertStringToNullUndefined(criteria.realEstateChecked)){
|
|
// console.log(criteria.realEstateChecked);
|
|
// listings=listings.filter(l=>l.realEstateIncluded);
|
|
// }
|
|
// if (convertStringToNullUndefined(criteria.category)){
|
|
// console.log(criteria.category);
|
|
// listings=listings.filter(l=>l.category===criteria.category);
|
|
// }
|
|
// return listings
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
}
|