Getting all the collection names with MongoDB and AS3
At work I've been making on a quick tool to modify objects in MongoDB for our upcoming game. As I remembered reading about some AS3 drivers that allow you to directly connect to a Mongo database rather than need to use a server script, I decided to take a look. And immediately hit a roadblock.
In the main libs I found; @s9tpepper's MongoAS3 lib, and JMCNet's full-mongo-flex-driver; there was no built-in way to get the collections in a database. If you've worked with Mongo before, you'll know that it's slightly important to know that.
Both libs provided access to the Mongo runCommand()
command, but no combination of keys seemed to do the job. Of course, you could always just keep a static Array
of Strings
, but where's the fun in that?
Other languages and drivers usually have a function along the lines of getCollectionNames()
and with the help of @dun4n's google-fu, we found out how to do it. Mmmm, smells like a hack!
How it works
So, for a database that looks like this:
the basic gist is that you query the system.namespaces
collection for all their entries. That'll returning you something like this:
{ name:test_db.system.indexes}
{ name:test_db.Foo}
{ name:test_db.Foo.$_id_}
{ name:test_db.Bar}
{ name:test_db.Bar.$_id_}
Once you have that, you need to strip out the name
value (the test_db.Foo
part), and ignore:
- Anything with a
$
in it - these are Mongo index collections - Anything with a
.fs.
in it - GridFS stuff - Anything with a
.system.
in it - Mongo database system info
Anything that's left is your collections.
So for both libs, a complete solution:
MongoAS3
The latest SWC didn't work for me - I kept getting an error with the system.namespaces
call - saying that there was a "." in the collection name. Using the code directly instead worked fine, though this means you'll have to have both the AS3Signals and AS3CoreLib in your classpath.
private var m_dbConn:Mongo = null; // our db connection
private var m_collections:Array = []; // our array of collection names
// connects to our db
private function _connect():void
{
// create our mongo object
this.m_dbConn = new Mongo( "localhost", 27017 );
// connect to our db
this.m_dbConn.db( "test_db" ).connected.add( this._onConnect );
this.m_dbConn.db( "test_db" ).connect();
}
// called when we're connected to the db
private function _onConnect( db:DB ):void
{
trace( "Connected to the db, getting the collections" );
this.m_dbConn.db( "test_db" ).collection( "system.namespaces" ).find( new Document, null ).add( this._onCollections );
}
// called when we get a response from our system query
private function _onCollections( cursor:Cursor ):void
{
// get the first document
var doc:Object = cursor.getNextDocument();
// go through them
while ( doc != null )
{
// store our name and get the next doc
var name:String = doc["name"] as String;
doc = cursor.getNextDocument();
// we ignore anything with a $ in it, as though are internal mongo collections.
// likewise, we ignore anything with .fs. or .system. in it
if ( name.lastIndexOf( "$" ) != -1 || name.indexOf( "fs." ) != -1 || name.indexOf( "system." ) != -1 )
continue;
// it's a good name, but it's still in the form
// dbName.collectionName, so strip off the db name
var index:int = name.indexOf( "." );
if ( index != -1 )
name = name.substring( index + 1 );
// add the name to our collection
this.m_collections.push( name );
}
// check for more (can have a large number of collections)
if ( cursor.hasMore() )
cursor.getMore().add( this._onCollections );
else
{
// we're finished
trace( "Finished getting collections (" + this.m_collections.length + "):" );
this.m_collections.sort();
for each( var s:String in this.m_collections )
trace( "\t" + s );
}
}
JMCNet
private var m_dbConn:JMCNetMongoDBDriver = null; // our db connection
private var m_collections:Array = []; // our array of collection names
// connects to our db
private function _connect():void
{
// create our db connection
this.m_dbConn = new JMCNetMongoDBDriver;
this.m_dbConn.hostname = "localhost";
this.m_dbConn.port = 27017;
this.m_dbConn.databaseName = "test_db";
// add our event listener
this.m_dbConn.addEventListener( JMCNetMongoDBDriver.EVT_CONNECTOK, this._onConnect );
this.m_dbConn.connect();
}
// called when we're connected to the db
private function _onConnect( event:EventMongoDB ):void
{
trace( "Connected to the db, getting the collections" );
// query the system namespace
this.m_dbConn.queryDoc( "system.namespaces", new MongoDocumentQuery, new MongoResponder( this._onCollections ) );
}
// called when we get the results of our system namespaces query
private function _onCollections( response:MongoDocumentResponse, token:* ):void
{
// go through the response documents
if ( response.documents != null && response.documents.length > 0 )
{
for each( var doc:MongoDocument in response.documents )
{
var name:String = doc.getValue( "name" ) as String;
// we ignore anything with a $ in it, as though are internal mongo collections.
// likewise, we ignore anything with .fs. or .system. in it
if ( name.lastIndexOf( "$" ) != -1 || name.indexOf( "fs." ) != -1 || name.indexOf( "system." ) != -1 )
continue;
// it's a good name, but it's still in the form
// dbName.collectionName, so strip off the db name
var index:int = name.indexOf( "." );
if ( index != -1 )
name = name.substring( index + 1 );
// add the name to our collection
this.m_collections.push( name );
}
}
// check if we have more
var cursor:Cursor = response.cursorID;
if ( cursor.isset() )
this.m_dbConn.getMoreDoc( "system.namespaces", cursor, new MongoResponder( this._onCollections ) );
else
{
// we're finished
trace( "Finished getting collections (" + this.m_collections.length + "):" );
this.m_collections.sort();
for each( var s:String in this.m_collections )
trace( "\t" + s );
}
}
Files
I've put together a quick FlashDevelop project showing both libs connecting and recovering the collection names. You can download it here.
Comments
Hi, im trying to use JMC library to connect Mongo to AIR, is there anyway to use it in pure ASĀ£ projects without all the mx stuff ?
Thank you in advance for interest.
If you check out the attached project, it's a pure AS3 project; the MX classes in the lib don't stop you using it, or force you to make a Flex project. If you want to completely remove it anyway, it should be possible to download the code from http://jmcnet-full-mongo-flex-driver.googlecode.com/svn/trunk/ and make the relevant substitutions, but I haven't looked into it.
Hi, i used your poejct as refernce and it works fine, still dont understand clearly why :D thank you
Submit a comment