Skip to main content
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with: Brian Swartzfager and Simon Free and Jason Dean and Jim Priest and Vicky Ryder and Dan Wilson
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with: Brian Swartzfager Simon Free Jason Dean Jim Priest Vicky Ryder Dan Wilson

MongoDB BSON Structs Are Case-Sensitive In Lucee CFML 5.3.6.61

By
Published in Comments (2)

Yesterday, I started to consider what a data-access component for MongoDB might look like in Lucee CFML. This thought process was initiated by the fact that I ran into a surprising key-case-sensitivity issue with the BSON documents returned by the MongoDB Java Driver. This isn't the first time that I've tripped over unexpected BSON document behaviors (see my post on incrementing BSON key-values); but, since I do enjoy documenting all of my problems, I figured I'd share this one as well in Lucee CFML 5.3.6.61.

ColdFusion / CFML is a case-insensitive language (for the most part). Personally, I've never really felt this was a "feature"; but, it is what it is. And, part of that case-insensitive behavior is the ability to look up Struct keys with arbitrary key-casing.

When querying a MongoDB database using the Java Driver in ColdFusion, results are returned to the ColdFusion context as BSON objects. BSON implements the Map interface, the same as any ColdFusion Struct. But, BSON keys are case sensitive. This can cause issues if you are working with legacy code that has inconsistent key-casing (as I often am).

To see this in action, we don't even need to query MongoDB - we can just instantiate a BSON document and try to access the keys:

<cfscript>

	nativeStruct = [ "foo" : "bar" ];
	bsonStruct = toBson( nativeStruct );

	echo( "<b>Testing Native ColdFusion Struct</b> <br />" );
	echo( "<code>foo</code> exists: #nativeStruct.keyExists( 'foo' )# <br />" );
	echo( "<code>FOO</code> exists: #nativeStruct.keyExists( 'FOO' )# <br />" );
	echo( "<br />" );

	echo( "<b>Testing BSON Struct</b> <br />" );
	echo( "<code>foo</code> exists: #bsonStruct.keyExists( 'foo' )# <br />" );
	echo( "<code>FOO</code> exists: #bsonStruct.keyExists( 'FOO' )# <br />" );
	echo( "<br />" );	

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

	/**
	* I perform a SHALLOW CONVERSION of the given Struct to a BSON document.
	* 
	* CAUTION: For OPERATION documents (such as those defining a search filter), the
	* passed-in Struct should be a LINKED / ORDERED struct so that the keys will be
	* output in the same order in which they were defined. Depending on the operation in
	* question, this may be a hard requirement of how the MongoDB API works.
	* 
	* @value I am the struct to convert to a BSON document.
	*/
	public any function toBson( required struct value ) {

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

	}


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

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

	}

</cfscript>

As you can see, I am creating a linked / ordered struct, which I often do when interacting with MongoDB. Then, I perform a shallow-conversion of that native ColdFusion struct to a BSON document. I then try to look up correct and incorrect key-cased keys in both objects. And, here's what we get:

Key-access is case-sensitive in BSON documents in Lucee CFML.

As you can see, after I define both a Struct and a BSON document with key, foo, trying to access the key, FOO (all uppercase), works fine, as expected, on the native ColdFusion struct; but, fails on the BSON document.

Again, if you just use consistent key-casing in your ColdFusion code, this would never even be an issue. But, when working with legacy code, this isn't always the case (no pun intended). As such, it can be important to know that the "Struct behavior" of a BSON document coming out of MongoDB diverges from the behavior of native ColdFusion Structs in Lucee CFML 5.3.6.61.

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

Reader Comments

15,848 Comments

@Zachary,

Yeah, I think that would also do the trick. I was actually contemplating creating a fromBson() function that would use that exact idea:

public any function fromBson( required any value ) {

	return( deserializeJson( serializeJson( value ) ) );

}

I was also considering use an .append() approach as well, which would perform a shallow conversion back to the native Struct:

public struct function fromBson( required any value ) {

	return( {}.append( value ) );

}

Ultimately, what I think I might go with is just explicitly creating Structs with explicit keys, as I look at in my previous post:

www.bennadel.com/blog/3914-considering-data-workflows-within-a-mongodb-data-access-layer-in-lucee-cfml-5-3-6-61.htm

This way, I can see exactly what comes out of the MongoDB database. I feel like you get a better sense of what the code is actually doing.

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