354 lines
8.4 KiB
JavaScript
354 lines
8.4 KiB
JavaScript
"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;
|
|
|
|
//Debugging log
|
|
console.log("New realestate:", realEstate.dataValues);
|
|
|
|
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);
|
|
|
|
//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 to make different query parts
|
|
|
|
//If real estate price is number then it searches for req that have priceMin and priceMax
|
|
//If real estate price is null it searches for req that accept ads without price
|
|
//User always defines price and area (sliders) - not null in search req
|
|
let priceQuery = {};
|
|
if (price != null) {
|
|
priceQuery = {
|
|
[Op.and]: [
|
|
{
|
|
priceMin: {
|
|
[Op.lte]: price
|
|
}
|
|
},
|
|
{
|
|
priceMax: {
|
|
[Op.gte]: price
|
|
}
|
|
}
|
|
]
|
|
};
|
|
} else {
|
|
priceQuery = {
|
|
includeWithoutPrice: {
|
|
[Op.eq]: true
|
|
}
|
|
};
|
|
}
|
|
|
|
let areaQuery = {};
|
|
if (area != null) {
|
|
areaQuery = {
|
|
[Op.and]: [
|
|
{
|
|
sizeMin: {
|
|
[Op.lte]: area
|
|
}
|
|
},
|
|
{
|
|
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
|
|
let gardenSizeQuery = {};
|
|
if (realEstateTypeObject.hasGardenSize) {
|
|
if (gardenSize != null) {
|
|
gardenSizeQuery = {
|
|
[Op.and]: [
|
|
{
|
|
gardenSizeMin: {
|
|
[Op.lte]: gardenSize
|
|
}
|
|
},
|
|
{
|
|
gardenSizeMax: {
|
|
[Op.gte]: gardenSize
|
|
}
|
|
}
|
|
]
|
|
};
|
|
} else {
|
|
checkForIncompleteWanted = true;
|
|
}
|
|
}
|
|
|
|
let numberOfRoomsQuery = {};
|
|
if (realEstateTypeObject.hasNumberOfRoom) {
|
|
if (numberOfRooms != null) {
|
|
//If real estate has defined number of rooms ex. 3 it returns req
|
|
// that accepts 3 rooms or ones that don't have defined number - null
|
|
//Ex. they didnt choose advanced filters at all
|
|
numberOfRoomsQuery = {
|
|
[Op.and]: [
|
|
{
|
|
numberOfRoomsMin: {
|
|
[Op.or]: {
|
|
[Op.lte]: numberOfRooms,
|
|
[Op.is]: null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
numberOfRoomsMax: {
|
|
[Op.or]: {
|
|
[Op.gte]: numberOfRooms,
|
|
[Op.is]: null
|
|
}
|
|
}
|
|
}
|
|
]
|
|
};
|
|
} else {
|
|
// If real estate dont have defined number of rooms ex. null
|
|
//It returns all search requests except for ones that dont want incpomlete ads
|
|
numberOfRoomsQuery = {
|
|
includeIncompleteAds: {
|
|
[Op.ne]: false
|
|
}
|
|
};
|
|
}
|
|
}
|
|
//Same logic for number of Floors and floors
|
|
let numberOfFloorsQuery = {};
|
|
if (realEstateTypeObject.hasNumberOfFloors) {
|
|
if (numberOfFloors != null) {
|
|
numberOfFloorsQuery = {
|
|
[Op.and]: [
|
|
{
|
|
numberOfFloorsMin: {
|
|
[Op.or]: {
|
|
[Op.lte]: numberOfFloors,
|
|
[Op.is]: null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
numberOfFloorsMax: {
|
|
[Op.or]: {
|
|
[Op.gte]: numberOfFloors,
|
|
[Op.is]: null
|
|
}
|
|
}
|
|
}
|
|
]
|
|
};
|
|
} else {
|
|
numberOfFloorsQuery = {
|
|
includeIncompleteAds: {
|
|
[Op.ne]: false
|
|
}
|
|
};
|
|
}
|
|
}
|
|
let floorQuery = {};
|
|
if (realEstateTypeObject.hasFloorProp) {
|
|
if (floor != null) {
|
|
floorQuery = {
|
|
[Op.and]: [
|
|
{
|
|
floorMin: {
|
|
[Op.or]: {
|
|
[Op.lte]: floor,
|
|
[Op.is]: null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
floorMax: {
|
|
[Op.or]: {
|
|
[Op.gte]: floor,
|
|
[Op.is]: null
|
|
}
|
|
}
|
|
}
|
|
]
|
|
};
|
|
} else {
|
|
floorQuery = {
|
|
includeIncompleteAds: {
|
|
[Op.ne]: false
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
//Logic for balcony, newBuilding and elevator
|
|
//If user dont check checkbox for ex. elevator it does not mean he only wants no elevator
|
|
//If real estate characteristic =true find all req, one that wants charachertistic or dont care - dont need query
|
|
//If real estate characteristic = false, find all req exept for ones that wants characteristic to be true
|
|
//If real estate characteristic = null, dont know if true or false, find all req except ones that dont want incomplete ads
|
|
let balconyQuery = {};
|
|
if (realEstateTypeObject.hasBalconyProp && balcony !== true) {
|
|
if (balcony === false) {
|
|
balconyQuery = {
|
|
balcony: {
|
|
[Op.ne]: true
|
|
}
|
|
};
|
|
} else if (balcony === null) {
|
|
balconyQuery = {
|
|
includeIncompleteAds: {
|
|
[Op.ne]: false
|
|
}
|
|
};
|
|
}
|
|
}
|
|
let newBuildingQuery = {};
|
|
if (realEstateTypeObject.hasNewBuildingProp && newBuilding !== true) {
|
|
if (newBuilding === false) {
|
|
newBuildingQuery = {
|
|
newBuilding: {
|
|
[Op.ne]: true
|
|
}
|
|
};
|
|
} else if (newBuilding === null) {
|
|
newBuildingQuery = {
|
|
includeIncompleteAds: {
|
|
[Op.ne]: false
|
|
}
|
|
};
|
|
}
|
|
}
|
|
let elevatorQuery = {};
|
|
if (realEstateTypeObject.hasElevatorProp && elevator !== true) {
|
|
if (elevator === false) {
|
|
elevatorQuery = {
|
|
elevator: {
|
|
[Op.ne]: true
|
|
}
|
|
};
|
|
} else if (elevator === null) {
|
|
elevatorQuery = {
|
|
includeIncompleteAds: {
|
|
[Op.ne]: false
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
//General query consists of each individual query
|
|
const query = {
|
|
adType,
|
|
realEstateType,
|
|
subscribed: true,
|
|
[Op.and]: [
|
|
geoSearchQueryPart,
|
|
priceQuery,
|
|
areaQuery,
|
|
gardenSizeQuery,
|
|
numberOfRoomsQuery,
|
|
numberOfFloorsQuery,
|
|
floorQuery,
|
|
balconyQuery,
|
|
newBuildingQuery,
|
|
elevatorQuery
|
|
]
|
|
};
|
|
|
|
//AccessRoadType is defined - should exists for each ad and estate type
|
|
if (accessRoadType != null) {
|
|
query.accessRoadType = {
|
|
[Op.or]: {
|
|
[Op.like]: "ANY",
|
|
[Op.eq]: accessRoadType
|
|
}
|
|
};
|
|
} else {
|
|
//Null values are returned for user request that wanted ANY acces road type
|
|
query.accessRoadType = {
|
|
[Op.eq]: "ANY"
|
|
};
|
|
}
|
|
//Tag to check if incomplete ads are accepted in query which is default
|
|
if (checkForIncompleteWanted) {
|
|
query.includeIncompleteAds = {
|
|
[Op.or]: {
|
|
[Op.eq]: true,
|
|
[Op.is]: null
|
|
}
|
|
};
|
|
}
|
|
|
|
//Debugging logs
|
|
console.log("Query for search of req:", query);
|
|
console.log("Price", priceQuery);
|
|
console.log("Area", areaQuery);
|
|
console.log("Garden", gardenSizeQuery);
|
|
console.log("Number of rooms", numberOfRoomsQuery);
|
|
console.log("Number offloors", numberOfFloorsQuery);
|
|
console.log("Floor", floorQuery);
|
|
console.log("Balcony", balconyQuery);
|
|
console.log("New building", newBuildingQuery);
|
|
console.log("Elevator", elevatorQuery);
|
|
//
|
|
|
|
return await db.SearchRequest.findAll({
|
|
where: query
|
|
});
|
|
};
|
|
|
|
module.exports = {
|
|
getSearchRequest,
|
|
createSearchRequest,
|
|
findSearchRequestsForRealEstate
|
|
};
|