My First Mongo Database

Allow me to present a partial log of my very first experiments with mongoDB.

My ultimate (short-term) goal is to convert a very simple Revit SDK sample called FireRating to a cloud-based multi-project implementation.

The existing sample is a Revit add-in implemented in C# .NET that exports the following data for all door instances in a building model to an Excel spreadsheet, enables the user to edit the external fire rating values, and reimports the modified data to the corresponding elements:

They are used as follows:

I believe and hope I can implement a node.js mongo db server to manage this data for multiple Revit building models quite quickly and easily with very little fuss.

So here goes.

I started off by installing mongoDB and the mongoose node.js library to access it programmatically through JavaScript. I obviously also have node.js and npm already installed and set up.

To run the database, start the mongo database daemon:

$ mongod
mongod --help for help and startup options
2015-06-30T16:12:49.620+0200 [initandlisten] MongoDB starting :
  pid=23595 port=27017 dbpath=/data/db 64-bit host=jeremy.box
2015-06-30T16:12:49.621+0200 [initandlisten]
. . .

You can interact with the database using the interactive mongo shell:

$ mongo
MongoDB shell version: 2.6.4
connecting to: test
> help
	db.help()                    help on db methods
	db.mycoll.help()             help on collection methods
	sh.help()                    sharding helpers
	rs.help()                    replica set helpers
	help admin                   administrative help
  . . .
  exit                         quit the mongo shell
>

This enables me to query the database collections though the command line:

> show dbs
admin              (empty)
firerating         0.078GB
> use firerating
switched to db firerating
> show collections
fireratings
system.indexes

I can drop a collection from the current database:

> show collections
fireratings
system.indexes
> db.fireratings.drop()
true
> show collections
system.indexes

The mongo ObjectId has a minimum length, somewhere between 20 and 24 characters:

> o = new ObjectId()
ObjectId("5592a9f4cd4c9b9e84396e21")
> o = new ObjectId('12345')
2015-06-30T16:38:50.765+0200 Error: invalid object id: length
> o = new ObjectId('1234567890')
2015-06-30T16:38:57.882+0200 Error: invalid object id: length
> o = new ObjectId('1234567890123456')
2015-06-30T16:39:02.442+0200 Error: invalid object id: length
> o = new ObjectId('12345678901234567890')
2015-06-30T16:39:06.346+0200 Error: invalid object id: length
> o = new ObjectId('123456789012345678901234')
ObjectId("123456789012345678901234")

You can shorten the ObjectId representation using base64 encoding, if you like.

I wish to use the existing Revit element UniqueId property as an identifier in my mongo database.

I implemented the following hard-coded node.js server as a proof of concept to ensure that I really can specify my own object id in the mongo db, setting it equal to the pre-existing Revit UniqueId, instead of using the automatic built-in mongo ObjectId generation functionality:

// given: a Revit door element UniqeId;
// it must obviously be unique in the database.

var unique_id
  = '60f91daf-3dd7-4283-a86d-24137b73f3da-0001fd0b';

var mongoose = require( 'mongoose' );

mongoose.connect( 'mongodb://localhost/firerating ');

var Schema = mongoose.Schema;

var RvtUniqueId = String;

var Door = new Schema(
  { _id          : RvtUniqueId // suppress automatic generation
    , level      : String
    , tag        : String
    , firerating : Number },
  { _id: false } // suppress automatic generation
);

var DoorModel = mongoose.model( 'Door', Door );

var instance = new DoorModel();
instance._id = unique_id; // new RvtUniqueId(unique_id);
instance.level = 'Level 1';
instance.tag = 'Tag 1';
instance.firerating = 123.45;

instance.save(function (err) {
  console.log( 'save returned err = ' + err );
});

The complete firerating node.js application lives in its own cosy little firerating GitHub repository, and the version described here is release 0.0.0.

The application implementation also includes another couple of files, e.g., package.json, which specifies which additional npm modules are required.

You need to install the required node modules using npm before executing the server:

$ npm install

Now we can run the application:

$ node server.js
save returned err = null

Doing so displays messages in the mongo console informing us about the generation of the new collection and its index:

2015-06-30T17:10:48.987+0200 [conn39] build index on:
  firerating.doors properties: { v: 1, key: { _id: 1 },
  name: "_id_", ns: "firerating.doors" }

2015-06-30T17:10:48.987+0200 [conn39] 	 added index
  to empty collection

I can examine the resulting mongo document in the interactive console:

> show dbs
admin              (empty)
firerating         0.078GB
> use firerating
switched to db firerating
> db.doors.find()
{ "_id" : "60f91daf-3dd7-4283-a86d-24137b73f3da-0001fd0b",
  "firerating" : 123.45,
  "tag" : "Tag 1",
  "level" : "Level 1",
  "__v" : 0 }

Once the hard-wired door instance has been added to the database and occupies the specified unique id, the command can obviously not be executed again without error:

$ node server.js
save returned err = MongoError: insertDocument
  :: caused by :: 11000 E11000
  duplicate key error index:
  firerating.doors.$_id_  dup key:
  { : "60f91daf-3dd7-4283-a86d-24137b73f3da-0001fd0b" }

Just to prove the case, let's manually remove the one and only entry again manually using the mongo console, then re-execute the server code to successfully reinsert the same document again:

> show dbs
admin              (empty)
firerating         0.078GB
jeremy_first_test  0.078GB
> use jeremy_first_test
switched to db jeremy_first_test
> db.dropDatabase()
{ "dropped" : "jeremy_first_test", "ok" : 1 }
> use firerating
switched to db firerating
> db.doors.find()
{ "_id" : "60f91daf-3dd7-4283-a86d-24137b73f3da-0001fd0b", "firerating" : 123.45, "tag" : "Tag 1", "level" : "Level 1", "__v" : 0 }
> db.doors.remove({ "_id" : "60f91daf-3dd7-4283-a86d-24137b73f3da-0001fd0b"})
WriteResult({ "nRemoved" : 1 })
> db.doors.find()
>

This also shows me dropping an obsolete test database, by the way.

As expected, the node server now reinserts the record without error:

$ node server.js
save returned err = null

Just like before, the door instance is retrieved again by querying the database collection using find:

> db.doors.find()
{ "_id" : "60f91daf-3dd7-4283-a86d-24137b73f3da-0001fd0b",
  "firerating" : 123.45,
  "tag" : "Tag 1",
  "level" : "Level 1",
  "__v" : 0 }

So far, so good.

Here are my plans for my next steps: