MongoDB Xojo Driver (maybe)

I think Gavin is using the same excuse :wink:

:smiley: I am sure he isā€¦

@Alwyn Bester , If you donā€™t want to much spam it may be better to remove your email-address in our Apache conversation. I donā€™t know how to start a private conversation therefor i misuse this one, sorry for that!

Removed my email address and sent you a PM. Thanks for the heads up.

Itā€™s a great idea! it works well and it is still maintained?

Surprisingly, I saw that there could be only one write simultaneously (like SQLite, WAL mode).

The driver works on Windows?
Thank you!
Olivier

Hi Olivier,

I havenā€™t spent much time on the driver during the past year.

As far as I know the driver works well, and except for some advanced features that still needs to be implemented, there arenā€™t any issues Iā€™m aware of.

The driver was written and tested on Windows, so it definitely works on Windows.

I havenā€™t tested the driver with the latest version of MongoDB, but no one has complained yet.

ok thank you Alwyn!

Hi guys,

The BSON Serializer used by the MongoDB driver has now been updated with support for empty JSON objects. You can pull the latest version of the module from

https://github.com/alwyn1024/mongodb-xojodriver

[quote=170640:@Alwyn Bester]Hi guys,

The BSON Serializer used by the MongoDB driver has now been updated with support for empty JSON objects. You can pull the latest version of the module from

https://github.com/alwyn1024/mongodb-xojodriver[/quote]
Thanks Alwyn and great work!

Thanks Mike :wink:

+1000

I have question about the implementation of cursors.

If I have a find() cursor and Iā€™m looping through its contents, but as Iā€™m looping through I am updating documents that would in theory change the results from the find query. Is this problematic? Or does the cursor maintain its results once it is created?

Hi Brock,

Been a while since Iā€™ve worked on the MongoDB driver, but if I remember correctly, the Cursor class caches the results in a property named ā€œDocumentā€, which is simply a string array. Each time you call the Cursor.GetNext method, it simply returns the next element in the array.

The cursor therefore maintains the results once it is created. You might have to re-query after making changes.

[quote=222055:@Alwyn Bester]Hi Brock,

Been a while since Iā€™ve worked on the MongoDB driver, but if I remember correctly, the Cursor class caches the results in a property named ā€œDocumentā€, which is simply a string array. Each time you call the Cursor.GetNext method, it simply returns the next element in the array.

The cursor therefore maintains the results once it is created. You might have to re-query after making changes[/quote]
Thanks @Alwyn Bester
In my case it actually works better that it caches the results. I really appreciate your driver. I wouldnā€™t even be working with Mongo in Xojo if it didnā€™t exist.

However, I canā€™t seem to get an update statement to work. Hereā€™s the simplified version:

dim query as string = "{'_id': '5624fd7cc966e66f14cdfec7'}" dim cursor as MongoCursor = collection.find(query) While cursor.hasNext dim update as string = "{$set: {Test: 'yay'}}" collection.update(query, update) wend
The cursor does indeed get a result (1 to be precise since I found it by id for testing), but the Update() method does not seem to work. The weird thing is if I run this in mongo it works fine:

db.getCollection('TransactionDetailRet').update({'_id': ObjectId('5624fd7cc966e66f14cdfec7')}, {$set: {'Test': true}})

Iā€™ve tried all different ways of changing the update parameter:
ā€œ{$set: {Test: ā€˜yayā€™}}ā€
ā€œ{$set: {Test: false}}ā€
ā€œ{ā€™$setā€™: {ā€˜Testā€™: ā€˜falseā€™}}ā€
ā€œ$set: {Test: ā€˜yayā€™}ā€
etcā€¦

And nothing seems to workā€¦ Does anyone know whatā€™s wrong?

==========
Also Iā€™ve updated the private properties to be protected properties instead on the MongoDriver. This allows subclassing them to have access which is helpful. Can we add this to the hosted version?

Iā€™ve pushed a new version to GitHub where all the private properties are now protected propertiesā€¦

Have you tried:

    dim update as string = "{Test: 'yay'}"
    collection.update(query, update)

instead of

    dim update as string = "{$set: {Test: 'yay'}}"
    collection.update(query, update)

[quote=222154:@Alwyn Bester]Iā€™ve pushed a new version to GitHub where all the private properties are now protected propertiesā€¦
[/quote]
Perfect. Thanks Alwyn!

[quote=222154:@Alwyn Bester]
Have you tried:

    dim update as string = "{Test: 'yay'}"
    collection.update(query, update)

instead of

dim update as string = "{$set: {Test: 'yay'}}" collection.update(query, update) [/quote]
Unfortunately that will replace the entire document rather than adding the new property I want to the document.

EDIT=====
Not sure what changed, but the update statement seems to be working now.

Just some food for thought on the driver.

I currently have a helper method to get the collection from a cursor:

[code]Function GetCollection(extends cursor as MongoDriver.MongoCursor) As MongoDriver.MongoCollection
//Find the Client (itā€™s currently a protected property)
dim client as MongoDriver.MongoClient
dim props() as Introspection.PropertyInfo = Introspection.GetType(cursor).GetProperties
For each prop as Introspection.PropertyInfo in props
if prop.Name = ā€œmClientā€ then
client = MongoDriver.MongoClient(prop.Value(cursor).ObjectValue)
exit
end
next

//Generate the Database and Collection from the cursorā€™s client if it exists
if client <> nil then
dim fullCollectionName as string = cursor.FullCollectionName
dim database as MongoDriver.MongoDatabase = client.getDB(fullCollectionName.NthField(".",1))
dim collection as MongoDriver.MongoCollection = database.getCollection(fullCollectionName.NthField(".",2))
return collection
end
return nil
End Function
[/code]

This could be vastly improved if a few things changed.
1)
Instead of having the cursor have a ā€œmClientā€ property it would be better if it had ā€œmCollectionā€
2)
Add these accessor methods on the MongoDriver classes:
Cursor.GetCollection() as MongoCollection //Returns mCollection
Collection.GetDatabase() as MongoDatabase //Returns mDatabase
Database.GetClient as MongoClient //Returns mClient

This would remove the need for my extension method.

I currently have a class called mongoClass that I subClass. The constructor of the mongoClass takes in a cursor, and then Deserializes its JsonObject into the class. This allows me to do things like this:

dim records() as myInvoiceClass //myInvoiceClass is a Subclass of mongoClass While cursor.hasNext records.Append new myInvoiceClass(cursor) //Constructor sets class properties from the cursor wend

And:

dim myInvoice as new myInvoiceClass(cursor) myInvoice.InvoiceNumber ="12345" myInvoice.SaveChanges

Having access to the Collection/Database/Client is key to making this all work. It would be nice if I didnā€™t have to hack using Introspection to get them.

Brock, I will definitely look into making these changes for you. It will benefit other users as well so thanks for mentioning this.

If you donā€™t mind, my schedule is a bit tight at the moment, but I should have some time during the weekend to update the driver with these changes for you.

Thanks @Alwyn Bester , you rock!

I had some other thoughts for future improvements of the Driver.
I have a Jquery library that strings together commands and it seems like the mongo library would benefit from something similar.
Essentially it looks like this:

myWebControl.Jquery.css("color","red").css("text-color","blue").css("left","30px").run

The Jquery method extends a webControl and returns a JqueryScript object. This object has all the methods of Jquery that essentially return another JqueryScript that keeps adding to the script. The final ā€œrunā€ command pass the constructed JqueryScript as an executeJavascript to the clientā€™s browser.

For Mongo,
You would first create a generic MongoCommand object.
This object would need to know about its collection (which can traverse to database ā€¦ client), its running command string thus far, its methodName as string, and a paramArray of parameters. You then create helper methods that create these MongoCommands for each mongo function:

For example a collection would have the find() method, but it would return a MongoCommand instead.

MongoDriver.MongoCollection.find(query as string, projection as string) as MongoCommand return new MongoCommand (mDatabase, mDatabase.getName() + "." + mName, "find", query, projection) //Constructor: database, running script thus far, method to append to the script, method params() End

You could then string them together:

dim cursor as MongoDriver.MongoCursor = myCollection.find(query, projection).sort( "{ age: -1 }").getCursor

The find() operator could know to return a subClass of MongoCommand that adds the ā€œsortā€ and other methods.

You could even do things like: collection.find().sort().explain()
There wouldnā€™t really be a limit and if a MongoCommand was missing it would be easy for someone to add their own extension method for it. Another option would be to create a mongoParameter as its own type with operator_convert both for string and JsonItem. This way you could do things like:
myCollection.find(myQueryString, myJsonProjection).sort("{ age: -1 } ").toCursor

Obviously this would take some reworking of current framework but it might be worth it.
Honestly Iā€™m mostly looking for support of the rest of the collection methods.
https://docs.mongodb.org/manual/reference/method/js-collection/
Both mapReduce and aggregate are pretty vital components to Mongo.

The chaining of commands is an awesome idea, and we can definitely look at updating the driver to support it, as per your suggested way of doing so.

Before rushing into all the new changes and improvements, and to make things easier, I think I first just need to re-save the driver project in the text project format, rather than continuing work in the binary format. This will make it much easier for us to track changes on GitHub, and work on multiple parts of the project at the same time.