Home
Analytics
Using a date parameter with mongodb aggregation
malcatron
<p>I'm trying to write a report that gets data from a mongodb using Birt 4.3.</p><p>The documents which record logins look like this</p><p> </p><p>[font="'courier new', courier, monospace;"]{<br />
"_id" : ObjectId("5208ff1c2d5df25629d115bf"),<br />
"status" : "A",<br />
"accountNo" : "UY675820415307047356",[/font]</p><p>[font="'courier new', courier, monospace;"] "location" : "UK"<br />
"timestamp" : ISODate("2013-08-12T15:28:28.388Z")<br />
}[/font]</p><p> </p><p>My issue is that I need to present the data in the following way:</p><p>1) limit data by a time period defined by 2 report parameters startDate & endDate</p><p>2) group the data by accountNo, counting the logins for each account and also the total logins</p><p> </p><p>So, I have tried to use the Command Expression of the query builder with the following expression:</p><p> </p><p>[font="'courier new', courier, monospace;"]{ $match: { timestamp: {$gte:'?',$lte:'?'} } },<br />
{ $group : <br />
{ _id : <br />
{ accountNo : "$accountNo",<br />
location: "$location"<br />
},<br />
totalSignonsPerAccount : { $sum : 1},<br />
totalSignons : { $sum : "$totalSignonsPerAccount"}<br />
}<br />
}[/font]</p><p> </p><p>The $group part of the aggregation pipeline works fine, but I cannot figure out how to write the $match portion.</p><p> </p><p>If I change the $match to use the status property e.g.</p><p> </p><p>[font="'courier new', courier, monospace;"]{ $match: { status: 'A' } },[/font]</p><p> </p><p>it works fine, but I can't for the life of me figure out how to get it to work with date parameters against the timestamp property .</p><p> </p><p>I have the same date parameters in filters on other data sets that don't require any aggregation and they work without issue.</p><p> </p><p>Anyone got any clues?</p><p> </p><p>Cheers</p>
Find more posts tagged with
Comments
kclark
<p>In the output columns section of the data set UI what is the type set to for your date field?</p>
malcatron
<p>The timestamp field is not part of the resulting data set, so it doesn't appear in the output columns section.</p><p> </p><p>I am trying to get a subset of the collection based on a time period and then perform aggregation on that subset.</p><p> </p><p>i.e. how many times did each account login between startDate and endDate</p><p> </p><p>Using the mongodb shell I can run the query I need (endDate omitted for brevity):</p><p>[color=#0000cd;][font="'courier new', courier, monospace;"]db.signonLog.aggregate({ $match: { timestamp: {$gte : new ISODate("2013-08-12T15:28:28.412Z") } } },<br />
{ $group : <br />
{ _id : <br />
{ accountNo : "$accountNo",<br />
location: "$location"<br />
},<br />
totalSignonsPerAccount : { $sum : 1},<br />
totalSignons : { $sum : "$totalSignonsPerAccount"}<br />
}<br />
})[/color][/font]</p><p> </p><p>But in Birt I just cannot figure out how to do the date comparison.</p><p> </p><p>Basically, I think that firstly I need to understand how do you represent a date in Birt for Mongodb i.e.</p><p>[color=#0000cd;][font="'courier new', courier, monospace;"]timestamp: {$gte : [/color][/font]<strong>[color=#ff0000;][font="'courier new', courier, monospace;"]<what goes here?>[/color][/font]</strong>[color=#0000cd;][font="'courier new', courier, monospace;"]) }[/color][/font]</p><p> </p><p>Thanks!
</p>
Oculis
<p>Your query in Birt runs just as well using $date as follows:</p><pre class="_prettyXprint">{ $match: { timestamp: { $gte: { $date: "2013-08-12T15:28:28.412Z" } } } },{ $group : { _id : { accountNo : "$accountNo", location: "$location" }, totalSignonsPerAccount : { $sum : 1}, totalSignons : { $sum : "$totalSignonsPerAccount"} }})</pre><p>But the actual question (at least for me) is how to get variables working inside the query so I could something like [color=#0000ff;]{ $date: params["timestamp"].value }[/color] instead of [color=#0000ff;]{ $date: "2013-08-12T15:28:28.412Z" }[/color]. Unfortunately it does not work that way.
</p>
malcatron
<p>Ok, it's a year later and I've had to revisit this issue for a similar report and have finally found a way around it! So to hopefully save someone else from tearing their hair out over this, here's how I did it...</p>
<p> </p>
<p>For this report I needed to list the number of downloads and playbacks of each title between 2 dates supplied as parameters.</p>
<p> </p>
<p>First create the parameters as type Date with a suitable format (e,g, dd-MM-yyyy)</p>
<p> </p>
<p>Then create a MongoDb data set and set its command type to be aggregation.</p>
<p> </p>
<p>Define its expression, using $date to create the dates (requires full ISODate i.e. 2014-05-01 will not work!)</p>
<pre class="_prettyXprint _lang-js">
{ $match:
{
name: {$in:['download', 'playback'] },
tstamp: { $gte: { $date: "1970-01-01T00:00:00.000Z" }, $lte: { $date: "9999-01-01T23:49:59.999Z" }}
}
},
{ $group:
{
_id: { title:"$key", activity: "$name"},
count: { $sum: 1 }
}
}
</pre>
<p>For the dates use extreme values as in the above example - this will ensure that Birt can get a good snapshot of your MongoDb docs to ascertain the fields available. Make a note of the exact dates used (e.g. 1970-01-01 & 9999-01-01) as we will now be using a script to do text replacement on the query expression (I know... :blink: ). Select the fields you want and finish creating the data set.</p>
<p> </p>
<p>Select the data set in the outline pane then select the script tab of the main editor pane.</p>
<p> </p>
<p>From the Script dropdown select beforeOpen and enter a script to replace the dates of the query expression with the report parameters (here: startDate and endDate) NB: Months start at 0 so need to add 1 to get correct value for text replace!!</p>
<pre class="_prettyXprint _lang-js">
startDay = params["startDate"].value.getDate();
startMonth = params["startDate"].value.getMonth() + 1;
startYear = BirtDateTime.year(params["startDate"]);
replaceQueryStartDate = "" + startYear + "-" + startMonth + "-" + startDay;
this.queryText = this.queryText.replace("1970-01-01", replaceQueryStartDate);
endDay = params["endDate"].value.getDate();
endMonth = params["endDate"].value.getMonth() + 1;
endYear = BirtDateTime.year(params["endDate"]);
replaceQueryEndDate = "" + endYear + "-" + endMonth + "-" + endDay;
this.queryText = this.queryText.replace("9999-01-01", replaceQueryEndDate);
vars["queryText"] = this.queryText;
</pre>
<p>You may find it useful to use a variable to output the query text for debugging, above I have used a variable called "queryText" and then added it temporarily to my report for reference during development.</p>
<p> </p>
<p>So, not particularly elegant but it works!</p>