Context: I am trying to code the back end of an "Advanced Search" option It is a project I'm coding to practice JS, MongoDB(Mongoose), Next, Node, Etc.
The Schema I'm using is the following:
House Schema:{
name:String,
address:{
street:String
city:String,
state:String
},
operations:{
rent:Boolean,
shortRent:Boolean,
purchase:Boolean,
agentRentPrice:Number,
agentShortRentPrice:Number,
agentSellingPrice:Number},
features:{
bathrooms:Number,
bedrooms:Number,
beds:Number,
amenities:[String],
},
}
Now, the FrontEnd sends the following info (in req.body):
query = {
name: null,
address: { city: null, state: null },
operations: {
rentAvailable: false,
purchaseAvailable: false,
shortRentAvailable: false,
agentRentPriceMin: null,
agentSellingPriceMin: null,
agentShortRentPriceMin: null,
agentRentPriceMax: null,
agentSellingPriceMax: null,
agentShortRentPriceMax: null,
},
features: {
bathroomsMin: null,
dormsMin: null,
bedsMin: null,
amenities: null,
},
}
Of course, those "null" values will be replaced with numbers or strings with the parameters introduced by the user.
With this object, I then declare another:
let queryObj = {
name: query.name,
address: { city: query.address.city, state: query.address.state },
operations: {
rentAvailable: query.operations.rentAvailable,
purchaseAvailable: query.operations.purchaseAvailable,
shortRentAvailable: query.operations.shortRentAvailable,
agentRentPrice: {
$gte: query.operations.agentRentPriceMin,
$lte: query.operations.agentRentPriceMax,
},
agentSellingPrice: {
$gte: query.operations.agentSellingPriceMin,
$lte: query.operations.agentSellingPriceMax,
},
agentShortRentPrice: {
$gte: query.operations.agentShortRentPriceMin,
$lte: query.operations.agentShortRentPriceMax,
},
},
features: {
bathrooms: { $gte: query.features.bathroomsMin },
dorms: { $gte: query.features.dormsMin },
beds: { $gte: query.features.bedsMin },
amenities: { $in: query.features.amenities },
},
}
Finally I reduce this object, removing any "null", "false", "" and "{}" values.
For example, the user searches for: house available for renting, in New York, with 2 bedrooms, with a pool, and max Rent price of $10.000
So, req.body.query will be
{
name: null,
address: { city: "New York", state: null },
operations: {
rentAvailable: true,
purchaseAvailable: false,
shortRentAvailable: false,
agentRentPriceMin: null,
agentSellingPriceMin: null,
agentShortRentPriceMin: null,
agentRentPriceMax: 10000,
agentSellingPriceMax: null,
agentShortRentPriceMax: null,
},
features: {
bathroomsMin: null,
dormsMin: 2,
bedsMin: null,
amenities: ["pool"],
},
}
Next, I declare
let queryObj = {
name: null,
address: { city: "New York", state: null },
operations: {
rentAvailable: true,
purchaseAvailable: false,
shortRentAvailable: false,
agentRentPrice: {
$gte: null,
$lte: 10000,
},
agentSellingPrice: {
$gte: null,
$lte: null,
},
agentShortRentPrice: {
$gte: null,
$lte: null,
},
},
features: {
bathrooms: { $gte: null },
dorms: { $gte: 2 },
beds: { $gte: null },
amenities: { $in: ["pool"] },
},
};
I have a function here that reduces this object (removing "false" "null" "" and "{}" values):
queryObj = {
address: { city: "New York" },
operations: {
rentAvailable: true,
agentRentPrice: {
$lte: 10000,
},
},
features: {
dorms: { $gte: 2 },
amenities: { $in: ["pool"] },
},
};
As you can see, "query" (and therefore "queryObj") will vary a lot; the user may or may not use any of the available search parameters, so (as i see it) it is not possible to "hard-code" the queryObj structure
I've tried using
Home.aggregate([{$match:queryObj}])
without success (returns no results).
Is it possible what I'm trying to do?
Try to use a function to build the filter:
const body = {
name: null,
address: { city: 'New York', state: null },
operations: {
rentAvailable: true,
purchaseAvailable: false,
shortRentAvailable: false,
agentRentPriceMin: null,
agentSellingPriceMin: null,
agentShortRentPriceMin: null,
agentRentPriceMax: 10000,
agentSellingPriceMax: null,
agentShortRentPriceMax: null,
},
features: {
bathroomsMin: null,
dormsMin: 2,
bedsMin: null,
amenities: ['pool'],
},
};
const buildFilter = (body) => {
const filter = {};
if (body.name != null) {
filter['name'] = body.name;
}
if (body.address.city != null) {
filter['address.city'] = body.address.city;
}
if (body.address.state != null) {
filter['address.state'] = body.address.state;
}
if (body.operations.rentAvailable != null) {
filter['operations.rentAvailable'] = body.operations.rentAvailable;
}
if (body.operations.purchaseAvailable != null) {
filter['operations.purchaseAvailable'] = body.operations.purchaseAvailable;
}
if (body.operations.shortRentAvailable != null) {
filter['operations.shortRentAvailable'] =
body.operations.shortRentAvailable;
}
let agentRentPrice = {};
if (body.operations.agentRentPriceMin != null) {
agentRentPrice = { $gte: body.operations.agentRentPriceMin };
}
if (body.operations.agentRentPriceMax != null) {
agentRentPrice = {
...agentRentPrice,
$lte: body.operations.agentRentPriceMax,
};
}
if (Object.keys(agentRentPrice).length > 0) {
filter['operations.agentRentPrice'] = agentRentPrice;
}
let agentSellingPrice = {};
if (body.operations.agentSellingPriceMin != null) {
agentSellingPrice = { $gte: body.operations.agentSellingPriceMin };
}
if (body.operations.agentSellingPriceMax != null) {
agentSellingPrice = {
...agentSellingPrice,
$lte: body.operations.agentSellingPriceMax,
};
}
if (Object.keys(agentSellingPrice).length > 0) {
filter['operations.agentSellingPrice'] = agentSellingPrice;
}
let agentShortRentPrice = {};
if (body.operations.agentShortRentPriceMin != null) {
agentShortRentPrice = { $gte: body.operations.agentShortRentPriceMin };
}
if (body.operations.agentShortRentPriceMax != null) {
agentShortRentPrice = {
...agentShortRentPrice,
$lte: body.operations.agentShortRentPriceMax,
};
}
if (Object.keys(agentShortRentPrice).length > 0) {
filter['operations.agentShortRentPrice'] = agentShortRentPrice;
}
if (body.features.bathrooms != null) {
filter['features.bathrooms'] = { $gte: body.features.bathrooms };
}
if (body.features.dorms != null) {
filter['features.dorms'] = { $gte: body.features.dorms };
}
if (body.features.beds != null) {
filter['features.beds'] = { $gte: body.features.beds };
}
if (body.features.amenities != null) {
filter['features.amenities'] = { $in: body.features.amenities };
}
return filter;
};
console.log(buildFilter(body));
A little verbose, but it is due to the fact that you don't have a 1-on-1 correspondence between the attributes of the body and your filter's properties.