skip to Main Content

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?

2

Answers


  1. 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.

    Login or Signup to reply.
  2. You can solve this by creating the filter dynamically on the backend. Basically, you will start with an empty filter, and then you will check each user input, and add it to the filter if exists. That way, you don’t have to worry if the user will send multiple filters, or if it will not send filters at all. Everything will be added dynamically.

    const queryObj = {
      address: { city: "New York" },
      operations: {
        rentAvailable: true,
        agentRentPrice: {
          $lte: 10000,
        },
      },
      features: {
        dorms: { $gte: 2 },
        amenities: { $in: ["pool"] },
      },
    };
    
    // Initialize empty filter.
    const filter = {};
    
    if (queryObj?.address?.city) filter['address.city'] = queryObj.address.city;
    
    if (typeof queryObj?.operations?.rentAvailable === boolean) filter['operations.rentAvailable'] = queryObj.operations.rentAvailable;
    
    if (queryObj?.operations?.agentRentPrice?.$lte) filter['operations.agentRentPrice'] = { $lte: queryObj.operations.agentRentPrice.$lte };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search