First version AI Search
This commit is contained in:
@@ -26,11 +26,17 @@ export class BusinessListingsController {
|
||||
}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@Post('search')
|
||||
@Post('find')
|
||||
find(@Request() req, @Body() criteria: ListingCriteria): any {
|
||||
return this.listingsService.findBusinessListings(criteria, req.user as JwtUser);
|
||||
}
|
||||
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@Post('search')
|
||||
search(@Request() req, @Body() criteria: ListingCriteria): any {
|
||||
return this.listingsService.searchBusinessListings(criteria.prompt);
|
||||
}
|
||||
|
||||
@Post()
|
||||
create(@Body() listing: any) {
|
||||
this.logger.info(`Save Listing`);
|
||||
|
||||
@@ -28,7 +28,7 @@ export class CommercialPropertyListingsController {
|
||||
return this.listingsService.findCommercialPropertiesByEmail(email, req.user as JwtUser);
|
||||
}
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
@Post('search')
|
||||
@Post('find')
|
||||
async find(@Request() req, @Body() criteria: ListingCriteria): Promise<any> {
|
||||
return await this.listingsService.findCommercialPropertyListings(criteria, req.user as JwtUser);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
|
||||
import { and, eq, gte, ilike, lte, ne, or, sql } from 'drizzle-orm';
|
||||
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
||||
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||
import OpenAI from 'openai';
|
||||
import { Logger } from 'winston';
|
||||
import * as schema from '../drizzle/schema.js';
|
||||
import { PG_CONNECTION, businesses, commercials } from '../drizzle/schema.js';
|
||||
@@ -11,11 +12,16 @@ import { JwtUser, ListingCriteria, emailToDirName } from '../models/main.model.j
|
||||
|
||||
@Injectable()
|
||||
export class ListingsService {
|
||||
openai: OpenAI;
|
||||
constructor(
|
||||
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
|
||||
@Inject(PG_CONNECTION) private conn: NodePgDatabase<typeof schema>,
|
||||
private fileService: FileService,
|
||||
) {}
|
||||
) {
|
||||
this.openai = new OpenAI({
|
||||
apiKey: process.env.OPENAI_API_KEY, // Stellen Sie sicher, dass Sie Ihren API-Key als Umgebungsvariable setzen
|
||||
});
|
||||
}
|
||||
private getConditions(criteria: ListingCriteria, table: typeof businesses | typeof commercials, user: JwtUser): any[] {
|
||||
const conditions = [];
|
||||
if (criteria.type) {
|
||||
@@ -42,6 +48,19 @@ export class ListingsService {
|
||||
// Listings general
|
||||
// ##############################################################
|
||||
|
||||
// #### Find by embeddng ########################################
|
||||
async searchBusinessListings(query: string, limit: number = 20): Promise<BusinessListing[]> {
|
||||
const queryEmbedding = await this.createEmbedding(query);
|
||||
|
||||
const results = await this.conn
|
||||
.select()
|
||||
.from(businesses)
|
||||
.orderBy(sql`embedding <-> ${JSON.stringify(queryEmbedding)}`)
|
||||
.limit(limit);
|
||||
|
||||
return results as BusinessListing[];
|
||||
}
|
||||
// #### Find by criteria ########################################
|
||||
async findCommercialPropertyListings(criteria: ListingCriteria, user: JwtUser): Promise<any> {
|
||||
const start = criteria.start ? criteria.start : 0;
|
||||
const length = criteria.length ? criteria.length : 12;
|
||||
@@ -86,6 +105,8 @@ export class ListingsService {
|
||||
]);
|
||||
return { total, data };
|
||||
}
|
||||
|
||||
// #### Find by ID ########################################
|
||||
async findCommercialPropertiesById(id: string, user: JwtUser): Promise<CommercialPropertyListing> {
|
||||
let result = await this.conn
|
||||
.select()
|
||||
@@ -102,6 +123,8 @@ export class ListingsService {
|
||||
result = result.filter(r => !r.draft || r.imageName === emailToDirName(user?.username) || user?.roles.includes('ADMIN'));
|
||||
return result[0] as BusinessListing;
|
||||
}
|
||||
|
||||
// #### Find by User EMail ########################################
|
||||
async findCommercialPropertiesByEmail(email: string, user: JwtUser): Promise<CommercialPropertyListing[]> {
|
||||
const conditions = [];
|
||||
conditions.push(eq(commercials.imagePath, emailToDirName(email)));
|
||||
@@ -124,6 +147,8 @@ export class ListingsService {
|
||||
.from(businesses)
|
||||
.where(and(...conditions))) as CommercialPropertyListing[];
|
||||
}
|
||||
|
||||
// #### Find by imagePath ########################################
|
||||
async findByImagePath(imagePath: string, serial: string): Promise<CommercialPropertyListing> {
|
||||
const result = await this.conn
|
||||
.select()
|
||||
@@ -131,13 +156,15 @@ export class ListingsService {
|
||||
.where(and(sql`${commercials.imagePath} = ${imagePath}`, sql`${commercials.serialId} = ${serial}`));
|
||||
return result[0] as CommercialPropertyListing;
|
||||
}
|
||||
|
||||
// #### CREATE ########################################
|
||||
async createListing(data: BusinessListing | CommercialPropertyListing, table: typeof businesses | typeof commercials): Promise<BusinessListing | CommercialPropertyListing> {
|
||||
data.created = new Date();
|
||||
data.updated = new Date();
|
||||
const [createdListing] = await this.conn.insert(table).values(data).returning();
|
||||
return createdListing as BusinessListing | CommercialPropertyListing;
|
||||
}
|
||||
|
||||
// #### UPDATE CommercialProps ########################################
|
||||
async updateCommercialPropertyListing(id: string, data: CommercialPropertyListing): Promise<BusinessListing | CommercialPropertyListing> {
|
||||
data.updated = new Date();
|
||||
data.created = new Date(data.created);
|
||||
@@ -150,22 +177,18 @@ export class ListingsService {
|
||||
const [updateListing] = await this.conn.update(commercials).set(data).where(eq(commercials.id, id)).returning();
|
||||
return updateListing as BusinessListing | CommercialPropertyListing;
|
||||
}
|
||||
// #### UPDATE Business ########################################
|
||||
async updateBusinessListing(id: string, data: BusinessListing): Promise<BusinessListing | CommercialPropertyListing> {
|
||||
data.updated = new Date();
|
||||
data.created = new Date(data.created);
|
||||
const [updateListing] = await this.conn.update(businesses).set(data).where(eq(businesses.id, id)).returning();
|
||||
return updateListing as BusinessListing | CommercialPropertyListing;
|
||||
}
|
||||
// #### DELETE ########################################
|
||||
async deleteListing(id: string, table: typeof businesses | typeof commercials): Promise<void> {
|
||||
await this.conn.delete(table).where(eq(table.id, id));
|
||||
}
|
||||
async getStates(table: typeof businesses | typeof commercials): Promise<any[]> {
|
||||
return await this.conn
|
||||
.select({ state: table.state, count: sql<number>`count(${table.id})`.mapWith(Number) })
|
||||
.from(table)
|
||||
.groupBy(sql`${table.state}`)
|
||||
.orderBy(sql`count desc`);
|
||||
}
|
||||
|
||||
// ##############################################################
|
||||
// Images for commercial Properties
|
||||
// ##############################################################
|
||||
@@ -182,4 +205,24 @@ export class ListingsService {
|
||||
listing.imageOrder.push(imagename);
|
||||
await this.updateCommercialPropertyListing(listing.id, listing);
|
||||
}
|
||||
// ##############################################################
|
||||
// States
|
||||
// ##############################################################
|
||||
async getStates(table: typeof businesses | typeof commercials): Promise<any[]> {
|
||||
return await this.conn
|
||||
.select({ state: table.state, count: sql<number>`count(${table.id})`.mapWith(Number) })
|
||||
.from(table)
|
||||
.groupBy(sql`${table.state}`)
|
||||
.orderBy(sql`count desc`);
|
||||
}
|
||||
// ##############################################################
|
||||
// Embedding
|
||||
// ##############################################################
|
||||
async createEmbedding(text: string): Promise<number[]> {
|
||||
const response = await this.openai.embeddings.create({
|
||||
model: 'text-embedding-3-small',
|
||||
input: text,
|
||||
});
|
||||
return response.data[0].embedding;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user