How to build dynamic mongoose queries
Tags: node, mongoose, mongodb
Published: 2019-11-04 23:40:56

---
title: How to build dynamic mongoose queries
published: true
description: A short post on how to query the database when the search field and search value are not known beforehand.
tags: node,mongoose, mongodb
---

***Disclaimer: This post assumes you have knowledge of Node js, and mongoose.***

#The challenge:
Say you need to build an internal product where a **user needs to be able to search  for data by different criteria** e.g. a grading app for teachers where students can be searched based on class, age, continuous assessment results, school fees(partial payment or full payment).


##On the back end:
-We would build a Student model
-A controller function for querying the database
-A router to listen for get requests on a specified path.

Our Student model:
We would need to define a model that encapsulates all the data we want each student attending our 'school of code' to have.

```javascript
const mongoose = require('mongoose');

const student = mongoose.Schema({
  name: String,
  age: Number,
  class: String,
  school_fees: { 
               type: String, 
               enum: ['Not Paid', 'Full Payment', 'Partial Payment'], 
               default: 'Not Paid' 
              },
  test_1: String,
  test_2: String
});

module.exports = mongoose.model('Student', student);

```


Now imagine we have a select drop-down tag named 'Search By' on the front end which gives a list of criteria to search students by(i.e. our 'search_field') and clicking on either of the options displays a search bar by which you can type in your search value (let's call it 'search_value'). 

We would have to get the 'search_field' and 'search_value' on the frontend and use it to query the database as such({search_field: search_value}). Now we don't know beforehand what the client will pick. 

For example: It could be Search by ('age'):(10). Age would be our search_field and 10 would be our search_value.

Our Controller function:

```javascript
 
const Student = require('../models/student.js');

exports.getStudentBy = async (req, res) => {
  try {
    // get search_criteria from query parameter
    // build a query object with it
    // send data to the frontend

    const { search_field, search_value } = req.query;

    const queryObj = {};

    if (search_field !== '' && search_value !== '') {
      queryObj[search_field] = search_value;
    }

    console.log('::queryObj:::', queryObj);

    const student = await Student.find(queryObj);

    if (!student) {
      return res.status(404).json({
        status: 'failure',
        message: `Student with the given ${search_field}:${search_value} not found`
      });
    }

    res.status(200).json({
      status: 'success',
      data: student
    });
  } catch (error) {
    res.status(500).json({
      status: 'failure',
      error: error.message
    });
  }
};

```

What's happening here?

First we get the search field and search value from the request object by using es6 object destructuring.

```javascript
 const { search_field, search_value } = req.query;
```

Next, we need to build a query object that would contain our query parameters in key-value pairs.

A gentle aside, assuming you need to search for students by class, you could do something like this:

```javascript
const students = await Student.find({class: 'grade 2'});
```

The above would be easy if we knew what the client was going to pick ahead of time such as an input field and a 'button' beside it that says 'search by class'. But now we have   a variety of inputs dependent on the client's choice.

So...

We build an object that dynamically sets the search field as the object's key, and sets the search value as the object's value.

Remember: 

```javascript
const object = {
                 key: value
               }
```

So we check that the search field and search value aren't empty strings. Then we set the search field to the query Object's key and the search value to the corresponding value.

```javascript
 const queryObj = {};

    if (search_field !== '' && search_value !== '') {
      queryObj[search_field] = search_value;
    }
```

**In case this looks confusing, google the difference between using the dot notation for adding properties to an object and using bracket notation. Don't use dot notation as this will not give you what you want**


Next, we place this query object in our find method, and 'await' the result

```javascript
     const student = await Student.find(queryObj);
```

Then we handle the asynchronous responses appropriately by placing the code in a try-catch block.

##Our Router:

```javascript
  
  const express = require('express');
  const { getStudentBy } = require('../controllers/student.js');

  const router = express.Router();

  router.get(`${API}/search`, getStudentBy);

  module.exports = router;
```

Here's where it all comes together, the router. We listen for get requests on the specified api path.

This tutorial is by no means exhaustive or well written. In fact it was a bit rushed, but I just had to post this because I just figured out how to do this and I'm stoked to share it!

Please leave your comments, contributions, corrections and likes!