Skip to main content
Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.

MongoDB BSON Structs Are Case-Sensitive In Lucee CFML 5.3.6.61

By Ben Nadel on
Tags: ColdFusion

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.



Reader 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.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Blog
Live in the Now
Oops!
NEW: Some basic markdown formatting is now supported: bold, italic, blockquotes, lists, fenced code-blocks. Read more about markdown syntax »
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.