Last week I started working on a new plug-in for Grails called trackR. It allows developers to track the activity of visitors within a Grails application (Apache style).
Source can be found here: https://trac.nbic.nl/grails-plugins/browser/dbxpTrackr/trunk
If you want to use it within your own project you must add our Nexus repository to your project: http://nexus.nmcdsp.org/content/repositories/releases. You can read how here: http://grails.org/doc/1.3.7/guide/single.html#12.2%20Plugin%20Repositories
When I started I was using Grails 2.x, but I switched back to Grails 1.3.7 because most of my projects have not upgraded yet to Grails 2.x. I will in the future upgrade the plug-in to Grails 2.x
How to use
1. install the plug-in
2. add location/prefix to the application config
3. add tag(s) to the gsp’s
4. view the trackR data
Install the plug-in
a. Add the following line to the BuildConfig.groovy
mavenRepo “http://nexus.nmcdsp.org/content/repositories/releases”
b. Do a ‘grails install-plugin trackr’
Set config
The trackR plug-in knows 2 config entries which you have to add to the Config.groovy:
//path to where the daily trackr log files should be created (make sure it is writable)
trackr.path = "/tmp/"
//prefix of the file
trackr.prefix = "grails.application.logfile."
Add tags
To enable the trackR you use the build-in taglib. Basic usage could be to include a trackR tag in the application’s layout template (grails-app/views/layouts/main.gsp). For example like this:
...
<trackr:track reference="${session.user ?: 'unknown user'}"/>
...
The reference parameter, which is optional, can be used to log application/page/user specific information. In the example I use it to track the user that is logged-in.
View the data
Now that we track the activity it would also be nice if we could view it. In the plug-in a controller called, yes you guessed right, trackr is included. Through this controller you get some basic access to the trackR data. Current version (0.6.3) only has information about the browsers used and the url’s accessed. I hope to add more in the near future. If you have any suggestions please contact me.
view the log data
analyse the data
Please note: by default the trackr data is exposed via the web, if you want to block this you could write a simple filter to block access. How to do this can be found here: http://grails.org/doc/2.0.x/ref/Plug-ins/filters.html
Using MongoDB from a Grails application is easy and a lot of fun! After reading the manual I wanted to find out more about the schemaless and dynamic nature of MongoDB. The way to do that is use the Low Level API. The one that comes with the MongoDB plugin is based on GMongo. Before you can try this you have to have MongoDB running (read here how to do that) and have a Grails application with the MongoDB plugin present (read here how to do that).
// connect to a MongoDB instance and use 'school'
def mongodb = mongo.getDB("school")
//clear all existing data
mongodb.getCollection("classes").drop();
//insert 3 documents where each document represents a class
mongodb.classes << [
[ name: 'Sixth Grade', year: 2011,
teacher: [name: 'A. Burrows', age: 35],
students:[
[name:'Sam', age: 11],
[name:'Pat', age: 11],
[name:'John', age: 12]
]
],[ name: 'Seventh Grade', year: 2011,
teacher: [name: 'H. Sedin', age: 37],
students:[
[name:'Sven', age: 12],
[name:'Randy', age: 12],
[name:'Fred', age: 14]
]
],[ name: 'Eighth Grade', year: 2011,
teacher: [name: 'M. Malhotra', age: 33],
students:[
[name:'Wanda', age: 16],
[name:'Jesse', age: 16]
]
]
]
// list name and year of classes
render "<h1>classes:</h1>"
mongodb.getCollection("classes").find().each {
render "${it.name} (${it.year})<br />"
}
// list name and age of teachers
render "<h1>teachers</h1>"
mongodb.getCollection("classes").find().each {
render "${it.teacher.name} (age: ${it.teacher.age})<br />"
}
// list name and age of students
render "<h1>students</h1>"
mongodb.getCollection("classes").find().each {
it.students.each { student ->
render "${student.name} (age: ${student.age})<br />"
}
}
// list avg age of students per grade
def classes = mongodb.getCollection("classes");
// set the map headers to be returned
def key = new BasicDBObject()
key.put("name", true);
key.put("year", true);
key.put("students", true);
key.put("age", true);
key.put("avg", true);
// filter the map
def cond = new BasicDBObject();
// only include students with the age 4 or higher
cond.put("students.age", new BasicDBObject('$gte', 4))
// set the initial values before starting the calculations
def initial = new BasicDBObject();
initial.put("students", 0);
initial.put("age", 0);
initial.put("avg", 0);
// set the reduce (javascript) function
def reduce = """
function(obj,prev) {
for (key in obj.students){
if (Number(obj.students[key].age) >= 1) {
prev.students >= 1 ? prev.students += 1 : prev.students = 1;
prev.age += Number(obj.students[key].age);
}
}
}"""
// set the finalalize (javascript) function
def finalize = """
function(prev) {
if (prev.age > 0 && prev.students > 0){
prev.avg = prev.age / prev.students;
}
}"""
// execute the group command
def result = classesCollection.group(
key, cond, initial, reduce, finalize
)
// output the results
render "<h1>age of students per grade/year</h1>"
result.each {
render """
${it.name} had
${it.students} students
with an avg age of ${it.avg.round(2)}
(in: ${it.year as int})
"""
}
There are a couple of things you have to keep in mind when using the MongoDB Plugin of Grails:
1. Conditions examples are often written like this:
def cond = new BasicDBObject();
cond.put(“students.age”, new BasicDBObject(”$gte”, 4))
but in Groovy/Grails this fails as ”$..” will result in a GString… you should use ’$..’ instead, like this:
def cond = new BasicDBObject();
cond.put(“students.age”, new BasicDBObject(’$gte’, 4))
2. Conditions, is it AND or is it OR:
When we want all students where age >= 10 or age <= 20…
WRONG WAY
this will return all ages as one of the conditions will always be TRUE
def cond = new BasicDBObject();
cond.put(“age”, new BasicDBObject(‘$gte’, 10));
cond.put(“age”, new BasicDBObject(‘$lte’, 20));
RIGHT WAY
this will return all students with age 10..20
def cond = new BasicDBObject();
cond.put(“age”, new BasicDBObject(‘$gte’, 10).append(‘$lte’, 20));
Links that may help!
http://www.mongodb.org/display/DOCS/SQL+to+Mongo+Mapping+Chart
http://devcheatsheet.com/tag/mongodb/
http://www.ibm.com/developerworks/java/library/j-javadev2-12/index.html