Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mongoose how to write a query with if condition?

Suppose I have the following query:

post.getSpecificDateRangeJobs = function(queryData, callback) {
var matchCriteria = queryData.matchCriteria;
var currentDate = new Date();
var match = { expireDate: { $gte: new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate()) } };
if (queryData.matchCriteria !== "") {
  match = {
    expireDate: { $gte: new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate()) },
    $text: { $search: matchCriteria }
  };
}
var pipeline = [
  {
    $match: match
  },
  {
    $group: {
      _id: null,
      thirtyHourAgo: {
        $sum: {
          $cond: [
            {
              $gte: [
                "$publishDate",
                new Date(queryData.dateGroups.thirtyHourAgo)
              ]
            },
            1,
            0
          ]
        }
      },
      fourtyEightHourAgo: {
        $sum: {
          $cond: [
            {
              $gte: [
                "$publishDate",
                new Date(queryData.dateGroups.fourtyHourAgo)
              ]
            },
            1,
            0
          ]
        }
      },
      thirtyDaysAgo: {
        $sum: {
          $cond: [
            {
              $lte: [
                "$publishDate",
                new Date(queryData.dateGroups.oneMonthAgo)
              ]
            },
            1,
            0
          ]
        }
      }
    }
  }
];
var postsCollection = post.getDataSource().connector.collection(
    post.modelName
);
postsCollection.aggregate(pipeline, function(err, groupByRecords) {
  if (err) {
    return callback("err");
  }
  return callback(null, groupByRecords);
});
};

What i want to do is: 1- check if queryData.dateGroups.thirtyHourAgo existed and has value, then only add the relevant match clause in query (count of posts only for past 30 hour). 2- check if queryData.dateGroups.fourtyHourAgo existed, then add relevant query section (count of posts for past 30 hour, and past 48 hour ago). 3 and the same for queryData.dateGroups.oneMonthAgo (count of posts for past 30 hour, 48 hour, and past one month).

I need something like Mysql if condition to check if a variable existed and not empty then include a query clause. Is it possible to do that?

My sample data is like:

/* 1 */
{
"_id" : ObjectId("58d8bcf01caf4ebddb842855"),
"vacancyNumber" : "123213",
"position" : "dsfdasf",
"number" : 3,
"isPublished" : true,
"publishDate" : ISODate("2017-03-11T00:00:00.000Z"),
"expireDate" : ISODate("2017-05-10T00:00:00.000Z"),
"keywords" : [ 
    "dasfdsaf", 
    "afdas", 
    "fdasf", 
    "dafd"
],
"deleted" : false
}

/* 2 */
{
"_id" : ObjectId("58e87ed516b51f33ded59eb3"),
"vacancyNumber" : "213123",
"position" : "Software Developer",
"number" : 4,
"isPublished" : true,
"publishDate" : ISODate("2017-04-14T00:00:00.000Z"),
"expireDate" : ISODate("2017-05-09T00:00:00.000Z"),
"keywords" : [ 
    "adfsadf", 
    "dasfdsaf"
],
"deleted" : false
}

/* 3 */
{
"_id" : ObjectId("58eb5b01c21fbad780bc74b6"),
"vacancyNumber" : "2432432",
"position" : "Web Designer",
"number" : 4,
"isPublished" : true,
"publishDate" : ISODate("2017-04-09T00:00:00.000Z"),
"expireDate" : ISODate("2017-05-12T00:00:00.000Z"),
"keywords" : [ 
    "adsaf", 
    "das", 
    "fdafdas", 
    "fdas"
],
"deleted" : false
}

/* 4 */
{
"_id" : ObjectId("590f04fbf97a5803636ec66b"),
"vacancyNumber" : "4354",
"position" : "Software Developer",
"number" : 5,
"isPublished" : true,
"publishDate" : ISODate("2017-05-19T00:00:00.000Z"),
"expireDate" : ISODate("2017-05-27T00:00:00.000Z"),
"keywords" : [ 
    "PHP", 
    "MySql"
],
"deleted" : false
}

Suppose I have three link in my application interface: 1- 30 hour ago posts. 2- 48 hour ago posts. 3- last one month posts.

Now if user click on first link i should control to group posts only for 30 hour ago, but if user click on second link, i should prepare my query to group posts for 30 hour and also for 48 hour, and if user click on third link i should prepare for all of them.

I want something like:

 var pipeline = [
  {
    $match: match
  },
  {
    $group: {
      _id: null,
      if (myVariable) {
        thirtyHourAgo: {
          ........
          ........
        }
      } 
      if (mysecondVariable) {
        fortyEightHourAgo: {
          ........
          ........
        }
      }
like image 727
jones Avatar asked May 10 '17 10:05

jones


People also ask

Can I use $in in Mongoose?

We can specify as many conditions in the query field. But in cases, where we need to filter according to only one field but more than on values. For example, if we want every document where the value of the name field is more than one value, then what? For such cases, mongoose provides the $in operator.

What does lean () do Mongoose?

The lean option tells Mongoose to skip hydrating the result documents. This makes queries faster and less memory intensive, but the result documents are plain old JavaScript objects (POJOs), not Mongoose documents.

What is __ V in MongoDB?

The __v field is called the version key. It describes the internal revision of a document. This __v field is used to track the revisions of a document. By default, its value is zero ( __v:0 ).

What does exec () do Mongoose?

In mongoose, exec method will execute the query and return a Promise.


2 Answers

You can use javascript to dynamically create json document based on your query parameters.

Your updated function will look something like

post.getSpecificDateRangeJobs = function(queryData, callback) {

  var matchCriteria = queryData.matchCriteria;
  var currentDate = new Date();

  // match document
  var match = {
    "expireDate": {
      "$gte": currentDate 
    }
  };

  if (matchCriteria !== "") {
    match["$text"]: {
      "$search": matchCriteria
    }
  };

  // group document
  var group = {
    _id: null
  };

  // Logic to calculate hours difference between current date and publish date is less than 30 hours.

  if (queryData.dateGroups.thirtyHourAgo) {
    group["thirtyHourAgo"] = {
      "$sum": {
        "$cond": [{
            "$lte": [{
              "$divide": [{
                "$subtract": [currentDate, "$publishDate"]
              }, 1000 * 60 * 60]
            }, 30]
          },
          1,
          0
        ]
      }
    };
  }

  // Similarly add more grouping condition based on query params.

  var postsCollection = post.getDataSource().connector.collection(
    post.modelName
  );

  // Use aggregate builder to create aggregation pipeline.

  postsCollection.aggregate()
    .match(match)
    .group(group)
    .exec(function(err, groupByRecords) {
      if (err) {
        return callback("err");
      }
      return callback(null, groupByRecords);
    });

};
like image 173
s7vr Avatar answered Sep 20 '22 04:09

s7vr


As I understood, I can suggest you following general query. Modify this according to your need.

db.getCollection('vacancy')
.aggregate([{$match: { $and: [ 
{publishDate:{ $gte: new Date(2017, 4, 13) }} , 
{publishDate:{ $lte: new Date(2017, 4, 14) }}
]} }])

Summary:

  • Used match to filter out result.
  • We are using aggregation Pipeline so you can add more aggregate operators n the pipeline
  • Using $and perform a logical AND because we want to fetch some documents between a give range say 1 day, 2 days or 1 month (change date parameters according to your requirement)
like image 44
Sandeep Sharma Avatar answered Sep 20 '22 04:09

Sandeep Sharma