Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic Query MongoDB

I'm trying to build a MongoDB query object based on data received from a client-side search form. My goal is to query the database with any and all criteria provided by the user, whilst allowing the user to leave some search fields blank if they choose to.

This is my current attempt at a query object is:

var q = {}; // declare the query object
  q['$and']=[]; // filter the search by any criteria given by the user
  if((req.body.learninglanguages).length > 0){ // if the criteria has a value or values
    q["$and"].push('{learningLanguages: {$in: ' + req.body.learninglanguages.split(",") + '}}'); // add to the query object
  }
  if((req.body.spokenlanguages).length > 0){
    q["$and"].push('{spokenLanguages: {$in: ' + req.body.spokenlanguages.split(",") + '}}');
  }
  if((req.body.country).length > 0){
    q["$and"].push('{country: {$in: ' + req.body.country.split(",") + '}}');
  }
  if((req.body.commethod).length > 0){
    q["$and"].push('{comMethod: {$in: ' + req.body.commethod.split(",") + '}}');
  }

But the resulting object is:

{ '$and': 
   [ '{learningLanguages: {$in: Albanian,American Sign Language,Amharic,Arabic,Arabic (Egyptian)}}',
     '{spokenLanguages: {$in: Akan,Albanian,American Sign Language,Amharic}}',
     '{country: {$in: Åland Islands}}',
     '{comMethod: {$in: whatsapp,email,face to face,skype}}' ] }

How can I correctly build a MongoDB $in query from req.body objects?

like image 787
Runny Yolk Avatar asked Oct 11 '16 21:10

Runny Yolk


2 Answers

The problem with your query is you are attempting to build a string instead of directly building an object, like mongoDB & mongoose accept:

var q = {}; // declare the query object
  q['$and']=[]; // filter the search by any criteria given by the user
  if((req.body.learninglanguages).length > 0){ // if the criteria has a value or values
    q["$and"].push({ learningLanguages: {$in: req.body.learninglanguages.split(",") }}); // add to the query object
  }
  if((req.body.spokenlanguages).length > 0){
    q["$and"].push({ spokenLanguages: {$in: req.body.spokenlanguages.split(",") }});
  }
  if((req.body.country).length > 0){
    q["$and"].push({ country: {$in: req.body.country.split(",") }});
  }
  if((req.body.commethod).length > 0){
    q["$and"].push({ comMethod: {$in: req.body.commethod.split(",") }});
  }

You can see that instead of pushing in a string, I am instead pushing in a direct object that fits the documentation specifications.

See the documentation here for more information:

  1. https://docs.mongodb.com/manual/reference/operator/query/and/
  2. https://docs.mongodb.com/manual/reference/operator/query/in/

And here's a working jsbin you can play with:

  1. http://jsbin.com/cequbipiso/edit?js,console
like image 99
Derek Pollard Avatar answered Sep 20 '22 07:09

Derek Pollard


I followed Derek's suggestion, which is spot on in terms of how to dynamically construct the query conditions. But it appears that this code does not handle a scenario where no search parameters are specified.

Specifically, if all req.body parameters are empty, then you have a query object with an empty $and array as declared here:

q['$and']=[];

This results in a MongoError: $and/$or/$nor must be a nonempty array error.

Here's what I did to fix this issue:

var conditions = {}; //declare a conditions object
var and_clauses = []; //an array for the and conditions (and one for or conditions and so on)

if((!!req.body.email_id)){
    and_clauses.push({ 'email_id': {$regex: req.body.email_id }});
}   

if(and_clauses.length > 0){ 
    conditions['$and'] = and_clauses; // filter the search by any criteria given by the user
}

//Run the query
User.find(conditions,
    function(err, users) {
        if (err){
            console.log(err);
            res.status(500).send(err);
        }else{
            console.log(users);
            res.status(200).json(users);
        }
});

Derek, apologies if I got something wrong, don't want to point out an issue incorrectly.

like image 41
Vikas Mujumdar Avatar answered Sep 22 '22 07:09

Vikas Mujumdar