"use strict"; const db = require("../../models/index"); const sequelize = require("sequelize"); const Op = sequelize.Op; const bulkUpsertRealEstates = async realEstateData => { try { const fieldsToUpdateIfDuplicate = [ "realEstateType", "adType", "price", "area", "streetNumber", "streetName", "locality", "municipality", "city", "region", "entity", "country", "locationLat", "locationLong", "title", "shortDescription", "longDescription", "gardenSize", "adStatus", "updatedAt", "renewedDate", "numberOfRooms", "numberOfFloors", "floor", "accessRoadType", "heatingType", "furnishingType", "balcony", "newBuilding", "elevator", "water", "electricity", "drainageSystem", "registeredInZkBooks", "recentlyAdapted", "parking", "garage", "gas", "antiTheftDoor", "airCondition", "phoneConnection", "cableTV", "internet", "basementAttic", "storeRoom", "videoSurveillance", "alarm", "suitableForStudents", "includingBills", "animalsAllowed", "pool", "urbanPlanPermit", "buildingPermit", "utilityConnection", "distanceToRiver", "numberOfViewsAgency" ]; const order = [["updatedAt", "desc"]]; return await db.RealEstate.bulkCreate(realEstateData, { updateOnDuplicate: fieldsToUpdateIfDuplicate, returning: true, order }); } catch (e) { console.log("Error bulk upserting realEstates : ", e); } }; const getRealEstateById = async id => { return db.RealEstate.findByPk(id); }; const findRealEstatesForSearchRequest = async (searchRequest, maxResults) => { const { priceMin, priceMax, sizeMin, sizeMax, adType, realEstateType, areaToSearch, gardenSizeMin, gardenSizeMax, numberOfRoomsMin, numberOfRoomsMax, numberOfFloorsMin, numberOfFloorsMax, floorMin, floorMax, includeIncompleteAds, balcony, elevator, newBuilding, accessRoadType } = searchRequest; const longitudeColumn = sequelize.col("locationLong"); const latitudeColumn = sequelize.col("locationLat"); const pointGeometry = sequelize.fn( "ST_Point", longitudeColumn, latitudeColumn ); const pointWithSRID = sequelize.fn("ST_SetSRID", pointGeometry, 4326); const areaToSearchAsGeometry = sequelize.fn( "ST_GeomFromGeoJSON", JSON.stringify(areaToSearch) ); const areaToSearchWithSRID = sequelize.fn( "ST_SetSRID", areaToSearchAsGeometry, 4326 ); const contains = sequelize.fn( "ST_Contains", areaToSearchWithSRID, pointWithSRID ); const geoSearchQueryPart = sequelize.where(contains, true); //General queries contain only attributes that are defined for every searchreq //Query for case of complete ads const query = { adType, realEstateType, price: { [Op.or]: { [Op.and]: { [Op.lte]: priceMax, [Op.gte]: priceMin }, [Op.is]: null } }, area: { [Op.lte]: sizeMax, [Op.gte]: sizeMin }, [Op.and]: geoSearchQueryPart }; //Query for case of incomplete ads const queryIncludeIncomplete = { adType, realEstateType, price: { [Op.or]: { [Op.and]: { [Op.lte]: priceMax, [Op.gte]: priceMin }, [Op.is]: null } }, area: { [Op.or]: { [Op.and]: { [Op.lte]: sizeMax, [Op.gte]: sizeMin }, [Op.is]: null } }, [Op.and]: geoSearchQueryPart }; //Every other attribute is checked separately and included in query only if it is defined if (gardenSizeMax && gardenSizeMin) { query.gardenSize = { [Op.lte]: gardenSizeMax, [Op.gte]: gardenSizeMin }; queryIncludeIncomplete.gardenSize = { [Op.or]: { [Op.and]: { [Op.lte]: gardenSizeMax, [Op.gte]: gardenSizeMin }, [Op.is]: null } }; } if (numberOfRoomsMin && numberOfRoomsMax) { query.numberOfRooms = { [Op.lte]: numberOfRoomsMax, [Op.gte]: numberOfRoomsMin }; queryIncludeIncomplete.numberOfRooms = { [Op.or]: { [Op.and]: { [Op.lte]: numberOfRoomsMax, [Op.gte]: numberOfRoomsMin }, [Op.is]: null } }; } if (numberOfFloorsMin && numberOfFloorsMax) { query.numberOfFloors = { [Op.lte]: numberOfFloorsMax, [Op.gte]: numberOfFloorsMin }; queryIncludeIncomplete.numberOfFloors = { [Op.or]: { [Op.and]: { [Op.lte]: numberOfFloorsMax, [Op.gte]: numberOfFloorsMin }, [Op.is]: null } }; } if (floorMin && floorMax) { query.floor = { [Op.lte]: floorMax, [Op.gte]: floorMin }; queryIncludeIncomplete.floor = { [Op.or]: { [Op.and]: { [Op.lte]: floorMax, [Op.gte]: floorMin }, [Op.is]: null } }; } if (balcony) { query.balcony = { [Op.eq]: balcony }; queryIncludeIncomplete.balcony = { [Op.or]: { [Op.eq]: balcony, [Op.is]: null } }; } if (newBuilding) { query.newBuilding = { [Op.eq]: newBuilding }; queryIncludeIncomplete.newBuilding = { [Op.or]: { [Op.eq]: newBuilding, [Op.is]: null } }; } if (elevator) { query.elevator = { [Op.eq]: elevator }; queryIncludeIncomplete.elevator = { [Op.or]: { [Op.eq]: elevator, [Op.is]: null } }; } if (accessRoadType !== "ANY") { query.accessRoadType = { [Op.eq]: accessRoadType }; queryIncludeIncomplete.accessRoadType = { [Op.or]: { [Op.eq]: accessRoadType, [Op.is]: null } }; } const order = [["updatedAt", "desc"]]; return db.RealEstate.findAll({ where: includeIncompleteAds ? queryIncludeIncomplete : query, limit: maxResults, order }); }; module.exports = { bulkUpsertRealEstates, getRealEstateById, findRealEstatesForSearchRequest };