1

I have documents in my mongodb collection that look like:

{
    "gran": "Day",
    "dix": NumberInt(80),
    "y": NumberInt(2017),
}, 
{
    "gran": "Month",
    "dix": NumberInt(3),
    "y": NumberInt(2017),
}

I want to add to those documents a from/to field, so they look like:

{
    "gran": "Day",
    "dix": NumberInt(80),
    "y": NumberInt(2017),
    "from": ISODate("2017-03-21T00:00:00.000+0000"),
    "to": ISODate("2017-03-21T23:59:59.000+0000")
},
{
    "gran": "Month",
    "dix": NumberInt(3),
    "y": NumberInt(2017),
    "from": ISODate("2017-03-01T00:00:00.000+0000"),
    "to": ISODate("2017-03-31T23:59:59.000+0000")
}

I wrote a javascript function that converts from granularities to dates:

function(year, index, gran) {
    var fromToDates = {};
    if (gran === "Day") {
        var initDate = new Date(year, 0); // initialize a date in `year-01-01`
        var currentDate = new Date(initDate.setDate(index)); // add the number of days
        var dateFrom = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate(), 0, 0, 0);
        var dateTo = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate(), 23, 59, 59);
        fromToDates.from = dateFrom;
        fromToDates.to = dateTo;
        return fromToDates;
    } else if (gran === "Week") {
        var simple = new Date(year, 0, 1 + (index - 1) * 7);
        var dow = simple.getDay();
        print(dow);
        var dateFrom = simple;
        if (dow <= 4)
            dateFrom.setDate(simple.getDate() - simple.getDay() + 1);
        else
            dateFrom.setDate(simple.getDate() + 8 - simple.getDay());
        var dateTo = new Date(dateFrom);

        dateTo.setDate(dateTo.getDate() + 6);
        dateTo.setUTCHours(23);
        dateTo.setUTCMinutes(59);
        dateTo.setUTCSeconds(59);
        fromToDates.from = dateFrom;
        fromToDates.to = dateTo;
        return fromToDates;

    } else if (gran === "Month") {
        var monthIndex = index - 1;
        var initDate = new Date(year, monthIndex);
        var dateFrom = new Date(initDate.getFullYear(), monthIndex, 1, 0, 0, 0);
        var lastDayOfMonth = monthIndex + 1;
        var dateTo = new Date(dateFrom.getFullYear(), lastDayOfMonth, 0, 23, 59, 59);
        fromToDates.from = dateFrom;
        fromToDates.to = dateTom;
        return fromToDates;
    } else if (gran === "Quarter") {
        var dateFrom = new Date(year,index*3-3,1);
        var dateTo = new Date(year,index*3,0, 23, 59, 59);
        fromToDates.from = dateFrom;
        fromToDates.to = dateTo;
        return fromToDates;
    }
    else if (gran === "Year") {
        var dateFrom = new Date(year, 0, 1, 0,0,0);
        var dateTo = new Date(year, 11, 31, 23, 59, 59);
        fromToDates.from = dateFrom;
        fromToDates.to = dateTo;
        return fromToDates;
    }
}

For adding the two fields from/to I thought I can use this method:

db.reports.findAndModify({
  query: { "from": {$exists: false}},
  update: {
      $set: {
            "from": new Date(),
            "to": new Date()
        }
    },
  multi: true
})

Then I read around that I could do this:

db.reports.aggregate([
{
  $match:
  {
  } 
}
]).map(function(doc) {
  var dates = dateIndexToDateWithFromTo(doc.y, doc.dix, doc.gran);
  doc['from'] = dates.from;
  doc['to'] =  dates.to;
  return doc;
})

but this is not really working. It does return the correct dates but of course it doesn't update them in the collection. Anyone has an idea how I can update my documents dynamically by using the script that I wrote?

I'm using mongodb version 3.2 so for everyone that will suggest to use $addFields in an aggregation pipeline, it's not possible, because it's not possible for me to update to 3.4 right now.

Alex P.
  • 3,073
  • 3
  • 22
  • 33

1 Answers1

0

I think I found it, or at least it works for the small amount of data that I'm testing.

db.media_code_reports.aggregate([
{
  $match:
  {
  } 
}
]).map(function(doc) {
  var dates = dateIndexToDateWithFromTo(doc.y, doc.dix, doc.gran);
  doc.from = dates.from;
  doc.to =  dates.to;
  db.media_code_reports.save(doc);
})

I'm not sure tho if this is efficient. I have to do two steps in order to add the fields:

  1. first add them with the findAndModify(...) function
  2. And then use aggregation with and empty match to get all documents, followed by ajavascript map() function to iterate through every document. And inside the map(). do a save() of each document.

If anyone has a better suggestion please say so, otherwise I'll consider this as answered.

Alex P.
  • 3,073
  • 3
  • 22
  • 33