Skip to main content
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with: David Frazier and Vicky Ryder and Saurin Shah
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with: David Frazier ( @susqurugger ) Vicky Ryder ( @fuzie ) Saurin Shah ( @vtecnrg )

Strange MongoDB Error When Incrementing A Document Property In Lucee CFML 5.2.9.31

By on
Tags:

Yesterday, I ran into a really strange error when trying to increment a numeric value on an in-memory MongoDB document in Lucee CFML 5.2.9.31. Google didn't really provide any answers; so, I wanted to document the error here and share the work-around that I finally used.

My ColdFusion code was attempting to read-in a MongoDB document that had a numeric property. Then, I was performing some logic around said document before incrementing that numeric property and saving the document back to the MongoDB database.

At first, I used the post-increment operator: ++. This resulted in two different errors, randomly depending on the request:

  • java.lang.IncompatibleClassChangeError - vtable stub

  • java.lang.IncompatibleClassChangeError - Class org.bson.Document does not implement the requested interface lucee.runtime.type.Collection

I have no idea what that first error, "vtable stub", means - I've never seen that one before. Most of the time, the issue manifested as the second error about the Lucee Collection interface. This makes me think the issue has something to do with how this CFML code is being compiled (ie, it's probably not an issue with the Java driver for MongoDB itself); but, that's just a guess on my part.

Eventually, I replaced the ++ post-increment operator with a full re-assignment of the numeric value and that worked. To see this in action, take a look at this minimal reproduction code - it creates a MongoDB document with a numeric count property. Then, it reads that document back into the ColdFusion memory-space and attempts to increment the count using several different expressions:

<cfscript>

	// Create an instance of the MongdDB Java Driver, version 3.9.1.
	mongoDB = createObject( "java", "com.mongodb.MongoClient" )
		.init( "mongo36", 27018 )
	;

	// Get our collection and TRUNCATE it so that our Insert will result in a single
	// document within the collection.
	collection = mongoDB
		.getDatabase( "testing" )
		.getCollection( "values" )
	;
	collection.drop();

	// Insert our test document - notice that the COUNT value is a NUMBER.
	collection.insertOne(
		createObject( "java", "org.bson.Document" ).init({
			count: 1
		})
	);

	// Read the document we just inserted.
	doc = collection
		.find()
		.into( [] )
		.first()
	;

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

	// Let's output the read-in document so we can see the data-types.
	dump( doc );
	dump( getMetadata( doc.count ).getName() );
	echo( "<hr />" );

	// First, let's try to use the POST-INCREMENT operator to increase the count.
	try {

		echo( "<p> Trying Post-Increment Operator. </p>" );
		doc.count++;

	} catch ( any error ) {

		dump(
			label = "Post-Increment error",
			var = error,
			hide = "additional,ErrorCode,Extended_Info,ExtendedInfo,StackTrace,TagContext"
		);

	}

	// Second, let's try to use the ADDITION-ASSIGNMENT operator to increase the count. 
	try {

		echo( "<p> Trying Addition-Assignment Operator. </p>" );
		doc.count += 1;

	} catch ( any error ) {

		dump(
			label = "Addition-Assignment error",
			var = error,
			hide = "additional,ErrorCode,Extended_Info,ExtendedInfo,StackTrace,TagContext"
		);

	}

	// Finally, let's try a full re-assignment operator.
	try {

		echo( "<p> Trying Re-Assignment Operator. </p>" );
		doc.count = ( doc.count + 1 );

	} catch ( any error ) {

		dump(
			label = "Re-Assignment error",
			var = error,
			hide = "additional,ErrorCode,Extended_Info,ExtendedInfo,StackTrace,TagContext"
		);

	}

</cfscript>

As you can see, we're using three different approaches when incrementing the count property value:

  • doc.count++
  • doc.count += 1
  • doc.count = ( doc.count + 1 )

The first two throw an error, the third one succeeds:

Various attempts to increment a numeric MongoDB document property in Lucee CFML 5.2.9.31.

To me, these three assignment operations are equivalent. Which makes me think this is just something strange in the way the Lucee CFML code is being compiled to Java Byte code. With that said, I just used the full re-assignment operation and moved on with my ColdFusoin application development. I'm just sharing this here in case anyone else tries to Google for the same problem.

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

Reader Comments

15,688 Comments

I just tested this on Lucee CFML 5.3.3.62 and it results in the same error (though, I was using a different MongoDB Java driver - 3.4.2 - which I installed via the Lucee Server admin).

45 Comments

Ben, did you put in a ticket for this? I just had a similar ticket this week related to Lucee not properly being able to do a for in loop over a Java "Set" because it wasn't a Lucee Collection. Micha already fixed it just yesterday. Mongo seems common enough he may be able to make Lucee recognise that Document class as a collection as well.

15,688 Comments

@Brad,

I have not. Can you point me to how to submit tickets. I've honestly tried to do it a few times, and I never quite figure it out. Is it documented anywhere?

45 Comments

The Lucee ticket tracker is located here:

https://luceeserver.atlassian.net/

They use an on-demand JIRA instance and it's free and quick to sign up for an account.

I would recommend keeping the ticket simple. Explain it will come in handy for anyone using MongoDB and include a very simple reproducible example that shows the error.

5 Comments

In the past, I worked on a project that used Marc Esher's CFMongoDB.

Similar errors occasionally occurred during daily development. The first step in diagnosis was to determine if the java mongo document was being converted to a native Lucee struct via the toCF() function within cfmongodb.core.MongoUtil .

While I'm not sure if this is applicable to your issue, the conversion of native java to CF was generally a huge hair pulling deterrent. :)

Cheers!

15,688 Comments

@MrV,

Good insight, I am not sure how the data is being converted under the hood. That said, one of the things that I am loving about Lucee CFML is that, because of the native data types and ordered structs, I can actually get much closer to the MongoDB API. For example, being able to create a org.bson.Document document directly from a Lucee Struct, and then being able to pipe it right into an Array with .into([]) is just a game-changer in terms of complexity.

Every day, I'm just loving Lucee CFML more and more.

15,688 Comments

@All,

I've been working a bit more with MongoDB lately; and, I've started to think about "unwrapping" all data that comes out of the database so that the concept of BSON never "leaks" out into the calling context at all:

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

Since this post on incrementing values, I've also run into other issues relating to BSON (and its divergence from a ColdFusion-native Struct implementation). I'll follow up with that specifically; but, overall, I'm liking the idea of wrapping / unwrapping data as it moves into and out of a MongoDB database, respectively.

15,688 Comments

@All,

I just ran into another slightly unexpected behavior in the BSON documents returned from MongoDB in ColdFusion - unlike native ColdFusion Structs, the BSON documented as case-sensitive:

www.bennadel.com/blog/3915-mongodb-bson-structs-are-case-sensitive-in-lucee-cfml-5-3-6-61.htm

This cause an issue in our legacy code where inconsistent key-casing lead to logic-blocks not executing when the MongoDB results appeared to not have the expected document-keys (which turned out to just be a key-casing issue).

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