UPDATE: GMongo is in constantly development, for more details check https://github.com/poiati/gmongo.
GMongo is an alternative to de default Java driver for Mongodb. It’s use an easy and less verbose syntax, the grammar is very close to the official mongo shell cliente (javascript).
It’s just a wrapper around the Java driver. So, every single method of the the official driver is available here too.
The main class is the com.gmongo.GMongo. It has the same API of the com.mongodb.Mongo but it’s not a subclass. Instead, the methods calls are delegated. Despite GMongo, all other classes are the same from the Java driver.
To be more practical, here are some examples:
// To download GMongo on the fly and put it at classpath
@Grab(group='com.gmongo', module='gmongo', version='0.5.1')
import com.gmongo.GMongo
// Instantiate a com.gmongo.GMongo object instead of com.mongodb.Mongo
// The same constructors and methods are available here
def mongo = new GMongo("127.0.0.1", 27017)
// Get a db reference in the old fashion way
def db = mongo.getDB("gmongo")
// Collections can be accessed as a db property (like the javascript API)
assert db.myCollection instanceof com.mongodb.DBCollection
// They also can be accessed with array notation
assert db['my.collection'] instanceof com.mongodb.DBCollection
// Insert a document
db.languages.insert([name: 'Groovy'])
// A less verbose way to do it
db.languages.insert(name: 'Ruby')
// Yet another way
db.languages << [name: 'Python']
// Insert a list of documents
db.languages << [[name: 'Javascript', type: 'prototyped'], [name: 'Ioke', type: 'prototyped']]
def statics = ['Java', 'C', 'VB']
statics.each {
db.languages << [name: it, type: 'static']
}
// Finding the first document
def lang = db.languages.findOne()
assert lang.name == 'Groovy'
// Set a new property
lang.site = 'http://groovy.codehaus.org/'
// Save the new version
db.languages.save lang
assert db.languages.findOne(name: 'Groovy').site == 'http://groovy.codehaus.org/'
// Counting the number of documents in the collection
assert db.languages.find(type: 'static').count() == 3
// Another way to count
assert db.languages.count(type: 'prototyped') == 2
// Updating a document using the '$set' operator
db.languages.update([name: 'Python'], [$set: [paradigms: ['object-oriented', 'functional', 'imperative']]])
assert 3 == db.languages.findOne(name: 'Python').paradigms.size()
// Using upsert
db.languages.update([name: 'Haskel'], [$set: [paradigms: ['functional']]], true)
assert db.languages.findOne(name: 'Haskel')
// Removing some documents
db.languages.remove(type: 'prototyped')
assert 0 == db.languages.count(type: 'prototyped')
// Removing all documents
db.languages.remove([:])
assert 0 == db.languages.count()
// To ensure complete consistency in a session use DB#inRequest
// It is analogous to user DB#requestStarted and DB#requestDone
db.inRequest {
db.languages.insert(name: 'Objective-C')
assert 1 == db.languages.count(name: 'Objective-C')
}
And a simple MapReduce:
@Grab(group='com.gmongo', module='gmongo', version='0.5.1')
import com.gmongo.GMongo
// There is some bug using the [Random] word into codesnipt plugin
import java.util.Random as Rand
def mongo = new GMongo("127.0.0.1", 27017)
def db = mongo.getDB("gmongo")
def words = ['foo', 'bar', 'baz']
def rand = new Rand()
1000.times {
db.words << [word: words[rand.nextInt(3)]]
}
assert db.words.count() == 1000
def result = db.words.mapReduce(
"""
function map() {
emit(this.word, {count: 1})
}
""",
"""
function reduce(key, values) {
var count = 0
for (var i = 0; i < values.length; i++)
count += values[i].count
return {count: count}
}
""",
"mrresult",
[:] // No Query
)
assert db.mrresult.count() == 3
assert db.mrresult.find()*.value*.count.sum() == 1000
It’s a stable version and still a work in progress. The only dependency is the mongo Java driver version 2.0 and, of course, Groovy. It’s tested under Groovy 1.7.2 and 1.7.3. It’s opensource and hosted at http://github.com/poiati/gmongo.
The binaries can be found at Maven Central.
Click here for more details.
Any bug, suggestion or issue, please, contact me.
Hi, I like GMongo, but I think it suffers the same problem as the default driver with respect to the need to call close() no database objects. For a long running process, if there are instances created with new that are not closed, unused threads can accumulate until the JVM is no longer able to allocate new ones and the app freezes. Instead of using new, have you considered a more Groovy like syntax using closures? E.g.
GMongo.collection(collection_name, db) { collection_ref ->
collection_ref.findAll { println it }
...
}
Hello Steve,
I’m not sure about what u mean. The only close I found in the Java API is the
com.mongodb.Mongo#close, and this one close all open connections from the pool.The problem is the JVM overflowing threads limit or the MongoDB connection pool being empty of usable connections? Can u show me some code u want to achieve in the Java driver?
typo: *call close() on database objects.*
Right that is the close that needs to be called. If you have a process that uses new instances of (G)Mongo that are not closed you will eventually run out of threads in the JVM if new is called repeatedly. Of course, the simple answer is don’t create new instances. On that note, I would refine the syntax I suggested in order to avoid passing in a new instance:
def collection(name, location='localhost', port='27017', closure) { ...}
...
collection('my_collection') { coll -> ...}
All the responsibility of maintaining mongo instances can be handled by the class containing the collection method which can ensure only a single connection pool per mongodb server instance exists.
Alternatively, a closure taking the db instance could be used
def database( location='localhost', port='27017', closure) { ... }
database { db ->
db.my_collection...
}
Since the user never gets to call new, resource leaks can be prevented in either scenario.
Prior post was missing name argument for database
def database(name, location='localhost', port='27017', closure) { ... }
...
database("gmongo") { db ->
def words = ['foo', 'bar', 'baz']
def rand = new Rand()
1000.times { db.words << [word: words[rand.nextInt(3)]] }
assert db.words.count() == 1000
}
Hello Steve,
I see your point. But, I don’t like the idea of protect the programmer from himself. Only make sense to had more Mongo instances if you have more than one server to connect, and the developer should be aware of this.
I think the Mongo#close must be used carefully, because of that, I think this should be done explicitly.
Thanks.
[]‘s
I seem to have the same problem as Tony;
groovy.lang.MissingPropertyException: No such property: myCollection for class: com.mongodb.DBApiLayer
However, I only got this problem when I put gmongo-0.5.1.jar and mongo-java-driver-2.0rc4.jar on the classpath myself (and removing the line with @grab)
Any idea’s how this might result in such a problem?
Thanks, Dennis
Hello Denis,
Which version of Groovy are you using?
You really need version 0.5.1? Currently we are in version 0.8:
http://mvnrepository.com/artifact/com.gmongo/gmongo
Hi Paulo,
When using version 0.8 I got the same problem. I am running from eclipse (helios) with groovy 1.7.5. Note that I only have this problem when not using @grab, but adjusting the classpath in my project settings instead.
Dennis
I downloaded Eclipse Helios, installed the Groovy Plugin, added gmongo and mongo-java-driver dependencies… And everything worked just fine.
Are you sure the classpath is configured correctly?
Tks!
[...] ^ GMongo [...]
Hi,
Is it possible to also provide a sort and limit in addition to the query for the mapReduce call? These are permitted options for MongoDB.
db.runCommand(
{ mapreduce : ,
map : ,
reduce :
[, query : ]
[, sort : ]
[, limit : ]
[, out : ]
[, keeptemp: ]
[, finalize : ]
[, scope : ]
[, verbose : true]
}
);
Thanks
Nice article and nice library!!! Very simple and useful!
One question: is it possible to update more than a record in a single statement, like sql?
thanks a lot!
Hello Alessandro. Tks!
Answering:
Yes, it’s possible. Using
updateMulti:@Grab( 'com.gmongo:gmongo:0.8' ) import com.gmongo.GMongo def gmongo = new GMongo( ) def db = gmongo.getDB( 'test' ) db.foo.drop( ) db.foo.insert key: 1, value: 'foo' db.foo.insert key: 2, value: 'bar' db.foo.insert key: 1, value: 'baz' println "Before" db.foo.find( ).each { println it } db.foo.updateMulti( [ key: 1 ], [ $set: [ value: 'qux'] ] ) println "\nAfter" db.foo.find( ).each { println it }Hi Paulo,
Thank you for all your great help when I needed it the most. Your groovy plugin is really great. I wonder if you could be so kind and share some more query tricks, such as filtering, sorting and things that I forgot to mention here.
I will put “Have a gmongo based GORM plugin for Grails that works!” on my X-mas wishlist.
All the best,
—Kai
Nice work Paulo, looking forward to checking this out!
The problem was clearly something on my end and not in your code, so I flailed until I figured it out — I was experimenting with Groovy in Eclipse, and the default Run Configuration it builds via one usage path is wrong. Sorry to clutter the blog
No problem Tony! Any possible problem or suggestion are welcome.
[...] GMongo 0.5 Released [...]
I am having trouble getting this to work in a simple test, using Mongo 1.7.3 and mongo-2.0.jar
It works fine from Java, but with this simple script I get an error. Getting the collection with db.getCollection(“things”) works, but with db.things it does not:
import com.gmongo.GMongo
GMongo mongo = new GMongo(“localhost”)
db = mongo.getDB(“test”)
def colls = db.getCollectionNames()
colls.each { println “Collection: ” + it }
def things1 = db.getCollection(“things”)
println “things1 = ${things1}, class = ${things1.getClass().getName()}”
def things2 = db.things
println “things2 = ${things2}, class = ${things2.getClass().getName()}”
Collection: foo
Collection: system.indexes
Collection: things
things1 = things, class = com.mongodb.DBApiLayer$MyCollection
Caught: groovy.lang.MissingPropertyException: No such property: things for class: com.mongodb.DBApiLayer
at Stuff.run(Stuff.groovy:11)
Hello Tony, did you mean Groovy 1.7.3 ?
I executed your script here, and it works for me.
@Grab("com.gmongo:gmongo:0.5") import com.gmongo.GMongo println System.properties['os.name'] println System.properties['java.vm.name'] println System.properties['java.version'] println System.properties['java.class.path'] println() GMongo mongo = new GMongo("localhost") db = mongo.getDB("test") def colls = db.getCollectionNames() colls.each { println "Collection: " + it } def things1 = db.getCollection("things") println "things1 = ${things1}, class = ${things1.getClass().getName()}" def things2 = db.things println "things2 = ${things2}, class = ${things2.getClass().getName()}"Output:
Mac OS X
Java HotSpot(TM) 64-Bit Server VM
1.6.0_20
/usr/local/langs/groovy/current/lib/groovy-1.7.3.jar
Collection: foo
Collection: system.indexes
Collection: testBinary
things1 = things, class = com.mongodb.DBApiLayer$MyCollection
things2 = things, class = com.mongodb.DBApiLayer$MyCollection
PS: gmongo-0.5 (from maven central) use mongo-java-driver-2.0RC4 as Dep., but it works with 2.0 as well.
Can you give me more details plz.
Tks.
Thank you! That’s perfect….
+1
Thank you! That’s perfect….
I love this! Good work.
One question: can you provide an example of saving into nested collections? I.e., if inside of languages you had a “static” collection and a “dynamic” collection, how would you save into them?
Thanks again!
Mongodb doesn’t support nested collections, what you can do is create two collections, one for each language type:
db[‘languages.static’].insert( ['C', 'Java'] )
db[‘languages.dynamic’].insert( ['Groovy', 'Ruby'] )