Multiple search filter using Nestjs, mongoose.
Tags: nestjs, typescript, mongoose, mongodb
Published: 2020-10-07 09:36:18

    The fields are all optional.The user can search for results using multiple fields given below:
    1. Select Country
    2. Project Type
    3. Practice Area
    4. Investment Range - Less than /Greater than
    5. Project tenure
Here, the fields are passed as query parameters and also as body in the Post Http request.Tenure, country, and fundsReuired are passed in the body json while practiceArea and type are sent as query parameters.
POST http://localhost:3000/api/v1/sme-project/filter/projectTenureFilter?practiceArea=Hardware&type=Private
  
 
        "country": "Australia",
        "fundsRequired": 20000,
        "lessThanFundsRequired": false
    }

a. If tenure is not empty, lines 22 to 35 are executed.
b. If practiceArea is not empty, lines 37 to 39 are executed.
c. If type is not empty, lines 40 to 42 are exectuted.
d. If fundsRequired is not empty, lines 44 to 52 are exectuted.
e. If country field is not empty, lines 55 to 85 are executed.
Like this, the query object is constructed for only the fields that are entered by user in search.This is how multiple search query is built and mongoose used for query.Also pagination is done for search results.
**Code (Part-I):**
{% runkit
// hidden setup JavaScript code goes in this preamble area
const hiddenVar = 42
%}import {
  Controller,
  Get,
  UseGuards,
  Request,
  Query,
  Put,
  Body,
  Post,
  BadRequestException,
  NotFoundException,
  Delete,
} from "@nestjs/common";
import {
  SmeProjectDto,
  CreateSmeProjectDto,
  UpdateSmeProjectDto,
} from "./objects/sme-project.dto";
import {
  BASEROUTES,
  USER_TYPES,
  ACTIVITY_FEED_EVENTS_SME,
  ACTIVITY_FEED_EVENTS_SPONSOR,
  PROJECT_STATUS,
  Status,
  KYC_VERIFICATION_STATUS,
  PROJECT_TENURE,
  TIMELINE_PREFERENCE_0,
} from "../common/constants/enum";
import { abstractBaseControllerFactory } from "../common/base/base.controller";
import { SmeProjectService } from "./sme-project.service";
import {
  RequestUser,
  IdOrCodeParser,
} from "../common/utils/controller.decorator";
import { JwtAuthGuard } from "../auth/auth.guard";
import { success } from "../common/base/httpResponse.interface";
import { AbstractClassTransformerPipe } from "../common/pipes/class-transformer.pipe";
import {
  ONLY_FOR_SME,
  PROJECT_EDIT_ERROR,
  PROJECT_APPROVED_STATUS_CHANGE_ERROR,
  PROJECT_FUNDED_STATUS_CHANGE_ERROR,
  PROJECT_COMPLETED_STATUS_CHANGE_ERROR,
  PROJECT_PENDING_STATUS_CHANGE_ERROR,
  PROJECT_REJECTED_STATUS_CHANGE_ERROR,
  PROJECT_DOCUMENTS_REQUIRED_STATUS_CHANGE_ERROR,
  ONLY_FOR_CREATED_BY_SME_OR_ADMIN,
  PROJECT_APPROVED_STATUS_CHANGE_ERROR1,
  FUNDS_REQUESTED_ERROR,
  CANNOT_DELETE_FUNDED_PROJECTS,
  PROJECT_PENDING_STATUS_CHANGE_ERROR_SME_KYC_NOT_APPROVED,
  ONLY_FOR_SPONSOR,
  USER_PREFERENCES_NOT_FILLED,
} from "../common/constants/string";
import { SmeActivityFeedService } from "../sme-activity-feed/sme-activity-feed.service";
import { use } from "passport";
import { ActivityFeedService } from "../activity-feed/activity-feed.service";
import { InjectModel } from "@nestjs/mongoose";
import { Model } from "mongoose";
import { ISmeProject } from "./objects/sme-project.schema";
import { BidDetailsService } from "../bid-details/bid-details.service";
import { IBidDetails } from "../bid-details/objects/bid-details.schema";
import { UsersService } from "../users/users.service";
import { plainToClass } from "class-transformer";
import { normalizePaginateResult } from "../common/interfaces/pagination";
import { IEntityDetails } from "../entity-details/objects/entity-details.schema";
import { IFavouriteProjects } from "../favourite-projects/objects/favourite-projects.schema";

const BaseController = abstractBaseControllerFactory<any>({
  DTO: SmeProjectDto,
  CreateDTO: CreateSmeProjectDto,
  UpdateDTO: UpdateSmeProjectDto,
  DisabledRoutes: [
    //BASEROUTES.DETELEONE,
    BASEROUTES.PATCH,
  ],
});

@UseGuards(JwtAuthGuard)
@Controller("sme-project")
export class SmeProjectController extends BaseController {
  constructor(
    private smeProjectService: SmeProjectService,
    private smeactivityFeedService: SmeActivityFeedService,
    private activityFeedService: ActivityFeedService,
    private usersService: UsersService,
    //  private bidDetailsService: BidDetailsService,
    //  @InjectModel("BidDetails")
    //  private readonly bidDetailsModel: Model<IBidDetails>,
    @InjectModel("SmeProject")
    private readonly smeProjectModel: Model<ISmeProject>,
    @InjectModel("EntityDetails")
    private readonly entityDetailsModel: Model<IEntityDetails>,
    @InjectModel("FavouriteProject")
    private readonly favouriteProjectsModel: Model<IFavouriteProjects>,
  ) {
    super(smeProjectService);
  }
    @UseGuards(JwtAuthGuard)
    @Post("filter/projectSponsorFilter")
    async findListFilterKYCStatus(
      @Request() req,
      @Query() query,
      @RequestUser() user,
      @Body() body: { tenure: string; country: string; fundsRequired: number; lessThanFundsRequired: boolean; }
    ) {
      if (user.type == USER_TYPES.ADMIN || user.type == USER_TYPES.SPONSOR) {
       
        var options = {
          limit: 3,
          page: 1,
          sort: "_id",
          skip: query.page ? (query.page - 1) : 0
        };
        let d: any = [];
        let _q: any;
    
        if (body.tenure !== undefined) {
          switch(body.tenure){
            case(TIMELINE_PREFERENCE_0.LESS_THAN_SIX_MONTHS):
              _q = { tenure: { $lt: 6 } };
              break;
            case(TIMELINE_PREFERENCE_0.SIX_MONTHS_TO_ONE_YEAR):
              let t = { $and: [{ tenure: { $lte: 12 } }, { tenure: { $gt: 6 } }] };
              _q = { ...t, ..._q };
              break;
            case(TIMELINE_PREFERENCE_0.GREATER_THAN_ONE_YEAR):
              _q = { tenure: { $gt: 12 } };
              break;
  
          }
        }
       
        if (query.practiceArea !== undefined) {
          _q = { practiceArea: query.practiceArea, ..._q };
        }
        if (query.type !== undefined) {
          _q = { type: query.type, ..._q };
        }
  
        if (body.fundsRequired !== undefined && body.lessThanFundsRequired !== undefined) {
          switch(body.lessThanFundsRequired){
            case false:
              _q = { fundsRequired: { $gt: body.fundsRequired } };
              break;
            case true:
              _q = { fundsRequired: { $lte: body.fundsRequired } }; 
          }
        }

        if (body.country !== undefined) {
          let projectId: string;
          var userIdsArray: string[] = [];
          var smeUserIdsArray: string[] = [];
          let _entityDetails = await this.entityDetailsModel.find(
            { "company.address.country": body.country },
            {},
            {
              sort: { _id: 1 },
              //skip: 0,
              // limit: 30,
              projection: {},
            }
          );
        
          await Promise.all(
            _entityDetails.map(async (element) => {
              await userIdsArray.push(element.createdBy);
              console.log("userssssssssArray", userIdsArray);
            })
          );
          await Promise.all(
            userIdsArray.map(async (element) => {
              let k = await this.usersService.findOne(element);
              if (k.type == USER_TYPES.SME) {
                await smeUserIdsArray.push(k.code);
              }
            })
          );
          let q = { createdBy: smeUserIdsArray };
          _q = { ...q, ..._q };
        }
        _q = { status: PROJECT_STATUS.APPROVED, ..._q };
     
        d = await this.smeProjectModel.find(
          _q,
          {},  { sort: { _id: 1 },
          skip: options.skip * options.limit, limit: options.limit,projection: {}}
        );
        
        let dCount = await this.smeProjectModel.count(
          _q,
        );
       
      
        var projectIdsArray: string[] = [];
        await d.map(async (data) => {
          await projectIdsArray.push(data.code);
        });
        let q = { code: projectIdsArray}
        let k = await this.findList(req, q, user );
        let pagination = normalizePaginateResult({
          total: dCount,//d.length,
          limit: options.limit,
          page: query.page,//options.page,
          pages: d.pages,
        });
        return { k, ...pagination };
      
      
      }
    }
  }
{% endrunkit %}
  ![Screenshot from 2020-10-07 15-01-46cycloanHundredTwenty](https://dev-to-uploads.s3.amazonaws.com/i/fgqw96n0okeym6l0a3hf.png)