Skip to main content
Ben Nadel at RIA Unleashed (Nov. 2009) with: Steven Erat
Ben Nadel at RIA Unleashed (Nov. 2009) with: Steven Erat

Getting MongoDB Database And Collection Names From The Connection String In Lucee CFML 5.3.6.61

By
Published in

At InVision, I'm working with Boaz Ruck on a little experiment: whereas our ColdFusion application would normally construct a MongoDB connection using a variety of individual values, the Data Services team would like to try providing the entire MongoDB connection configuration as a single environment variable. This way, the Data Service team could tweak the settings without having to touch the application code. This posed an interesting problem because the ColdFusion application is generally tightly coupled to the MongoDB implementation. As such, I needed to figure out how to access a MongoDB Collection using the Java driver without being able to hard-code the database name or the collection name. After some trial-and-error, I figured out how to extract this information from the ConnectionString object in Lucee CFML 5.3.6.61.

So, normally, when creating and consuming a MongoDB document collection, my ColdFusion code will "hard code" the name of the database and the name of the collection:

mongoClient.getDatabase( "bennadel" ).getCollection( "friends" )

ASIDE: In some cases, the name of the MongoDB database is passed-in as a separate environment variable. It depends on how old the code is; newer code tends to be a bit less hard-coded.

However, the Data Services team would like to try passing all of that information in as a single environment variable:

CONNECTION_STRING="mongodb://mongo:27017/bennadel.friends?maxPoolSize=5"

In the ColdFusion code, however, I still have to make a .getDatabase(name) call followed by a .getCollection(name) call before I can start reading and writing MongoDB documents. Which means, my code still has to reference a database name and a collection name. It doesn't appear that I can get this information from the MongoClient client; but, if I construct a ConnectionString class from the environment variable, I can then extract the database and collection names from the connection string. This would allow me to consume then in an opaque manner in my Lucee CFML:

<cfscript>

	try {

		// For the sake of the demo, let's assume that we have some sort of connection
		// string being provided as an environmental variable. This connection string
		// will include both the DATABASE and the COLLECTION that the application code
		// is going to consume (as well as any other options).
		ENV_CONNECTION_STRING = "mongodb://mongo:27017/bennadel.friends?maxPoolSize=5";

		// Since the application is going to remain unaware of the implementation
		// details of the collection to which it is connecting, we need to create a
		// ConnectionString object from which we can extract these "opaque" values.
		mongoConnectionString = loadClass( "com.mongodb.ConnectionString" )
			.init( ENV_CONNECTION_STRING )
		;

		mongoClient = loadClass( "com.mongodb.client.MongoClients" )
			.create( mongoConnectionString )
		;

		// When accessing the MongoDB collection, notice that we are using the Database
		// and the Collection defined in the ConnectionString instance - this way, as the
		// ENV variable changes, this code will change, transparently, in lock-step.
		collection = mongoClient
			.getDatabase( mongoConnectionString.getDatabase() )
			.getCollection( mongoConnectionString.getCollection() )
		;

		results = collection
			.find(
				toBson([
					id: 3
				])
			)
			.into( [] )
			.map( fromBson )
		;

		dump( results );

	} catch ( any error ) {

		dump( error );

	}

	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //

	/**
	* I load the given Java class.
	* 
	* @className I am the class definition being loaded.
	*/
	public any function loadClass( required string className ) {

		return( createObject( "java", className ) );

	}


	/**
	* I convert the given Struct into a BSON Document for use in a MongoDB query.
	* 
	* CAUTION: Having a predictable key-order is CRITICAL FOR A MONGODB QUERY. As such,
	* this method is expecting the struct to be a LINKED / ORDERED struct, which allows
	* the keys to be ITERATED in the same order in which they were DEFINED. This is a
	* hard requirement of how the MongoDB API works.
	* 
	* @orderedKeyValuePairs I am the linked / ordered struct to convert to a BSON document.
	*/
	public any function toBson( required struct orderedKeyValuePairs ) {

		return( loadClass( "org.bson.Document" ).init( orderedKeyValuePairs ) );

	}


	/**
	* When a document comes out of the MongoDB Java driver, it is a BSON document. The
	* BSON document implements the MAP interface; but, sometimes, it can cause some
	* interoperability issues. As such, let's unwrap the BSON document and downcast it to
	* a normal Lucee CFML struct.
	* 
	* NOTE: This isn't strictly necessary - this is really just a fun EXPLORATION for me
	* to make the demo a little bit more interesting.
	* 
	* @bsonDocument I am the MongoDB BSON document being cast to a Struct.
	*/
	public struct function fromBson( required any bsonDocument ) {

		var unwrappedStruct = structNew( "linked" ).append( bsonDocument );

		loop
			index = "local.key"
			item = "local.value"
			struct = unwrappedStruct
			{

			if ( ! isNull( value ) && isStruct( value ) ) {

				unwrappedStruct[ key ] = fromBson( value );

			}

		}

		return( unwrappedStruct );

	}

</cfscript>

As you can see, when I connect to the MongoDB database, my code isn't strictly aware of the database name or the collection name. Instead, it is calling these two methods, respectively:

  • mongoConnectionString.getDatabase()
  • mongoConnectionString.getCollection()

And, when we run this ColdFusion code, we get the following output:

A results of a MongoDB document query in Lucee CFML.

In reality, the Data Services doesn't actually want to provide the Collection name - that was just me exploring the MongoDB Java driver API in Lucee CFML 5.3.6.61. But, they do want to be able to opaquely provide the database name, pool size, SSL flag, read-replica configuration, cluster settings, and all the other low-level write-priority kinds of stuff. And, it's nice to see that I can still connect-to and consume the MongoDB documents without having to know any of that in my ColdFusion code.

Want to use code from this post? Check out the license.

Reader Comments

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel