Skip to main content
Ben Nadel at dev.Objective() 2015 (Bloomington, MN) with: Jeff Kunkel
Ben Nadel at dev.Objective() 2015 (Bloomington, MN) with: Jeff Kunkel

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

By
Published in Comments (1)

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.

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

Reader Comments

15,848 Comments

@All,

Knowing that ordered / linked Structs are critical for MongoDB queries, I wanted to figure out how I might be able to inspect the type of a Struct such that I might be able to enforce an ordered struct inside a MongoDB gateway component in ColdFusion:

www.bennadel.com/blog/3909-checking-to-see-if-a-struct-is-of-type-ordered-linked-in-lucee-cfml-5-3-6-61.htm

Lucee CFML doesn't expose any decision functions about Struct Type (that I could find); so, I had to dip into the Java layer, which does expose a .getType() function.

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