본문 바로가기

express

Query mongoose

We got query conditions at once. so we should divide process of querying.

Find resource(query) -> Select -> Sort -> pagination

 

Below code is advanced query I learned in my udemy course. I need to understand this line by line.

so dive in to this code.

const advancedResults = (model, populate) => async (req, res, next) => {
  let query;

  // Copy req.query
  const reqQuery = { ...req.query };

  // Fields to exclude
  const removeFields = ['select', 'sort', 'page', 'limit'];

  // Loop over removeFields and delete them from reqQuery
  removeFields.forEach(param => delete reqQuery[param]);

  // Create query string
  let queryStr = JSON.stringify(reqQuery);

  // Create operators ($gt, $gte, etc)
  queryStr = queryStr.replace(/\b(gt|gte|lt|lte|in)\b/g, match => `$${match}`);

  // Finding resource
  query = model.find(JSON.parse(queryStr));

  // Select Fields
  if (req.query.select) {
    const fields = req.query.select.split(',').join(' ');
    query = query.select(fields);
  }

  // Sort
  if (req.query.sort) {
    const sortBy = req.query.sort.split(',').join(' ');
    query = query.sort(sortBy);
  } else {
    query = query.sort('-createdAt');
  }

  // Pagination
  const page = parseInt(req.query.page, 10) || 1;
  const limit = parseInt(req.query.limit, 10) || 25;
  const startIndex = (page - 1) * limit;
  const endIndex = page * limit;
  const total = await model.countDocuments(JSON.parse(queryStr));

  query = query.skip(startIndex).limit(limit);

  if (populate) {
    query = query.populate(populate);
  }

  // Executing query
  const results = await query;

  // Pagination result
  const pagination = {};

  if (endIndex < total) {
    pagination.next = {
      page: page + 1,
      limit
    };
  }

  if (startIndex > 0) {
    pagination.prev = {
      page: page - 1,
      limit
    };
  }

  res.advancedResults = {
    success: true,
    count: results.length,
    pagination,
    data: results
  };

  next();
};

module.exports = advancedResults;

How does 'advancedResult' process.

1. Copy req.query in a variable

2. Exclude fields which dont need at find() method. ('select', 'sort', etc)

3. Create queryString and modify to mongoose can understands( in => $in, gt => $gte)

4. Query with modified queryString

5. and then with query Results, do select, sort, populate etc

At first, when we call "advancedResults", we provide 'model' and 'populate' 

router
  .route('/')
  .get(advancedResults(Model, 'populate'), getCourses);

Searching what we want in query(Filtering from the model) 

1. Copy req.query in a variable(reqQuery)

2. Stringify requset / we need request as a string to manipuate and then append '$' to the matched variable

3. Replace arguments(put '$', gt => $gte, in => $in)

4. Change stringified request to JSON back.

const advancedResults = (model, populate) => async (req, res, next) => {
   let query;
   
1) let queryStr = JSON.stringify(req.query);
2) queryStr = queryStr.replace(/b(gt|gte|lt|lte|in)\b/g, match => `$${match}`);
3) query = Course.find(JSON.parse(queryStr));
4) const courses = await query;

If we request below.

// if we have fieldA in course
GET /api/v1/courses?fiedsA[lte]=10000

 Server got above request as JSON object below. 

{ fieldsA: { lte: 10000 } }

Then by the advancedResults it will be changed to

{ fieldA: { $lte: 10000 }}

So, mongoose can query this lte.

 

Next, we do "select" what we will send to front and what order we will show /"sort"

Selecting and Sorting is next step after searching data. so we should remove this variables in finding condition.

let query;

const reqQuery = { ...req.query };

// Fields to exclude
+ const removeFields = ['select', 'sort'];   [ 1 ]

// Loop over removeFields and delete them from reqQuery
+ removeFields.forEach(param => delte reqQuery[param]);   [ 2 ]
  
let queryStr = JSON.stringify(reqQuery);
query = model.find(JSON.parse(queryStr));   

// Select Fields
+ if (req.query.select) {    
+   const fields = req.query.select.split(',').join(' ');    [ 3 ]
+   query = query.select(fields);
+}

[ 1 ] Delete parameters that is unrelated with searching(seletc, sort etc)

[ 2 ] Then do finding in model. "model.find()"

[ 3 ] Then do works what we already deletes. selecting, sorting.

So what we need to do is basically create our own version of request stock query and pull select out 
so that it doesn't try to match it. And then we can move on and do what we want with it.

Populate

Check advancedResult() methods has 'populate' parameter.

router
  .route('/')
  .get(advancedResults(Course, 'reviews'), getCourses)