← servoy magazine

[Tip] Connect Servoy to MongoDB

  • Tips

We changed the data structure in the upcoming release of Sutra CMS to key/value pairs for a number of tables. Our aim was to nip runaway table creation in the bud (like what happens in Concrete5 for every new block you install) and potentially add the ability to use a NoSQL data storage for scaling. Sutra CMS is multi-tenant and multi-site and with clustered Servoy servers the RDBMS could conceivably be a limiting factor.

I finally sat down this evening to see if I could get Servoy and MongoDB to talk to each other. Turns out it is stupid easy. 

MongoDB download and installation instructions:
http://www.mongodb.org/display/DOCS/Quickstart

Download and put the mongo java driver into Servoy's plugin directory:
https://github.com/mongodb/mongo-java-driver/downloads

Furthermore, MongoDB's data format (BSON) and easy syntax is second nature to a Servoy javascript programmer. It almost has me thinking it might almost be easier to use MongoDB instead of an RDBMS for certain types of programming like meta data driven frameworks, solution model structures, configurations, etc.

I've gone ahead and pasted my inline notes along with the code as I have a couple of outstanding questions to still check on. I want to give the mongodb-rhino jars a testing for the automatic to/from javascript native object conversion and I suspect running all code through the headless client plugin is the proper approach. If you have any ideas, comments appreciated!

 Servoy javascript code to test with:

 

/**
 * MONGODB TESTING
 * 
 * This test method uses the default Java driver placed in the plugins directory.
 * Startup MongoDB first: ./mongod
 * 
 * My guess is that all MongoDB code will need to be run through headless client plugin in production.
 * This way Servoy Server is the client to MongoDB and handles all connection pooling. Need to verify.
 * 
 * Testing MongoDB as optional data storage for "web_block_data" table which is key/value pairs to begin
 * with and in a multi-tenant, multi-website environment could get quite large comparitive to other tables.
 * 
 * Logging and session tables are additional candidates.
 * 
 * Future: checkout mongodb-rhino. allows conversion to/from native JavaScript objects and MongoDB's BSON
 * 	(do away with get and put to build and retrieve BSON objects)
 * http://code.google.com/p/mongodb-rhino/
 * 
 * @author David Workman, Data Mosaic, November 2011
 * 
 * @properties={typeid:24,uuid:"BA231E32-D846-433D-BBF5-260D5590DD17"}
 */
function test() {

	// header
	importClass(Packages.com.mongodb.Mongo)
	importClass(Packages.com.mongodb.DB)
	importClass(Packages.com.mongodb.DBCollection)
	importClass(Packages.com.mongodb.BasicDBObject)
	importClass(Packages.com.mongodb.DBObject)
	importClass(Packages.com.mongodb.DBCursor)
	
	// connection
	var m = new Mongo()
	
	// get database and collection
	var db = m.getDB( "cms" ) 
	var coll = db.getCollection("block_data")
	
	// insert data	
	if ( !coll.findOne() ) {  // already inserted
		
		// complex document
		var doc = new BasicDBObject()
		doc.put("name", "MongoDB")
		doc.put("type", "database")
		doc.put("count", 1)
		var info = new BasicDBObject()
		info.put("x", 203)
		info.put("y", 102)
		doc.put("info", info)
	
		coll.insert(doc)
		
		// insert sample simple documents
		for (var i = 0; i < 100; i++) {
			coll.insert(new BasicDBObject().append("i", i));
		}
	}
	
	// count
	application.output(coll.getCount())  // = 101
	
	// first record
	var doc = coll.findOne()
	application.output(doc)
	application.output(doc.get("name"))
	
	// simple query
	var query = new BasicDBObject()
	query.put("i", 71)
	cur = coll.find(query)
	while(cur.hasNext()) {
		application.output(cur.next())
	}
	
	// set of documents
	var query = new BasicDBObject()
    query.put("i", new BasicDBObject("$gt", 95));  // e.g. find all where i > 95
	cur = coll.find(query)
	while(cur.hasNext()) {
		application.output(cur.next())
	}

}

Comments (2)

Servoy Stuff
You might also want to have a look at the MongoDB Connector plugin on ServoyForge: https://www.servoyforge.net/projects/mongodb The only trouble I have with your approach is that the way you do it (and the way the plugin is also doing it) is to access the MongoDB directly from the client side: potential security troubles here IMHO. It might help performance to do it this way but realize that you are bypassing the server entirely here... Also your approach would have problems in different environments with strict firewall rules. This doesn't really fit the Servoy way of accessing the data from the Server only, see my new article https://www.servoyforge.net/projects/articles/wiki/CodeExecution about the Servoy architecture. The way I would do it is to build a server plugin that would deal with MongoDB interaction on the server side only and the client would then be dealing with the server the way it always do, communicating with the server side plugin. Or as you said have a headless client which sole purpose would be to communicate with the MongoDB server side on behalf of the real client.
David Workman
Ah k, very cool. Suspicion confirmed and explained so that even I understand it :) And with a dedicated server plugin vs headless a headless client plugin call -- wouldn't have to worry about dealing with callbacks. Although, having the option to offload to a separate thread for large blob uploading would be good. Maybe combine somehow with the code you put into the file plugin that does file streaming....