import { Op, sequelize, House as Hou } from "../../../db/mysql/db.config"; import { House } from "../../../domain/house"; import type { ConstrueHouseCommand } from "../../command/house/construeHouseCommand"; export class ConstrueHouseCommandHandler { private constructor() {} public static async create(construeHouseCommand: ConstrueHouseCommand) { const houseCandidates: House[] = []; let i = 0; construeHouseCommand.rawHouseList().forEach((houseCandidate: object) => { houseCandidates.push( House.create( `${++i}`, houseCandidate.title, houseCandidate.url, houseCandidate.neighborhood, houseCandidate.area, houseCandidate.rooms, houseCandidate.price, houseCandidate.description, houseCandidate.pic, houseCandidate.baths, houseCandidate.level, houseCandidate.phone )); }); const proposedHouses = ConstrueHouseCommandHandler.findHouseByTitleAndExactPriceAndRoomsAndBaths(houseCandidates); return proposedHouses; } async execute(): Promise { } private static async findHouseByTitleAndExactPriceAndRoomsAndBaths(houseCandidates: any): Promise { const mergedHouses: any[] = []; // Usamos un bucle for normal para manejar bien el await dentro del ciclo for (let i = 0; i < houseCandidates.length; i++) { const houseCandidate = houseCandidates[i]; const sanitizedTitle = houseCandidate.title.replace(/'/g, "\\'"); // Esperamos a que la búsqueda en la base de datos se resuelva const matchingHouses = await Hou.findAll({ where: { [Op.and]: [ { price: houseCandidate.rawPrice }, { rooms: houseCandidate.rooms }, { area: houseCandidate.area }, { baths: houseCandidate.baths }, sequelize.literal(`MATCH(title) AGAINST('${sanitizedTitle}' IN NATURAL LANGUAGE MODE)`) ] } }); const resultHouse = ConstrueHouseCommandHandler.mergeHouseProperties(matchingHouses); // Si hay coincidencias, añadimos los resultados al array de casas encontradas if (matchingHouses.length > 0) { mergedHouses.push(resultHouse); // Eliminamos todas las casas que coincidan con los resultados obtenidos houseCandidates = houseCandidates.filter((candidate: any) => { return !matchingHouses.some((matchedHouse: any) => { // Aquí puedes definir los criterios para eliminar la casa original. // Por ejemplo, comparando el ID, precio, habitaciones, etc. return ( candidate.rawPrice === matchedHouse.price && candidate.rooms === matchedHouse.rooms && candidate.area === matchedHouse.area && candidate.baths === matchedHouse.baths ); }); }); // Como hemos modificado el array, restamos 1 al índice i = -1; // Reiniciamos el índice para volver a iterar el array actualizado } } return mergedHouses; // const sanitizedTitle = house.title.replace(/'/g, "\\'"); // const matchingHouses = Hou.findAll({ // where: { // [Op.and]: [ // {price: house.rawPrice}, // {rooms: house.rooms}, // {area: house.area}, // {baths: house.baths}, // sequelize.literal(`MATCH(title) AGAINST('${sanitizedTitle}' IN NATURAL LANGUAGE MODE)`) // ] // } // }); // return matchingHouses; } private static mergeHouseProperties(houses: any[]): any { if (!Array.isArray(houses)) { throw new Error("El parámetro 'houses' debe ser un array."); } const mergedHouse = { id: ConstrueHouseCommandHandler.getLongestString(houses.map(h => h.id)) || Math.floor(Math.random() * 100000000).toString().padStart(8, '0'), title: ConstrueHouseCommandHandler.getLongestString(houses.map(h => h.title)), url: ConstrueHouseCommandHandler.joinByComma(houses.map(h => h.url)), neighborhood: ConstrueHouseCommandHandler.getLongestString(houses.map(h => h.neighborhood)), area: houses[0]?.area || 0, // Asumo que el área es la misma en todos los duplicados rooms: houses[0]?.rooms || 0, // Asumo que el número de habitaciones es el mismo price: houses[0]?.price || 0, // Asumo que el precio es el mismo description: ConstrueHouseCommandHandler.getLongestString(houses.map(h => h.description)), pic: ConstrueHouseCommandHandler.joinByComma(houses.map(h => h.pic)), baths: houses[0]?.baths || 0, // Asumo que el número de baños es el mismo level: ConstrueHouseCommandHandler.getLongestString(houses.map(h => h.level)), phone: ConstrueHouseCommandHandler.joinByComma(houses.map(h => h.phone)) // Concatenar los teléfonos }; return mergedHouse; } private static getLongestString(strings: string[]): string { const preSelectedString = strings .filter(str => str && !str.startsWith(' con el href y el contenido interno const anchorTagRegex = /]*>(.*?)<\/a>/g; let match; let result = ""; while ((match = anchorTagRegex.exec(htmlString)) !== null) { let urlPart = match[1]; // La parte que queremos desde el href let innerText = match[2].trim(); // El texto dentro de las etiquetas // Si innerText está vacío, tomamos la parte de la URL if (!innerText) { result= ConstrueHouseCommandHandler.snakeToSpaceCase(urlPart); } else { result = innerText; } } return result; } private static snakeToSpaceCase(snakeCaseString) { // Reemplazar tanto los guiones bajos (_) como los guiones medios (-) por espacios let spaceCaseString = snakeCaseString.replace(/[_-]/g, ' '); // Capitalizar la primera letra de cada palabra spaceCaseString = spaceCaseString.split(' ').map(word => { return word.charAt(0).toUpperCase() + word.slice(1); }).join(' '); return spaceCaseString; } }