5

I receive a POST argument that looks like this:

sort:
    [ 
        { field: 'name', dir: 'asc', compare: '' },
        { field: 'org', dir: 'asc', compare: '' }
    ] 
}

and I need to create a MongoDB query based on that, so it should look like:

db.collection("my_collection").find( ... ).sort({'name': 'asc', 'org': 'asc'}).toArray(...);

Anyways, keep in mind that more fields could be passed. Also, it could happen that none of those fields is passed, meaning that the query won't have .sort().

My question: How can I create dynamically a query with Node's MongoDB driver? Is there a query builder or something similar?

1 Answer 1

9

I've found that most cases are unique regarding passed data, so building query objects varies from project to project.
So first ideas was to create middleware for express (in my case), that would parse query arguments into objects that are valid for query.

mongo-native can use as chained options to cursor, as well as in object:

Chained:

items.find({ type: 'location' }).sort({ title: 1 }).limit(42).toArray(function(err, data) {
  // ...
});

Non-chained:

items.find({ type: 'location' }, { sort: { title: 1 }, limit: 42 }).toArray(function(err, data) {
  // ...
});

As you can see Non-Chained can accept everything as object, while chained returns cursor after every method and can be reused. So generally you have two options:

For Chained:

var cursor = items.find({ type: 'location' });
if (sort) {
  cursor.sort(sort);
}
cursor.toArray(function(err, data) {
  // ...
});

For Non-Chained:

var options = { };
if (sort) {
  options.sort = sort;
}
items.find({ type: 'location' }, options).toArray(function(err, data) {
  // ...
});

It is important to remember that any data from query have to be validated and parsed properly. As well if you are developing API (for example), and will decide to change the way sorting arguments are passed or will want to add new way, then making middleware (in express.js) for parsing this data - is the way to go.

Example for pagination:

function pagination(options) {
  return function(req, res, next) {
    var limit = options.limit ? options.limit : 0;
    var skip = 0;

    if (req.query.limit) {
      var tmp = parseInt(req.query.limit);
      if (tmp != NaN) {
        limit = tmp;
      }
    }
    if (req.query.skip) {
      var tmp = parseInt(req.query.skip);
      if (tmp != NaN && tmp > 0) {
        skip = tmp;
      }
    }

    if (options.max) {
      limit = Math.min(limit, options.max);
    }
    if (options.min) {
      limit = Math.max(limit, options.min);
    }

    req.pagination = {
      limit: limit,
      skip: skip
    };
    next();
  }
}

Usage:

app.get('/items', pagination({
  limit: 8, // by default will return up to 8 items
  min: 1, // minimum 1
  max: 64 // maximum 64
}), function(req, res, next) {
  var options = {
    limit: req.pagination.limit,
    skip: req.pagination.limit
  };
  items.find({ }, options).toArray(function(err, data) {
    if (!err) {
      res.json(data);
    } else {
      next(err);
    }
  });
});

And url examples:

http://example.com/items  
http://example.com/items?skip=64  
http://example.com/items?skip=256&limit=32  

So it is the way to develop well flexible framework, which does not creates any rules of how things have to be coded as well as solving your challenge.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.