From a1ab36334918567786e97a48078cfeda848fe699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Mart=C3=ADnez?= Date: Wed, 2 Oct 2024 00:57:28 +0200 Subject: [PATCH] Cleaning up andcreating mongo configs --- config/mongose.js | 9 ++ pages/index.vue | 98 ++++++++++-- pages/list.vue | 2 +- pages/parse.vue | 8 +- .../house/{construe.get.ts => parse.get.ts} | 8 +- .../command/house/construeHouseCommand.ts | 2 +- .../house/construeHouseCommandHandler.ts | 148 +----------------- server/db/mongo/index.js | 3 +- .../db/mongo/models/{Property.js => House.js} | 14 +- server/db/mysql/repository/HouseRepository.ts | 124 +++++++++++++-- 10 files changed, 222 insertions(+), 194 deletions(-) create mode 100644 config/mongose.js rename server/api/house/{construe.get.ts => parse.get.ts} (62%) rename server/db/mongo/models/{Property.js => House.js} (74%) diff --git a/config/mongose.js b/config/mongose.js new file mode 100644 index 0000000..74dde3b --- /dev/null +++ b/config/mongose.js @@ -0,0 +1,9 @@ +import dotenv from 'dotenv'; + +dotenv.config(); + +const mongoseConfig = { + mongodbUri: process.env.MONGODB_URI, +}; + +export default mongoseConfig; \ No newline at end of file diff --git a/pages/index.vue b/pages/index.vue index 070f582..4eb7c1c 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -1,25 +1,89 @@ + + diff --git a/pages/list.vue b/pages/list.vue index 56bad08..4eb7c1c 100644 --- a/pages/list.vue +++ b/pages/list.vue @@ -20,7 +20,7 @@ const properties = ref([]); const fetchProperties = async () => { try { - const response = await fetch("/api/house/construe", { + const response = await fetch("/api/house/parse", { method: "GET", }); const result = await response.json(); diff --git a/pages/parse.vue b/pages/parse.vue index fd4acc6..8cd522d 100644 --- a/pages/parse.vue +++ b/pages/parse.vue @@ -25,12 +25,8 @@ const parse = async () => { const initiateParse = async () => { try { - const response = await fetch("/api/parse", { - method: "POST", - body: JSON.stringify({ id: "searchtext", title: "Test" }), - headers: { - "Content-Type": "application/json", - }, + const response = await fetch("/api/house/parse", { + method: "GET", }); const result = await response.json(); diff --git a/server/api/house/construe.get.ts b/server/api/house/parse.get.ts similarity index 62% rename from server/api/house/construe.get.ts rename to server/api/house/parse.get.ts index 92c160e..4afadb0 100644 --- a/server/api/house/construe.get.ts +++ b/server/api/house/parse.get.ts @@ -1,14 +1,8 @@ -import { House } from "../../db/mysql/db.config"; -import { ConstrueHouseCommand } from "../../application/command/house/construeHouseCommand"; import { ConstrueHouseCommandHandler } from "../../application/commandHandler/house/construeHouseCommandHandler"; export default defineEventHandler(async (event) => { try { - const candidateHouses = await House.findAll(); - - const construeHouseCommandHandler = await ConstrueHouseCommandHandler.create( - ConstrueHouseCommand.create(candidateHouses) - ); + const construeHouseCommandHandler = await ConstrueHouseCommandHandler.create(); return { success: true, data: construeHouseCommandHandler }; } catch (error) { diff --git a/server/application/command/house/construeHouseCommand.ts b/server/application/command/house/construeHouseCommand.ts index 2b5afe3..d7d32f2 100644 --- a/server/application/command/house/construeHouseCommand.ts +++ b/server/application/command/house/construeHouseCommand.ts @@ -15,7 +15,7 @@ export class ConstrueHouseCommand { return construeHouseCommand; } - public rawHouseList() { + public get rawHouseList() { return this._rawHouseList; } diff --git a/server/application/commandHandler/house/construeHouseCommandHandler.ts b/server/application/commandHandler/house/construeHouseCommandHandler.ts index af4473c..edb1416 100644 --- a/server/application/commandHandler/house/construeHouseCommandHandler.ts +++ b/server/application/commandHandler/house/construeHouseCommandHandler.ts @@ -1,15 +1,15 @@ -import { Op, sequelize, House as Hou } from "../../../db/mysql/db.config"; +import { HouseRepository } from "~/server/db/mysql/repository/HouseRepository"; import { House } from "../../../domain/house"; -import type { ConstrueHouseCommand } from "../../command/house/construeHouseCommand"; export class ConstrueHouseCommandHandler { - private constructor() {} + public static async create() { + const houseRepository = new HouseRepository(); + const houseList = await houseRepository.findAll(); - public static async create(construeHouseCommand: ConstrueHouseCommand) { const houseCandidates: House[] = []; let i = 0; - construeHouseCommand.rawHouseList().forEach((houseCandidate: object) => { + houseList.forEach((houseCandidate: object) => { houseCandidates.push( House.create( `${++i}`, @@ -27,147 +27,11 @@ export class ConstrueHouseCommandHandler { )); }); - const proposedHouses = ConstrueHouseCommandHandler.findHouseByTitleAndExactPriceAndRoomsAndBaths(houseCandidates); + const proposedHouses = houseRepository.filterHouseByTitleAndExactPriceAndRoomsAndBaths(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; -} - } \ No newline at end of file diff --git a/server/db/mongo/index.js b/server/db/mongo/index.js index cd8776d..bfe38fd 100644 --- a/server/db/mongo/index.js +++ b/server/db/mongo/index.js @@ -1,7 +1,8 @@ import mongoose from 'mongoose'; +import mongoseConfig from '~/config/mongose'; export default async () => { - const config = useRuntimeConfig(); + const config = mongoseConfig; await mongoose.connect(config.mongodbUri, { useNewUrlParser: true, useUnifiedTopology: true }); diff --git a/server/db/mongo/models/Property.js b/server/db/mongo/models/House.js similarity index 74% rename from server/db/mongo/models/Property.js rename to server/db/mongo/models/House.js index b3bb4af..8e68ee1 100644 --- a/server/db/mongo/models/Property.js +++ b/server/db/mongo/models/House.js @@ -1,20 +1,20 @@ import mongoose from "mongoose"; -const propertySchema = new mongoose.Schema({ +const houseSchema = new mongoose.Schema({ id: { type: String, required: true, unique: true }, title: { type: String, required: true }, url: { type: String, required: true }, - price: { type: String, required: true }, - rooms: { type: Number, required: true }, + neighborhood: { type: String, required: true }, area: { type: Number, required: true }, - level: { type: String, required: true }, + rooms: { type: Number, required: true }, + price: { type: Number, required: true }, description: { type: String }, pic: { type: String }, baths: { type: Number, required: true }, - neighborhood: { type: String, required: true }, + level: { type: String, required: true }, phone: { type: String, required: true }, }); -const Property = mongoose.model("Property", propertySchema); +const House = mongoose.model("House", houseSchema); -export default Property; +export default House; diff --git a/server/db/mysql/repository/HouseRepository.ts b/server/db/mysql/repository/HouseRepository.ts index ffb75a3..f43d87a 100644 --- a/server/db/mysql/repository/HouseRepository.ts +++ b/server/db/mysql/repository/HouseRepository.ts @@ -1,18 +1,118 @@ -import { sequelize, Op, House } from "../db.config"; +import { Op, sequelize, House } from "../../../db/mysql/db.config"; -export async function findHouseByTitleAndExactPriceAndRoomsAndBaths(house: any) { - //const sanitizedTitle = house.title.replace(/'/g, "\\'"); +export class HouseRepository { + + public async findAll(): Promise { + return await House.findAll(); + } - return "pollas"; + public async filterHouseByTitleAndExactPriceAndRoomsAndBaths(houseCandidates: any): Promise { + const mergedHouses: any[] = []; - const matchingHouses = await House.findAll({ - where: { - price: house.price, - rooms: house.rooms, - baths: house.baths, - [Op.and]: sequelize.literal(`MATCH(title) AGAINST('${sanitizedTitle}' IN NATURAL LANGUAGE MODE)`) + for (let i = 0; i < houseCandidates.length; i++) { + const houseCandidate = houseCandidates[i]; + const sanitizedTitle = houseCandidate.title.replace(/'/g, "\\'"); + + const matchingHouses = await House.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 = this.mergeHouseProperties(matchingHouses); + + if (matchingHouses.length > 0) { + mergedHouses.push(resultHouse); + + houseCandidates = houseCandidates.filter((candidate: any) => { + return !matchingHouses.some((matchedHouse: any) => { + return ( + candidate.rawPrice === matchedHouse.price && + candidate.rooms === matchedHouse.rooms && + candidate.area === matchedHouse.area && + candidate.baths === matchedHouse.baths + ); + }); + }); + + i = -1; + } } - }); + + return mergedHouses; + } + + private mergeHouseProperties(houses: any[]): any { + if (!Array.isArray(houses)) { + throw new Error("El parámetro 'houses' debe ser un array."); + } + + const mergedHouse = { + //id: this.getLongestString(houses.map(h => h.id)) || Math.floor(Math.random() * 100000000).toString().padStart(8, '0'), + title: this.getLongestString(houses.map(h => h.title)), + url: this.joinByComma(houses.map(h => h.url)), + neighborhood: this.getLongestString(houses.map(h => h.neighborhood)), + area: houses[0]?.area || 0, + rooms: houses[0]?.rooms || 0, + price: houses[0]?.price || 0, + description: this.getLongestString(houses.map(h => h.description)), + pic: this.joinByComma(houses.map(h => h.pic)), + baths: houses[0]?.baths || 0, + level: this.getLongestString(houses.map(h => h.level)), + phone: this.joinByComma(houses.map(h => h.phone)) + }; + + return mergedHouse; + } + + private getLongestString(strings: string[]): string { + const preSelectedString = strings + .filter(str => str && !str.startsWith(']*>(.*?)<\/a>/g; + let match; + let result = ""; + + while ((match = anchorTagRegex.exec(htmlString)) !== null) { + const urlPart = match[1]; + const innerText = match[2].trim(); + + if (!innerText) { + result= this.snakeToSpaceCase(urlPart); + } else { + result = innerText; + } + } + + return result; + } + + private snakeToSpaceCase(snakeCaseString: string): string { + let spaceCaseString = snakeCaseString.replace(/[_-]/g, ' '); + + spaceCaseString = spaceCaseString.split(' ').map(word => { + return word.charAt(0).toUpperCase() + word.slice(1); + }).join(' '); + + return spaceCaseString; + } + - return matchingHouses; }