Debugging “object has no properties” in a MongoDB map/reduce

Back | mongoid, javascript, mongodb | 11/29/2012 |

You got to love debugging JavaScript errors. Here’s one from a MongoDB map/reduce. The operation fails with this:

  1. 04:39:41  Database command 'mapreduce' failed: (
  2.     assertion: 'map invoke failed: JS Error:
  3.         TypeError: spline has no properties nofile_b:7';
  4.     assertionCode: '9014';
  5.     errmsg: 'db assertion failure';
  6.     ok: '0.0').

Examining the MongoDB log we notice that the map/reduce completes to about 70%, so this is caused by some data inside the database. In this case the issue is in the spline object being accessed by the map function. Indeed, the map tries to fetch spline.reticulated, which is hopefully a Boolean value. The error “spline has no properties” is, generally, another way of saying “spline is null”. In my case splines live in an embedded collection inside bones, and aren’t expected to be null, so I need to track down a null spline inside bone.splines.

While we can just look for a null embedded object, more generally, we can find whether a JS object has no properties with this isEmpty function from SO. Declare it in the mongodb console (use a 1-liner):

  1. function isEmpty(map) {
  2.     for(var key in map) {
  3.         if (map.hasOwnProperty(key)) {
  4.             return false;
  5.         }
  6.     }
  7.     return true;
  8. }

Iterate over all bones records.

  1. db.bones.find().forEach(function(bone) {
  2.     if (bone.splines) {
  3.         bone.splines.forEach(function(spline) {
  4.             if (isEmpty(spline)) {
  5.                 printjson(bone);
  6.             }
  7.         }
  8.     );
  9. }});

Here’s the culprit, notice the null embedded spline.

  1. Here's the culprit:
  2.  
  3. {
  4.     "_id" : ObjectId("507ea06bd646a40002000759"),
  5.     "splines" : [
  6.         {
  7.             "_id" : ObjectId("508a6911c6e210000200078c"),
  8.                         ....
  9.         },
  10.         null
  11.     ],

You can get rid of it in different ways on the mongo console. In this case we have two splines, one to keep and another to erase.

  1. var bone = db.bones.findOne({ _id: ObjectId("507ea06bd646a40002000759") })
  2. bone.splines = [ bone.splines[0] ];
  3. db.bones.save(a);

How did we get into having this null record anyway? See https://jira.mongodb.org/browse/SERVER-831 and https://github.com/mongoid/mongoid/issues/2545.