Index: TODO =================================================================== diff -u -rc9258caf4c18915bdf4d752ad879932c6da7d967 -rfe68b259d9a15328a04a9dc64394dc5ffe5ba5a4 --- TODO (.../TODO) (revision c9258caf4c18915bdf4d752ad879932c6da7d967) +++ TODO (.../TODO) (revision fe68b259d9a15328a04a9dc64394dc5ffe5ba5a4) @@ -2605,8 +2605,17 @@ * handle fetch of embedded objects * added method count for mongo mapped classes * improve documentation + * added handling of bson types for timestamps and dates + * provide setup based on mongo_db, mongo_collection and mongo_ns TODO: + +- the scripted init block is called before init. If the default + setting of the default parameters depend on each other, this has to + be set in the "init". But this won't be available in the init block. + This is not nice in the mongo case, where one has "mongo_ns", + "mongo_db" and "mongo_collection" which are related variables. + - how to delete attributes? - Higher binary compatibility for future versions: Index: library/mongodb/example-nsf-mongo.tcl =================================================================== diff -u -r2837e8ce08344ee3f82a7451109f14a4b7cb3395 -rfe68b259d9a15328a04a9dc64394dc5ffe5ba5a4 --- library/mongodb/example-nsf-mongo.tcl (.../example-nsf-mongo.tcl) (revision 2837e8ce08344ee3f82a7451109f14a4b7cb3395) +++ library/mongodb/example-nsf-mongo.tcl (.../example-nsf-mongo.tcl) (revision fe68b259d9a15328a04a9dc64394dc5ffe5ba5a4) @@ -16,11 +16,13 @@ ::mongo::remove $mongoConn tutorial.persons {} puts stderr "\nInserting a few tuples" - ::mongo::insert $mongoConn tutorial.persons [list name string Joe projects string abc age int 23] + ::mongo::insert $mongoConn tutorial.persons [list name string Joe projects string abc age int 23 \ + classes array {0 object {$ref string courses $id oid 1}}] ::mongo::insert $mongoConn tutorial.persons [list name string Gustaf projects string nsf age int 53] ::mongo::insert $mongoConn tutorial.persons [list name string Stefan projects string nsf] ::mongo::insert $mongoConn tutorial.persons [list name string Franz info object {x int 203 y int 102} age int 29] ::mongo::insert $mongoConn tutorial.persons [list name string Victor a array {0 string "x" 1 string "y"} age int 31] + ::mongo::insert $mongoConn tutorial.persons [list name string Selim ts timestamp {1302945037 1} d date 1302947619279] puts stderr "\nCreate an index on name (ascending)" ::mongo::index $mongoConn tutorial.persons [list name int 1] Index: library/mongodb/example-nx-bi.tcl =================================================================== diff -u -rc9258caf4c18915bdf4d752ad879932c6da7d967 -rfe68b259d9a15328a04a9dc64394dc5ffe5ba5a4 --- library/mongodb/example-nx-bi.tcl (.../example-nx-bi.tcl) (revision c9258caf4c18915bdf4d752ad879932c6da7d967) +++ library/mongodb/example-nx-bi.tcl (.../example-nx-bi.tcl) (revision fe68b259d9a15328a04a9dc64394dc5ffe5ba5a4) @@ -29,11 +29,11 @@ package require nx::test # Establish connection to the database -::nx::mongo::db connect +::nx::mongo::db connect -db "tutorial" # Make sure, we start always from scratch, so remove everything from # the collection. -nx::mongo::db remove tutorial.bi {} +nx::mongo::db remove tutorial.comments {} # # Create the application classes based on the "Business Insider" data @@ -44,15 +44,14 @@ # "... delete ..." to add values to the attributes). # nx::mongo::Class create Comment { - # This class does not have a :document" spec, since it is embedded. + # This class does not have a :mongo_ns" spec, since it is embedded. :attribute author:required :attribute comment:required :attribute replies:embedded,arg=::Comment,0..n {set :incremental 1} } nx::mongo::Class create Posting { - :document "tutorial.bi" :index tags :attribute title:required :attribute author:required @@ -95,7 +94,7 @@ $c delete # The delete operation destroy the embedded object and removes the -# referece to it in the comments attribute. +# reference to it in the comments attribute. ? [list llength [$p comments]] 1 # The delete operation does not automatically persist the change, Index: library/mongodb/example-nx-mongo.tcl =================================================================== diff -u -rec9e525c887a0ae430bdb35bef01f499b25d617f -rfe68b259d9a15328a04a9dc64394dc5ffe5ba5a4 --- library/mongodb/example-nx-mongo.tcl (.../example-nx-mongo.tcl) (revision ec9e525c887a0ae430bdb35bef01f499b25d617f) +++ library/mongodb/example-nx-mongo.tcl (.../example-nx-mongo.tcl) (revision fe68b259d9a15328a04a9dc64394dc5ffe5ba5a4) @@ -6,7 +6,7 @@ package require nx::mongo # Establish connection to the database -::nx::mongo::db connect +::nx::mongo::db connect -db "tutorial" # Make sure, we start always from scratch nx::mongo::db remove tutorial.persons {} @@ -15,7 +15,6 @@ # Create the application class "Person" # nx::mongo::Class create Person { - :document "tutorial.persons" :index name :attribute name:required @@ -94,3 +93,17 @@ puts "\t$p:\t[$p name]" } + +nx::mongo::Class create Users { + :attribute name:required +} + +nx::mongo::Class create Post { + :attribute title:required + :attribute user_id +} + +set u [Users new -name Smith] +$u save +set p [Post new -title "Hello World" -user_id [$u _id]] +$p save \ No newline at end of file Index: library/mongodb/nsfmongo.c =================================================================== diff -u -r2837e8ce08344ee3f82a7451109f14a4b7cb3395 -rfe68b259d9a15328a04a9dc64394dc5ffe5ba5a4 --- library/mongodb/nsfmongo.c (.../nsfmongo.c) (revision 2837e8ce08344ee3f82a7451109f14a4b7cb3395) +++ library/mongodb/nsfmongo.c (.../nsfmongo.c) (revision fe68b259d9a15328a04a9dc64394dc5ffe5ba5a4) @@ -160,7 +160,6 @@ BsonToList(Tcl_Interp *interp, const char *data , int depth) { bson_iterator i; const char *key, *tag; - bson_timestamp_t ts; char oidhex[25]; Tcl_Obj *resultObj, *elemObj; @@ -177,7 +176,8 @@ switch ( t ){ case bson_int: tag = "integer"; elemObj = Tcl_NewIntObj(bson_iterator_int( &i )); break; - case bson_long: tag = "long"; elemObj = Tcl_NewIntObj(bson_iterator_long( &i )); break; + case bson_long: tag = "long"; elemObj = Tcl_NewLongObj(bson_iterator_long( &i )); break; + case bson_date: tag = "date"; elemObj = Tcl_NewLongObj(bson_iterator_date( &i )); break; case bson_double: tag = "double"; elemObj = Tcl_NewDoubleObj(bson_iterator_double( &i )); break; case bson_bool: tag = "boolean"; elemObj = Tcl_NewBooleanObj(bson_iterator_bool( &i )); break; case bson_regex: tag = "regex"; elemObj = Tcl_NewStringObj(bson_iterator_regex( &i ), -1); break; @@ -189,13 +189,15 @@ elemObj = Tcl_NewStringObj(oidhex, -1); break; } - case bson_timestamp: + case bson_timestamp: { + bson_timestamp_t ts; tag = "timestamp"; ts = bson_iterator_timestamp( &i ); elemObj = Tcl_NewListObj(0, NULL); - Tcl_AppendObjToObj(elemObj, Tcl_NewIntObj(ts.i)); - Tcl_AppendObjToObj(elemObj, Tcl_NewIntObj(ts.t)); + Tcl_ListObjAppendElement(interp, elemObj, Tcl_NewIntObj(ts.t)); + Tcl_ListObjAppendElement(interp, elemObj, Tcl_NewIntObj(ts.i)); break; + } case bson_object: case bson_array: tag = t == bson_object ? "object" : "array"; @@ -240,7 +242,9 @@ switch (firstChar) { case 'a': /* array */ return bson_array; case 'b': /* bool */ return bson_bool; - case 'd': /* double */ return bson_double; + case 'd': + if (*(tag + 1) == 'a') /* date */ return bson_date; + if (*(tag + 1) == 'o') /* double */ return bson_double; case 'i': /* integer */ return bson_int; case 'l': /* long */ return bson_long; case 'n': /* null */ return bson_null; @@ -326,6 +330,14 @@ bson_append_regex(bbPtr, name, ObjStr(value), opts); break; } + case bson_date: { + long v; + result = Tcl_GetLongFromObj(interp, value, &v); + fprintf(stderr, "bson date v %ld result %d\n", v, result); + if (result != TCL_OK) break; + bson_append_date(bbPtr, name, v); + break; + } case bson_timestamp: { bson_timestamp_t v; Tcl_Obj **objv; @@ -334,9 +346,9 @@ if (result != TCL_OK || objc != 2) { return NsfPrintError(interp, "invalid timestamp: %s", ObjStr(value)); } - result = Tcl_GetIntFromObj(interp, objv[0], &v.i); + result = Tcl_GetIntFromObj(interp, objv[0], &v.t); if (result == TCL_OK) { - result = Tcl_GetIntFromObj(interp, objv[1], &v.t); + result = Tcl_GetIntFromObj(interp, objv[1], &v.i); } if (result != TCL_OK) break; bson_append_timestamp(bbPtr, name, &v); Index: library/mongodb/nx-mongo.tcl =================================================================== diff -u -rc9258caf4c18915bdf4d752ad879932c6da7d967 -rfe68b259d9a15328a04a9dc64394dc5ffe5ba5a4 --- library/mongodb/nx-mongo.tcl (.../nx-mongo.tcl) (revision c9258caf4c18915bdf4d752ad879932c6da7d967) +++ library/mongodb/nx-mongo.tcl (.../nx-mongo.tcl) (revision fe68b259d9a15328a04a9dc64394dc5ffe5ba5a4) @@ -9,15 +9,18 @@ # todo: how to handle multiple connections; currently we have a single, global connection # todo: make embedded spec nicer -# todo: handle time stamps # todo: handle remove for non-multivalued embedded objects -# todo: handle names of nx objects (e.g. attribute like __name) -# todo: handle classes von nx objects (e.g. attribute like __class) +# idea: handle names of nx objects (e.g. attribute like __name) +# idea: handle classes von nx objects (e.g. attribute like __class) namespace eval ::nx::mongo { ::nx::Object create ::nx::mongo::db { - :public method connect {args} {set :mongoConn [::mongo::connect {*}$args]} + :attribute db + :public method connect {{-db test} args} { + set :db $db + set :mongoConn [::mongo::connect {*}$args] + } :public method count {args} {::mongo::count ${:mongoConn} {*}$args} :public method index {args} {::mongo::index ${:mongoConn} {*}$args} :public method insert {args} {::mongo::insert ${:mongoConn} {*}$args} @@ -132,10 +135,12 @@ ::nx::Class create ::nx::mongo::Class -superclass nx::Class { # - # Every mongo class can be configured with a document, from which + # Every mongo class can be configured with a mongo_ns, from which # its instance data is queried. # - :attribute document + :attribute mongo_ns + :attribute mongo_db + :attribute mongo_collection # # Provide helper methods to access from an external specifier @@ -221,9 +226,10 @@ # index method # :public method index {att {-type 1}} { + if {![info exists :mongo_ns]} {:mongo_setup} # todo: 2d index will need a different type - #::mongo::index $::mongoConn ${:document} [list $att int $type] - db index ${:document} [list $att int $type] + #::mongo::index $::mongoConn ${:mongo_ns} [list $att int $type] + db index ${:mongo_ns} [list $att int $type] } # @@ -240,7 +246,7 @@ # number of tuples for the query. # :public method count {{-cond ""}} { - return [::nx::mongo::db count ${:document} $cond] + return [::nx::mongo::db count ${:mongo_ns} $cond] } # @@ -252,7 +258,7 @@ {-cond ""} {-orderby ""} } { - set tuple [lindex [::nx::mongo::db query ${:document} \ + set tuple [lindex [::nx::mongo::db query ${:mongo_ns} \ [:bson query -cond $cond -orderby $orderby] \ -limit 1] 0] #puts "find first fetched: $tuple" @@ -270,7 +276,7 @@ set opts [list] if {[info exists limit]} {lappend opts -limit $limit} if {[info exists skip]} {lappend opts -skip $skip} - set fetched [::nx::mongo::db query ${:document} \ + set fetched [::nx::mongo::db query ${:mongo_ns} \ [:bson query -cond $cond -orderby $orderby] \ {*}$opts] puts "[join $fetched \n]" @@ -280,13 +286,35 @@ return $result } + :method mongo_setup {} { + # + # setup mongo_collection, mongo_db and mongo_ns + # + if {[info exists :mongo_ns]} { + puts stderr "mongo_ns is set to ${:mongo_ns}" + if {![regexp {^([^.]+)[.](.*)$} ${:mongo_ns} :mongo_db :mongo_collection]} { + error "${:mongo_ns} does not contain a dot." + } + } else { + if {![info exists :mongo_collection]} { + set :mongo_collection [string tolower [namespace tail [self]]]s + } + if {![info exists :mongo_db]} { + set :mongo_db [::nx::mongo::db db] + } + set :mongo_ns ${:mongo_db}.${:mongo_collection} + puts stderr "mongo_ns is set to ${:mongo_ns}" + } + } + # # When a mongo::Class is created, mixin the mongo::Object to make # "save" etc. available # :method init {} { :mixin add ::nx::mongo::Object + :mongo_setup } # :public method create args { @@ -396,8 +424,8 @@ } else { puts "delete a non-embedded entry" if {[info exists :_id]} { - set document [[:info class] document] - ::nx::mongo::db remove $document [list _id oid ${:_id}] + set mongo_ns [[:info class] mongo_ns] + ::nx::mongo::db remove $mongo_ns [list _id oid ${:_id}] } else { error "[self]: object does not contain an _id; it can't be delete from the mongo db." } @@ -409,19 +437,19 @@ # otherwise perform an insert # :public method save {} { - set document [[:info class] document] - if {$document eq ""} { + set mongo_ns [[:info class] mongo_ns] + if {$mongo_ns eq ""} { # We could perform the delegation probably automatically, but # for now we provide an error - error "No document specified for [:info class]. In case this is an embedded object, save the embedding one." + error "No mongo_ns specified for [:info class]. In case this is an embedded object, save the embedding one." } else { set bson [:bson encode] if {[info exists :_id]} { puts stderr "we have to update [:bson pp -indent 4 $bson]" - ::nx::mongo::db update $document [list _id oid ${:_id}] $bson + ::nx::mongo::db update $mongo_ns [list _id oid ${:_id}] $bson } else { puts stderr "we have to insert [:bson pp -indent 4 $bson]" - set r [::nx::mongo::db insert $document $bson] + set r [::nx::mongo::db insert $mongo_ns $bson] set :_id [lindex $r 2] } }