"use strict"; const db = require("../../models/index"); const sequelize = require("sequelize"); const Op = sequelize.Op; const { AD_CATEGORY } = require("../../common/enums"); const getSearchRequest = async searchRequestId => { try { return await db.SearchRequest.findByPk(searchRequestId); } catch (error) { console.log("searchrequest.js", error); return null; } }; const createSearchRequest = async (searchRequestFields = {}) => { return await db.SearchRequest.create(searchRequestFields); }; const findSearchRequestsForRealEstate = async realEstate => { const { price, area, adType, realEstateType, locationLat, locationLong, accessRoadType, balcony, newBuilding, elevator, gardenSize, numberOfRooms, numberOfFloors, floor } = realEstate; if (!locationLat || !locationLong) { return []; } const stGeometry = sequelize.fn( "ST_GEOMFROMTEXT", `POINT (${locationLong} ${locationLat})`, 4326 ); const areaToSearchColumn = sequelize.col("areaToSearch"); const contains = sequelize.fn("ST_Contains", areaToSearchColumn, stGeometry); const geoSearchQueryPart = sequelize.where(contains, true); //General query contains only attributes that are defined for every RealEstate - not null const query = { adType, realEstateType, subscribed: true, [Op.and]: geoSearchQueryPart }; //Needed for defining which attribute should exist or not const realEstateTypeObject = AD_CATEGORY[realEstateType]; //Needed to decide on including incomplete RealEstates data let checkForIncompleteWanted = false; //Attributes are checked separately and included in query only if defined - not null //Price and area should be defined for every property if (price != null) { query.priceMin = { [Op.lte]: price }; query.priceMax = { [Op.gte]: price }; } if (area != null) { query.sizeMin = { [Op.lte]: area }; query.sizeMax = { [Op.gte]: area }; } else { checkForIncompleteWanted = true; } //Other attributes can be defined or not depending on RealEstate type //we check what to include in query based on real estate type object if (realEstateTypeObject.hasGardenSize) { if (gardenSize != null) { query.gardenSizeMin = { [Op.lte]: gardenSize }; query.gardenSizeMax = { [Op.gte]: gardenSize }; } else { checkForIncompleteWanted = true; } } if (realEstateTypeObject.hasNumberOfRoom) { if (numberOfRooms != null) { query.numberOfRoomsMin = { [Op.lte]: numberOfRooms }; query.numberOfRoomsMax = { [Op.gte]: numberOfRooms }; } else { checkForIncompleteWanted = true; } } if (realEstateTypeObject.hasNumberOfFloors) { if (numberOfFloors != null) { query.numberOfFloorsMin = { [Op.lte]: numberOfFloors }; query.numberOfFloorsMax = { [Op.gte]: numberOfFloors }; } else { checkForIncompleteWanted = true; } } if (realEstateTypeObject.hasFloorProp) { if (floor != null) { query.floorMin = { [Op.lte]: floor }; query.floorMax = { [Op.gte]: floor }; } else { checkForIncompleteWanted = true; } } //AccessRoadType is defined - should exits for each ad and estate type if (accessRoadType != null) { query.accessRoadType = { [Op.or]: { [Op.eq]: "ANY", [Op.eq]: accessRoadType } }; } else if (realEstateTypeObject.hasAccesRoadType) { checkForIncompleteWanted = true; } if (realEstateTypeObject.hasBalconyProp) { if (balcony != null) { query.balcony = { [Op.eq]: balcony }; } else { checkForIncompleteWanted = true; } } if (realEstateTypeObject.hasNewBuildingProp) { if (newBuilding != null) { query.newBuilding = { [Op.eq]: newBuilding }; } else { checkForIncompleteWanted = true; } } if (realEstateTypeObject.hasElevatorProp) { if (elevator != null) { query.elevator = { [Op.eq]: elevator }; } else { checkForIncompleteWanted = true; } } //If one of the attributes that exists for property type is null //we include in query to check if incomplete real estates are accepted if (checkForIncompleteWanted) { query.includeIncompleteAds = { [Op.eq]: true }; } return await db.SearchRequest.findAll({ where: query }); }; module.exports = { getSearchRequest, createSearchRequest, findSearchRequestsForRealEstate };