1 // query.js
  2 
  3 if ( typeof DBQuery == "undefined" ){
  4     DBQuery = function( mongo , db , collection , ns , query , fields , limit , skip , batchSize ){
  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 
 17         this._cursor = null;
 18         this._numReturned = 0;
 19         this._special = false;
 20     }
 21     print( "DBQuery probably won't have array access " );
 22 }
 23 
 24 DBQuery.prototype.help = function(){
 25     print( "DBQuery help" );
 26     print( "\t.sort( {...} )" )
 27     print( "\t.limit( n )" )
 28     print( "\t.skip( n )" )
 29     print( "\t.count() - total # of objects matching query, ignores skip,limit" )
 30     print( "\t.size() - total # of objects cursor would return skip,limit effect this" )
 31     print( "\t.explain()" )
 32     print( "\t.forEach( func )" )
 33     print( "\t.map( func )" )
 34     
 35 }
 36 
 37 DBQuery.prototype.clone = function(){
 38     var q =  new DBQuery( this._mongo , this._db , this._collection , this._ns , 
 39         this._query , this._fields , 
 40         this._limit , this._skip , this._batchSize );
 41     q._special = this._special;
 42     return q;
 43 }
 44 
 45 DBQuery.prototype._ensureSpecial = function(){
 46     if ( this._special )
 47         return;
 48     
 49     var n = { query : this._query };
 50     this._query = n;
 51     this._special = true;
 52 }
 53 
 54 DBQuery.prototype._checkModify = function(){
 55     if ( this._cursor )
 56         throw "query already executed";
 57 }
 58 
 59 DBQuery.prototype._exec = function(){
 60     if ( ! this._cursor ){
 61         assert.eq( 0 , this._numReturned );
 62         this._cursor = this._mongo.find( this._ns , this._query , this._fields , this._limit , this._skip , this._batchSize );
 63         this._cursorSeen = 0;
 64     }
 65     return this._cursor;
 66 }
 67 
 68 DBQuery.prototype.limit = function( limit ){
 69     this._checkModify();
 70     this._limit = limit;
 71     return this;
 72 }
 73 
 74 DBQuery.prototype.batchSize = function( batchSize ){
 75     this._checkModify();
 76     this._batchSize = batchSize;
 77     return this;
 78 }
 79 
 80 
 81 DBQuery.prototype.skip = function( skip ){
 82     this._checkModify();
 83     this._skip = skip;
 84     return this;
 85 }
 86 
 87 DBQuery.prototype.hasNext = function(){
 88     this._exec();
 89 
 90     if ( this._limit > 0 && this._cursorSeen >= this._limit )
 91         return false;
 92     var o = this._cursor.hasNext();
 93     return o;
 94 }
 95 
 96 DBQuery.prototype.next = function(){
 97     this._exec();
 98     
 99     var o = this._cursor.hasNext();
100     if ( o )
101         this._cursorSeen++;
102     else
103         throw "error hasNext: " + o;
104     
105     var ret = this._cursor.next();
106     if ( ret.$err && this._numReturned == 0 && ! this.hasNext() )
107         throw "error: " + tojson( ret );
108 
109     this._numReturned++;
110     return ret;
111 }
112 
113 DBQuery.prototype.toArray = function(){
114     if ( this._arr )
115         return this._arr;
116     
117     var a = [];
118     while ( this.hasNext() )
119         a.push( this.next() );
120     this._arr = a;
121     return a;
122 }
123 
124 DBQuery.prototype.count = function( applySkipLimit ){
125     var cmd = { count: this._collection.getName() };
126     if ( this._query ){
127         if ( this._special )
128             cmd.query = this._query.query;
129         else 
130             cmd.query = this._query;
131     }
132     cmd.fields = this._fields || {};
133 
134     if ( applySkipLimit ){
135         if ( this._limit )
136             cmd.limit = this._limit;
137         if ( this._skip )
138             cmd.skip = this._skip;
139     }
140     
141     var res = this._db.runCommand( cmd );
142     if( res && res.n != null ) return res.n;
143     throw "count failed: " + tojson( res );
144 }
145 
146 DBQuery.prototype.size = function(){
147     return this.count( true );
148 }
149 
150 DBQuery.prototype.countReturn = function(){
151     var c = this.count();
152 
153     if ( this._skip )
154         c = c - this._skip;
155 
156     if ( this._limit > 0 && this._limit < c )
157         return this._limit;
158     
159     return c;
160 }
161 
162 /**
163 * iterative count - only for testing
164 */
165 DBQuery.prototype.itcount = function(){
166     var num = 0;
167     while ( this.hasNext() ){
168         num++;
169         this.next();
170     }
171     return num;
172 }
173 
174 DBQuery.prototype.length = function(){
175     return this.toArray().length;
176 }
177 
178 DBQuery.prototype._addSpecial = function( name , value ){
179     this._ensureSpecial();
180     this._query[name] = value;
181     return this;
182 }
183 
184 DBQuery.prototype.sort = function( sortBy ){
185     return this._addSpecial( "orderby" , sortBy );
186 }
187 
188 DBQuery.prototype.hint = function( hint ){
189     return this._addSpecial( "$hint" , hint );
190 }
191 
192 DBQuery.prototype.min = function( min ) {
193     return this._addSpecial( "$min" , min );
194 }
195 
196 DBQuery.prototype.max = function( max ) {
197     return this._addSpecial( "$max" , max );
198 }
199 
200 DBQuery.prototype.forEach = function( func ){
201     while ( this.hasNext() )
202         func( this.next() );
203 }
204 
205 DBQuery.prototype.map = function( func ){
206     var a = [];
207     while ( this.hasNext() )
208         a.push( func( this.next() ) );
209     return a;
210 }
211 
212 DBQuery.prototype.arrayAccess = function( idx ){
213     return this.toArray()[idx];
214 }
215 
216 DBQuery.prototype.explain = function(){
217     var n = this.clone();
218     n._ensureSpecial();
219     n._query.$explain = true;
220     n._limit = n._limit * -1;
221     return n.next();
222 }
223 
224 DBQuery.prototype.snapshot = function(){
225     this._ensureSpecial();
226     this._query.$snapshot = true;
227     return this;
228 }
229 
230 DBQuery.prototype.shellPrint = function(){
231     try {
232         var n = 0;
233         while ( this.hasNext() && n < DBQuery.shellBatchSize ){
234             var s = tojson( this.next() , "" , true );
235             print( s );
236             n++;
237         }
238         if ( this.hasNext() ){
239             print( "has more" );
240             ___it___  = this;
241         }
242         else {
243             ___it___  = null;
244         }
245     }
246     catch ( e ){
247         print( e );
248     }
249     
250 }
251 
252 DBQuery.prototype.toString = function(){
253     return "DBQuery: " + this._ns + " -> " + tojson( this.query );
254 }
255 
256 DBQuery.shellBatchSize = 20;
257