PageRenderTime 51ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/src/mongo/shell/query.js

https://github.com/javacruft/mongo
JavaScript | 505 lines | 381 code | 79 blank | 45 comment | 59 complexity | d59e79cb35feac9c50e64f22caa711ea MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause
  1. // query.js
  2. if ( typeof DBQuery == "undefined" ){
  3. DBQuery = function( mongo , db , collection , ns , query , fields , limit , skip , batchSize , options ){
  4. this._mongo = mongo; // 0
  5. this._db = db; // 1
  6. this._collection = collection; // 2
  7. this._ns = ns; // 3
  8. this._query = query || {}; // 4
  9. this._fields = fields; // 5
  10. this._limit = limit || 0; // 6
  11. this._skip = skip || 0; // 7
  12. this._batchSize = batchSize || 0;
  13. this._options = options || 0;
  14. this._cursor = null;
  15. this._numReturned = 0;
  16. this._special = false;
  17. this._prettyShell = false;
  18. }
  19. print( "DBQuery probably won't have array access " );
  20. }
  21. DBQuery.prototype.help = function () {
  22. print("find() modifiers")
  23. print("\t.sort( {...} )")
  24. print("\t.limit( n )")
  25. print("\t.skip( n )")
  26. print("\t.count(applySkipLimit) - total # of objects matching query. by default ignores skip,limit")
  27. print("\t.size() - total # of objects cursor would return, honors skip,limit")
  28. print("\t.explain([verbose])")
  29. print("\t.hint(...)")
  30. print("\t.addOption(n) - adds op_query options -- see wire protocol")
  31. print("\t._addSpecial(name, value) - http://dochub.mongodb.org/core/advancedqueries#AdvancedQueries-Metaqueryoperators")
  32. print("\t.batchSize(n) - sets the number of docs to return per getMore")
  33. print("\t.showDiskLoc() - adds a $diskLoc field to each returned object")
  34. print("\t.min(idxDoc)")
  35. print("\t.max(idxDoc)")
  36. print("\t.comment(comment)")
  37. print("\t.snapshot()")
  38. print("\t.readPref(mode, tagset)")
  39. print("\nCursor methods");
  40. print("\t.toArray() - iterates through docs and returns an array of the results")
  41. print("\t.forEach( func )")
  42. print("\t.map( func )")
  43. print("\t.hasNext()")
  44. print("\t.next()")
  45. print("\t.objsLeftInBatch() - returns count of docs left in current batch (when exhausted, a new getMore will be issued)")
  46. print("\t.itcount() - iterates through documents and counts them")
  47. print("\t.getQueryPlan() - get query plans associated with shape. To get more info on query plans, " +
  48. "call getQueryPlan().help().");
  49. print("\t.pretty() - pretty print each document, possibly over multiple lines")
  50. }
  51. DBQuery.prototype.clone = function(){
  52. var q = new DBQuery( this._mongo , this._db , this._collection , this._ns ,
  53. this._query , this._fields ,
  54. this._limit , this._skip , this._batchSize , this._options );
  55. q._special = this._special;
  56. return q;
  57. }
  58. DBQuery.prototype._ensureSpecial = function(){
  59. if ( this._special )
  60. return;
  61. var n = { query : this._query };
  62. this._query = n;
  63. this._special = true;
  64. }
  65. DBQuery.prototype._checkModify = function(){
  66. if ( this._cursor )
  67. throw Error("query already executed");
  68. }
  69. DBQuery.prototype._exec = function(){
  70. if ( ! this._cursor ){
  71. assert.eq( 0 , this._numReturned );
  72. this._cursor = this._mongo.find( this._ns , this._query , this._fields , this._limit , this._skip , this._batchSize , this._options );
  73. this._cursorSeen = 0;
  74. }
  75. return this._cursor;
  76. }
  77. DBQuery.prototype.limit = function( limit ){
  78. this._checkModify();
  79. this._limit = limit;
  80. return this;
  81. }
  82. DBQuery.prototype.batchSize = function( batchSize ){
  83. this._checkModify();
  84. this._batchSize = batchSize;
  85. return this;
  86. }
  87. DBQuery.prototype.addOption = function( option ){
  88. this._options |= option;
  89. return this;
  90. }
  91. DBQuery.prototype.skip = function( skip ){
  92. this._checkModify();
  93. this._skip = skip;
  94. return this;
  95. }
  96. DBQuery.prototype.hasNext = function(){
  97. this._exec();
  98. if ( this._limit > 0 && this._cursorSeen >= this._limit )
  99. return false;
  100. var o = this._cursor.hasNext();
  101. return o;
  102. }
  103. DBQuery.prototype.next = function(){
  104. this._exec();
  105. var o = this._cursor.hasNext();
  106. if ( o )
  107. this._cursorSeen++;
  108. else
  109. throw Error( "error hasNext: " + o );
  110. var ret = this._cursor.next();
  111. if ( ret.$err )
  112. throw Error( "error: " + tojson( ret ) );
  113. this._numReturned++;
  114. return ret;
  115. }
  116. DBQuery.prototype.objsLeftInBatch = function(){
  117. this._exec();
  118. var ret = this._cursor.objsLeftInBatch();
  119. if ( ret.$err )
  120. throw Error( "error: " + tojson( ret ) );
  121. return ret;
  122. }
  123. DBQuery.prototype.readOnly = function(){
  124. this._exec();
  125. this._cursor.readOnly();
  126. return this;
  127. }
  128. DBQuery.prototype.toArray = function(){
  129. if ( this._arr )
  130. return this._arr;
  131. var a = [];
  132. while ( this.hasNext() )
  133. a.push( this.next() );
  134. this._arr = a;
  135. return a;
  136. }
  137. DBQuery.prototype.count = function( applySkipLimit ) {
  138. var cmd = { count: this._collection.getName() };
  139. if ( this._query ) {
  140. if ( this._special ) {
  141. cmd.query = this._query.query;
  142. if ( this._query.$maxTimeMS ) {
  143. cmd.maxTimeMS = this._query.$maxTimeMS;
  144. }
  145. if ( this._query.$hint ) {
  146. cmd.hint = this._query.$hint;
  147. }
  148. }
  149. else {
  150. cmd.query = this._query;
  151. }
  152. }
  153. cmd.fields = this._fields || {};
  154. if ( applySkipLimit ) {
  155. if ( this._limit )
  156. cmd.limit = this._limit;
  157. if ( this._skip )
  158. cmd.skip = this._skip;
  159. }
  160. var res = this._db.runCommand( cmd );
  161. if( res && res.n != null ) return res.n;
  162. throw Error( "count failed: " + tojson( res ) );
  163. }
  164. DBQuery.prototype.size = function(){
  165. return this.count( true );
  166. }
  167. DBQuery.prototype.countReturn = function(){
  168. var c = this.count();
  169. if ( this._skip )
  170. c = c - this._skip;
  171. if ( this._limit > 0 && this._limit < c )
  172. return this._limit;
  173. return c;
  174. }
  175. /**
  176. * iterative count - only for testing
  177. */
  178. DBQuery.prototype.itcount = function(){
  179. var num = 0;
  180. while ( this.hasNext() ){
  181. num++;
  182. this.next();
  183. }
  184. return num;
  185. }
  186. DBQuery.prototype.length = function(){
  187. return this.toArray().length;
  188. }
  189. DBQuery.prototype._addSpecial = function( name , value ){
  190. this._ensureSpecial();
  191. this._query[name] = value;
  192. return this;
  193. }
  194. DBQuery.prototype.sort = function( sortBy ){
  195. return this._addSpecial( "orderby" , sortBy );
  196. }
  197. DBQuery.prototype.hint = function( hint ){
  198. return this._addSpecial( "$hint" , hint );
  199. }
  200. DBQuery.prototype.min = function( min ) {
  201. return this._addSpecial( "$min" , min );
  202. }
  203. DBQuery.prototype.max = function( max ) {
  204. return this._addSpecial( "$max" , max );
  205. }
  206. DBQuery.prototype.showDiskLoc = function() {
  207. return this._addSpecial( "$showDiskLoc" , true );
  208. }
  209. DBQuery.prototype.maxTimeMS = function( maxTimeMS ) {
  210. return this._addSpecial( "$maxTimeMS" , maxTimeMS );
  211. }
  212. /**
  213. * Sets the read preference for this cursor.
  214. *
  215. * @param mode {string} read preference mode to use.
  216. * @param tagSet {Array.<Object>} optional. The list of tags to use, order matters.
  217. * Note that this object only keeps a shallow copy of this array.
  218. *
  219. * @return this cursor
  220. */
  221. DBQuery.prototype.readPref = function( mode, tagSet ) {
  222. var readPrefObj = {
  223. mode: mode
  224. };
  225. if ( tagSet ){
  226. readPrefObj.tags = tagSet;
  227. }
  228. return this._addSpecial( "$readPreference", readPrefObj );
  229. };
  230. DBQuery.prototype.forEach = function( func ){
  231. while ( this.hasNext() )
  232. func( this.next() );
  233. }
  234. DBQuery.prototype.map = function( func ){
  235. var a = [];
  236. while ( this.hasNext() )
  237. a.push( func( this.next() ) );
  238. return a;
  239. }
  240. DBQuery.prototype.arrayAccess = function( idx ){
  241. return this.toArray()[idx];
  242. }
  243. DBQuery.prototype.comment = function (comment) {
  244. return this._addSpecial( "$comment" , comment );
  245. }
  246. DBQuery.prototype.explain = function (verbose) {
  247. /* verbose=true --> include allPlans, oldPlan fields */
  248. var n = this.clone();
  249. n._addSpecial( "$explain", true );
  250. n._limit = Math.abs(n._limit) * -1;
  251. var e = n.next();
  252. function cleanup(obj){
  253. if (typeof(obj) != 'object'){
  254. return;
  255. }
  256. delete obj.allPlans;
  257. delete obj.oldPlan;
  258. delete obj.stats;
  259. if (typeof(obj.length) == 'number'){
  260. for (var i=0; i < obj.length; i++){
  261. cleanup(obj[i]);
  262. }
  263. }
  264. if (obj.shards){
  265. for (var key in obj.shards){
  266. cleanup(obj.shards[key]);
  267. }
  268. }
  269. if (obj.clauses){
  270. cleanup(obj.clauses);
  271. }
  272. }
  273. if (!verbose)
  274. cleanup(e);
  275. return e;
  276. }
  277. DBQuery.prototype.snapshot = function(){
  278. return this._addSpecial( "$snapshot" , true );
  279. }
  280. DBQuery.prototype.pretty = function(){
  281. this._prettyShell = true;
  282. return this;
  283. }
  284. DBQuery.prototype.shellPrint = function(){
  285. try {
  286. var start = new Date().getTime();
  287. var n = 0;
  288. while ( this.hasNext() && n < DBQuery.shellBatchSize ){
  289. var s = this._prettyShell ? tojson( this.next() ) : tojson( this.next() , "" , true );
  290. print( s );
  291. n++;
  292. }
  293. if (typeof _verboseShell !== 'undefined' && _verboseShell) {
  294. var time = new Date().getTime() - start;
  295. print("Fetched " + n + " record(s) in " + time + "ms");
  296. }
  297. if ( this.hasNext() ){
  298. print( "Type \"it\" for more" );
  299. ___it___ = this;
  300. }
  301. else {
  302. ___it___ = null;
  303. }
  304. }
  305. catch ( e ){
  306. print( e );
  307. }
  308. }
  309. /**
  310. * Returns a QueryPlan for the query.
  311. */
  312. DBQuery.prototype.getQueryPlan = function() {
  313. return new QueryPlan( this );
  314. }
  315. DBQuery.prototype.toString = function(){
  316. return "DBQuery: " + this._ns + " -> " + tojson( this._query );
  317. }
  318. DBQuery.shellBatchSize = 20;
  319. /**
  320. * Query option flag bit constants.
  321. * @see http://dochub.mongodb.org/core/mongowireprotocol#MongoWireProtocol-OPQUERY
  322. */
  323. DBQuery.Option = {
  324. tailable: 0x2,
  325. slaveOk: 0x4,
  326. oplogReplay: 0x8,
  327. noTimeout: 0x10,
  328. awaitData: 0x20,
  329. exhaust: 0x40,
  330. partial: 0x80
  331. };
  332. function DBCommandCursor(mongo, cmdResult, batchSize) {
  333. assert.commandWorked(cmdResult)
  334. this._firstBatch = cmdResult.cursor.firstBatch.reverse(); // modifies input to allow popping
  335. this._cursor = mongo.cursorFromId(cmdResult.cursor.ns, cmdResult.cursor.id, batchSize);
  336. }
  337. DBCommandCursor.prototype = {};
  338. DBCommandCursor.prototype.hasNext = function() {
  339. return this._firstBatch.length || this._cursor.hasNext();
  340. }
  341. DBCommandCursor.prototype.next = function() {
  342. if (this._firstBatch.length) {
  343. // $err wouldn't be in _firstBatch since ok was true.
  344. return this._firstBatch.pop();
  345. }
  346. else {
  347. var ret = this._cursor.next();
  348. if ( ret.$err )
  349. throw Error( "error: " + tojson( ret ) );
  350. return ret;
  351. }
  352. }
  353. DBCommandCursor.prototype.objsLeftInBatch = function() {
  354. if (this._firstBatch.length) {
  355. return this._firstBatch.length;
  356. }
  357. else {
  358. return this._cursor.objsLeftInBatch();
  359. }
  360. }
  361. DBCommandCursor.prototype.help = function () {
  362. // This is the same as the "Cursor Methods" section of DBQuery.help().
  363. print("\nCursor methods");
  364. print("\t.toArray() - iterates through docs and returns an array of the results")
  365. print("\t.forEach( func )")
  366. print("\t.map( func )")
  367. print("\t.hasNext()")
  368. print("\t.next()")
  369. print("\t.objsLeftInBatch() - returns count of docs left in current batch (when exhausted, a new getMore will be issued)")
  370. print("\t.itcount() - iterates through documents and counts them")
  371. print("\t.pretty() - pretty print each document, possibly over multiple lines")
  372. }
  373. // Copy these methods from DBQuery
  374. DBCommandCursor.prototype.toArray = DBQuery.prototype.toArray
  375. DBCommandCursor.prototype.forEach = DBQuery.prototype.forEach
  376. DBCommandCursor.prototype.map = DBQuery.prototype.map
  377. DBCommandCursor.prototype.itcount = DBQuery.prototype.itcount
  378. DBCommandCursor.prototype.shellPrint = DBQuery.prototype.shellPrint
  379. DBCommandCursor.prototype.pretty = DBQuery.prototype.pretty
  380. /**
  381. * QueryCache
  382. * Holds a reference to the cursor.
  383. * Proxy for planCache* query shape-specific commands.
  384. */
  385. if ( ( typeof QueryPlan ) == "undefined" ){
  386. QueryPlan = function( cursor ){
  387. this._cursor = cursor;
  388. }
  389. }
  390. /**
  391. * Name of QueryPlan.
  392. * Same as collection.
  393. */
  394. QueryPlan.prototype.getName = function() {
  395. return this._cursor._collection.getName();
  396. }
  397. /**
  398. * tojson prints the name of the collection
  399. */
  400. QueryPlan.prototype.tojson = function(indent, nolint) {
  401. return tojson(this.getPlans());
  402. }
  403. /**
  404. * Displays help for a PlanCache object.
  405. */
  406. QueryPlan.prototype.help = function () {
  407. var shortName = this.getName();
  408. print("QueryPlan help");
  409. print("\t.help() - show QueryPlan help");
  410. print("\t.clearPlans() - drops query shape from plan cache");
  411. print("\t.getPlans() - displays the cached plans for a query shape");
  412. return __magicNoPrint;
  413. }
  414. /**
  415. * List plans for a query shape.
  416. */
  417. QueryPlan.prototype.getPlans = function() {
  418. return this._cursor._collection.getPlanCache().getPlansByQuery(this._cursor);
  419. }
  420. /**
  421. * Drop query shape from the plan cache.
  422. */
  423. QueryPlan.prototype.clearPlans = function() {
  424. this._cursor._collection.getPlanCache().clearPlansByQuery(this._cursor);
  425. return;
  426. }