1 // query.js 2 3 if ( typeof DBQuery == "undefined" ){ 4 DBQuery = function( mongo , db , collection , ns , query , fields , limit , skip , batchSize , options ){ 5 6 this._mongo = mongo; // 0 7 this._db = db; // 1 8 this._collection = collection; // 2 9 this._ns = ns; // 3 10 11 this._query = query || {}; // 4 12 this._fields = fields; // 5 13 this._limit = limit || 0; // 6 14 this._skip = skip || 0; // 7 15 this._batchSize = batchSize || 0; 16 this._options = options || 0; 17 18 this._cursor = null; 19 this._numReturned = 0; 20 this._special = false; 21 this._prettyShell = false; 22 } 23 print( "DBQuery probably won't have array access " ); 24 } 25 26 DBQuery.prototype.help = function () { 27 print("find() modifiers") 28 print("\t.sort( {...} )") 29 print("\t.limit( n )") 30 print("\t.skip( n )") 31 print("\t.count() - total # of objects matching query, ignores skip,limit") 32 print("\t.size() - total # of objects cursor would return, honors skip,limit") 33 print("\t.explain([verbose])") 34 print("\t.hint(...)") 35 print("\t.showDiskLoc() - adds a $diskLoc field to each returned object") 36 print("\nCursor methods"); 37 print("\t.forEach( func )") 38 print("\t.print() - output to console in full pretty format") 39 print("\t.map( func )") 40 print("\t.hasNext()") 41 print("\t.next()") 42 } 43 44 DBQuery.prototype.clone = function(){ 45 var q = new DBQuery( this._mongo , this._db , this._collection , this._ns , 46 this._query , this._fields , 47 this._limit , this._skip , this._batchSize , this._options ); 48 q._special = this._special; 49 return q; 50 } 51 52 DBQuery.prototype._ensureSpecial = function(){ 53 if ( this._special ) 54 return; 55 56 var n = { query : this._query }; 57 this._query = n; 58 this._special = true; 59 } 60 61 DBQuery.prototype._checkModify = function(){ 62 if ( this._cursor ) 63 throw "query already executed"; 64 } 65 66 DBQuery.prototype._exec = function(){ 67 if ( ! this._cursor ){ 68 assert.eq( 0 , this._numReturned ); 69 this._cursor = this._mongo.find( this._ns , this._query , this._fields , this._limit , this._skip , this._batchSize , this._options ); 70 this._cursorSeen = 0; 71 } 72 return this._cursor; 73 } 74 75 DBQuery.prototype.limit = function( limit ){ 76 this._checkModify(); 77 this._limit = limit; 78 return this; 79 } 80 81 DBQuery.prototype.batchSize = function( batchSize ){ 82 this._checkModify(); 83 this._batchSize = batchSize; 84 return this; 85 } 86 87 88 DBQuery.prototype.addOption = function( option ){ 89 this._options |= option; 90 return this; 91 } 92 93 DBQuery.prototype.skip = function( skip ){ 94 this._checkModify(); 95 this._skip = skip; 96 return this; 97 } 98 99 DBQuery.prototype.hasNext = function(){ 100 this._exec(); 101 102 if ( this._limit > 0 && this._cursorSeen >= this._limit ) 103 return false; 104 var o = this._cursor.hasNext(); 105 return o; 106 } 107 108 DBQuery.prototype.next = function(){ 109 this._exec(); 110 111 var o = this._cursor.hasNext(); 112 if ( o ) 113 this._cursorSeen++; 114 else 115 throw "error hasNext: " + o; 116 117 var ret = this._cursor.next(); 118 if ( ret.$err && this._numReturned == 0 && ! this.hasNext() ) 119 throw "error: " + tojson( ret ); 120 121 this._numReturned++; 122 return ret; 123 } 124 125 DBQuery.prototype.objsLeftInBatch = function(){ 126 this._exec(); 127 128 var ret = this._cursor.objsLeftInBatch(); 129 if ( ret.$err ) 130 throw "error: " + tojson( ret ); 131 132 return ret; 133 } 134 135 DBQuery.prototype.toArray = function(){ 136 if ( this._arr ) 137 return this._arr; 138 139 var a = []; 140 while ( this.hasNext() ) 141 a.push( this.next() ); 142 this._arr = a; 143 return a; 144 } 145 146 DBQuery.prototype.count = function( applySkipLimit ){ 147 var cmd = { count: this._collection.getName() }; 148 if ( this._query ){ 149 if ( this._special ) 150 cmd.query = this._query.query; 151 else 152 cmd.query = this._query; 153 } 154 cmd.fields = this._fields || {}; 155 156 if ( applySkipLimit ){ 157 if ( this._limit ) 158 cmd.limit = this._limit; 159 if ( this._skip ) 160 cmd.skip = this._skip; 161 } 162 163 var res = this._db.runCommand( cmd ); 164 if( res && res.n != null ) return res.n; 165 throw "count failed: " + tojson( res ); 166 } 167 168 DBQuery.prototype.size = function(){ 169 return this.count( true ); 170 } 171 172 DBQuery.prototype.countReturn = function(){ 173 var c = this.count(); 174 175 if ( this._skip ) 176 c = c - this._skip; 177 178 if ( this._limit > 0 && this._limit < c ) 179 return this._limit; 180 181 return c; 182 } 183 184 /** 185 * iterative count - only for testing 186 */ 187 DBQuery.prototype.itcount = function(){ 188 var num = 0; 189 while ( this.hasNext() ){ 190 num++; 191 this.next(); 192 } 193 return num; 194 } 195 196 DBQuery.prototype.length = function(){ 197 return this.toArray().length; 198 } 199 200 DBQuery.prototype._addSpecial = function( name , value ){ 201 this._ensureSpecial(); 202 this._query[name] = value; 203 return this; 204 } 205 206 DBQuery.prototype.sort = function( sortBy ){ 207 return this._addSpecial( "orderby" , sortBy ); 208 } 209 210 DBQuery.prototype.hint = function( hint ){ 211 return this._addSpecial( "$hint" , hint ); 212 } 213 214 DBQuery.prototype.min = function( min ) { 215 return this._addSpecial( "$min" , min ); 216 } 217 218 DBQuery.prototype.max = function( max ) { 219 return this._addSpecial( "$max" , max ); 220 } 221 222 DBQuery.prototype.showDiskLoc = function() { 223 return this._addSpecial( "$showDiskLoc" , true); 224 } 225 226 DBQuery.prototype.forEach = function( func ){ 227 while ( this.hasNext() ) 228 func( this.next() ); 229 } 230 231 DBQuery.prototype.map = function( func ){ 232 var a = []; 233 while ( this.hasNext() ) 234 a.push( func( this.next() ) ); 235 return a; 236 } 237 238 DBQuery.prototype.arrayAccess = function( idx ){ 239 return this.toArray()[idx]; 240 } 241 242 DBQuery.prototype.explain = function (verbose) { 243 /* verbose=true --> include allPlans, oldPlan fields */ 244 var n = this.clone(); 245 n._ensureSpecial(); 246 n._query.$explain = true; 247 n._limit = Math.abs(n._limit) * -1; 248 var e = n.next(); 249 250 function cleanup(obj){ 251 if (typeof(obj) != 'object'){ 252 return; 253 } 254 255 delete obj.allPlans; 256 delete obj.oldPlan; 257 258 if (typeof(obj.length) == 'number'){ 259 for (var i=0; i < obj.length; i++){ 260 cleanup(obj[i]); 261 } 262 } 263 264 if (obj.shards){ 265 for (var key in obj.shards){ 266 cleanup(obj.shards[key]); 267 } 268 } 269 270 if (obj.clauses){ 271 cleanup(obj.clauses); 272 } 273 } 274 275 if (!verbose) 276 cleanup(e); 277 278 return e; 279 } 280 281 DBQuery.prototype.snapshot = function(){ 282 this._ensureSpecial(); 283 this._query.$snapshot = true; 284 return this; 285 } 286 287 DBQuery.prototype.pretty = function(){ 288 this._prettyShell = true; 289 return this; 290 } 291 292 DBQuery.prototype.shellPrint = function(){ 293 try { 294 var n = 0; 295 while ( this.hasNext() && n < DBQuery.shellBatchSize ){ 296 var s = this._prettyShell ? tojson( this.next() ) : tojson( this.next() , "" , true ); 297 print( s ); 298 n++; 299 } 300 if ( this.hasNext() ){ 301 print( "has more" ); 302 ___it___ = this; 303 } 304 else { 305 ___it___ = null; 306 } 307 } 308 catch ( e ){ 309 print( e ); 310 } 311 312 } 313 314 DBQuery.prototype.toString = function(){ 315 return "DBQuery: " + this._ns + " -> " + tojson( this.query ); 316 } 317 318 DBQuery.shellBatchSize = 20; 319