9

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: {
          ........
          ........
        }
      }
s7vr
  • 73,656
  • 11
  • 106
  • 127
jones
  • 1,423
  • 3
  • 35
  • 76
  • Can you post sample data to test with and the expected output from that sample? Usually solutions for problems pertaining to using the aggregation framework are inferred from the expected output, so if you could provide the IO (Input -> Output) data it would go a long way with helping you solve this. – chridam May 10 '17 at 11:08
  • @chridam I have update my question, hope know it is clear enough. – jones May 10 '17 at 11:23

2 Answers2

10

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);
    });

};
s7vr
  • 73,656
  • 11
  • 106
  • 127
1

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)
Sandeep Sharma
  • 1,855
  • 3
  • 19
  • 34