Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Asher Snyder
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Asher Snyder@ashyboy )

Ordered Structs Are Perfect For Creating MongoDB BSON Documents In Lucee 5.3.2.77

By Ben Nadel on
Tags: ColdFusion

Yesterday, I took a look at using ordered, or linked, structs in Lucee 5.3.2.77. These are Structs in which the order of the iteration of the keys matches the order of the insertion of the keys. Internally to ColdFusion, having a predictable Struct iteration order is almost always unnecessary; but, where this feature can shine is when crossing boundaries into an external system. One such example of this is when communicating with a MongoDB document database. MongoDB uses the Hash / Map data-type in a ton of places; and, unlike with ColdFusion, MongoDB relies heavily on a predictable Map iteration. As such, we can use ordered, or linked, Structs to make communicating with MongoDB fairly seamless in Lucee 5.3.2.77.

To demonstrate the benefit of using ordered structs in the communication with MongoDB, we can try to create a few indexes on a MongoDB collection. An index is created using a Map (which is the Abstraction that the ColdFusion Struct implements); and, the order of the properties in the index are defined by the iteration order of the keys in the provided Map.

If you recall from my article on database index design, the order of the columns in an index is critical because it determines the various ways in which you can use the index during a database search. As such, if you create an index with poor, or unpredictable, column order, you won't be able to leverage the index for faster data access.

To see how ordered structs compare to the default struct type in Lucee ColdFusion, let's try to create the same index (ie, an index with the same columns) using both an ordered struct and the default struct. Then, we can dump the indexes to the browser and see what was created:

<cfscript>

	collection = getMongoCollection( "ben_test" );

	// Drop the collection so we can see the freshly-created indexes in the demo.
	collection.drop();
	sleep( 500 );

	// Create a MongoDB index using an ORDERED STRUCT.
	// --
	// NOTE: The reason the "ordered" nature of the Struct is important is because the
	// order of the keys in the Struct dictate the ORDER OF THE PROPERTIES IN THE INDEX.
	collection.createIndex(
		load( "com.mongodb.BasicDBObject" ).init(
			[
				z: 1,
				o: 1,
				a: 1
			]
		),
		load( "com.mongodb.client.model.IndexOptions" ).init().name( "via_ordered_struct" )
	);

	// Create a MongoDB index using a NORMAL STRUCT.
	// --
	// CAUTION: Since we are using a normal Struct, the order of the keys in the Struct
	// is unpredictable. This will lead to unexpected and undesired index creation.
	collection.createIndex(
		load( "com.mongodb.BasicDBObject" ).init(
			{
				z: 1,
				o: 1,
				a: 1
			}
		),
		load( "com.mongodb.client.model.IndexOptions" ).init().name( "via_normal_struct" )
	);

	// Output the collection indexes that we just created.
	dump(
		label = "MongoDB Collection Indexes",
		var = collection.listIndexes().into( [] )
	);

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

	/**
	* I return the MongoDB collection with the given name.
	* 
	* @name I am the name of the collection being accessed.
	*/
	public any function getMongoCollection( required string name ) {

		var mongoClient = load( "com.mongodb.client.MongoClients" )
			.create( "mongodb://127.0.0.1:27017" )
		;
	
		var db = mongoClient.getDatabase( "invision" );
		var collection = db.getCollection( name );

		return( collection );

	}


	/**
	* I load the given Java Class Path out of the MongoDB JAR files.
	* 
	* @classPath I am the Java Class being loaded.
	*/
	public any function load( required string classPath ) {

		// This JAR file was downloaded from Maven:
		// --
		// https://mvnrepository.com/artifact/org.mongodb/mongo-java-driver/3.10.2
		var jarPaths = [
			expandPath( "./vendor/mongo-java-driver-3.10.2.jar" )
		];

		return( createObject( "java", classPath, jarPaths ) );

	}

</cfscript>

As you can see, both of the indexes that we are creating are attempting to use the columns: ( z, o, a ). The first attempt uses an ordered struct when generating the BSON document; the second attempt uses the default struct. Now, when we run this CFML code, we get the following output:

Using an ordered struct of MongoDB index creation leads to predicable key ordering in Lucee 5.3.2.77.

As you can see, when using the ordered, or linked, struct, we were able to create a MongoDB collection index with the appropriately-ordered keys. However, when we used a normal struct, our index was created with a reversed set of keys (which almost certainly makes the index unusable).

And, if we refresh the page (dropping and re-creating the indexes), we get the following output:

Using a normal struct to define an index will lead to random and incorrectly ordered keys in Lucee 5.3.2.77.

As you can see, when we refreshed the page and recreated the MongoDB indexes, the shape of the index created using the default struct is different. This is because the order of the keys in the default struct is unpredictable; and, in this case, appears to lead to randomly-shaped BSON documents.

Within the boundary of your Lucee ColdFusion application, you'll almost never need to use an ordered struct. However, providing predictable iteration for a struct can be very helpful when crossing the application boundary into an external system. Communicating with MongoDB is one place this will really shine. Since MongoDB deals with "ordered maps", being able to define ordered structs in Lucee 5.3.2.77 is going to make for some effortless database communication.



Reader Comments

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
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.