diff --git a/db/clientcursor.h b/db/clientcursor.h index a3a41530ae0..7806a699b12 100644 --- a/db/clientcursor.h +++ b/db/clientcursor.h @@ -39,6 +39,7 @@ namespace mongo { typedef long long CursorId; /* passed to the client so it can send back on getMore */ class Cursor; /* internal server cursor base class */ class ClientCursor; + class ParsedQuery; /* todo: make this map be per connection. this will prevent cursor hijacking security attacks perhaps. */ @@ -127,6 +128,7 @@ namespace mongo { return _lastLoc; } + shared_ptr< ParsedQuery > pq; shared_ptr< FieldMatcher > fields; // which fields query wants returned Message originalMessage; // this is effectively an auto ptr for data the matcher points to diff --git a/db/diskloc.h b/db/diskloc.h index cc29e60bd8d..ece9df202e5 100644 --- a/db/diskloc.h +++ b/db/diskloc.h @@ -22,6 +22,8 @@ #pragma once +#include "jsobj.h" + namespace mongo { #pragma pack(1) @@ -30,7 +32,6 @@ namespace mongo { class DeletedRecord; class Extent; class BtreeBucket; - class BSONObj; class MongoDataFile; class DiskLoc { @@ -87,6 +88,10 @@ namespace mongo { } operator string() const { return toString(); } + BSONObj toBSONObj() const { + return BSON( "file" << fileNo << "offset" << ofs ); + } + int& GETOFS() { return ofs; } diff --git a/db/query.cpp b/db/query.cpp index 42c1b9345bb..2213353329c 100644 --- a/db/query.cpp +++ b/db/query.cpp @@ -324,7 +324,7 @@ namespace mongo { last = c->currLoc(); BSONObj js = c->current(); - fillQueryResultFromObj(b, cc->fields.get(), js); + fillQueryResultFromObj(b, cc->fields.get(), js, (cc->pq->showDiskLoc() ? &last : 0)); n++; if ( (ntoreturn>0 && (n >= ntoreturn || b.len() > MaxBytesToReturnToClientAtOnce)) || (ntoreturn==0 && b.len()>1*1024*1024) ) { @@ -547,7 +547,7 @@ namespace mongo { if ( _inMemSort ) { // note: no cursors for non-indexed, ordered results. results must be fairly small. - _so->add( _pq.returnKey() ? _c->currKey() : _c->current() ); + _so->add( _pq.returnKey() ? _c->currKey() : _c->current(), _pq.showDiskLoc() ? &cl : 0 ); } else if ( _ntoskip > 0 ) { _ntoskip--; @@ -570,7 +570,7 @@ namespace mongo { else { BSONObj js = _c->current(); assert( js.isValid() ); - fillQueryResultFromObj( _buf , _pq.getFields() , js ); + fillQueryResultFromObj( _buf , _pq.getFields() , js , (_pq.showDiskLoc() ? &cl : 0)); } _n++; if ( ! _c->supportGetMore() ){ @@ -657,7 +657,8 @@ namespace mongo { /* run a query -- includes checking for and running a Command */ auto_ptr< QueryResult > runQuery(Message& m, QueryMessage& q, CurOp& curop ) { StringBuilder& ss = curop.debug().str; - ParsedQuery pq( q ); + shared_ptr pq_shared( new ParsedQuery(q) ); + ParsedQuery& pq( *pq_shared ); const char *ns = q.ns; int ntoskip = q.ntoskip; BSONObj jsobj = q.query; @@ -763,7 +764,7 @@ namespace mongo { uassert( 10110 , "bad query object", false); } - if ( ! explain && isSimpleIdQuery( query ) && !pq.hasOption( QueryOption_CursorTailable ) ) { + if ( ! (explain || pq.showDiskLoc()) && isSimpleIdQuery( query ) && !pq.hasOption( QueryOption_CursorTailable ) ) { nscanned = 1; bool nsFound = false; @@ -820,6 +821,7 @@ namespace mongo { DEV out() << " query has more, cursorid: " << cursorid << endl; cc->matcher = dqo.matcher(); cc->pos = n; + cc->pq = pq_shared; cc->fields = pq.getFieldPtr(); cc->originalMessage = m; cc->updateLocation(); diff --git a/db/query.h b/db/query.h index a89d3585752..df2471f89ed 100644 --- a/db/query.h +++ b/db/query.h @@ -176,6 +176,7 @@ namespace mongo { bool isExplain() const { return _explain; } bool isSnapshot() const { return _snapshot; } bool returnKey() const { return _returnKey; } + bool showDiskLoc() const { return _showDiskLoc; } const BSONObj& getMin() const { return _min; } const BSONObj& getMax() const { return _max; } @@ -244,6 +245,7 @@ namespace mongo { _explain = false; _snapshot = false; _returnKey = false; + _showDiskLoc = false; _maxScan = 0; } @@ -276,6 +278,8 @@ namespace mongo { _returnKey = e.trueValue(); else if ( strcmp( "$maxScan" , name ) == 0 ) _maxScan = e.numberInt(); + else if ( strcmp( "$showDiskLoc" , name ) == 0 ) + _showDiskLoc = e.trueValue(); } @@ -311,6 +315,7 @@ namespace mongo { bool _explain; bool _snapshot; bool _returnKey; + bool _showDiskLoc; BSONObj _min; BSONObj _max; BSONElement _hint; diff --git a/db/scanandorder.h b/db/scanandorder.h index b5d37c7a4a5..aef487ce8f2 100644 --- a/db/scanandorder.h +++ b/db/scanandorder.h @@ -50,7 +50,7 @@ namespace mongo { _ response size limit from runquery; push it up a bit. */ - inline void fillQueryResultFromObj(BufBuilder& bb, FieldMatcher *filter, BSONObj& js) { + inline void fillQueryResultFromObj(BufBuilder& bb, FieldMatcher *filter, BSONObj& js, DiskLoc* loc=NULL) { if ( filter ) { BSONObjBuilder b( bb ); BSONObjIterator i( js ); @@ -65,6 +65,13 @@ namespace mongo { filter->append( b , e ); } } + if (loc) + b.append("$diskLoc", loc->toBSONObj()); + b.done(); + } else if (loc) { + BSONObjBuilder b( bb ); + b.appendElements(js); + b.append("$diskLoc", loc->toBSONObj()); b.done(); } else { bb.append((void*) js.objdata(), js.objsize()); @@ -79,17 +86,24 @@ namespace mongo { KeyType order; unsigned approxSize; - void _add(BSONObj& k, BSONObj o) { - best.insert(make_pair(k,o)); + void _add(BSONObj& k, BSONObj o, DiskLoc* loc) { + if (!loc){ + best.insert(make_pair(k,o)); + } else { + BSONObjBuilder b; + b.appendElements(o); + b.append("$diskLoc", loc->toBSONObj()); + best.insert(make_pair(k, b.obj())); + } } - void _addIfBetter(BSONObj& k, BSONObj o, BestMap::iterator i) { + void _addIfBetter(BSONObj& k, BSONObj o, BestMap::iterator i, DiskLoc* loc) { const BSONObj& worstBestKey = i->first; int c = worstBestKey.woCompare(k, order.pattern); if ( c > 0 ) { // k is better, 'upgrade' best.erase(i); - _add(k, o); + _add(k, o, loc); } } @@ -105,20 +119,20 @@ namespace mongo { return best.size(); } - void add(BSONObj o) { + void add(BSONObj o, DiskLoc* loc) { assert( o.isValid() ); BSONObj k = order.getKeyFromObject(o); if ( (int) best.size() < limit ) { approxSize += k.objsize(); uassert( 10128 , "too much key data for sort() with no index. add an index or specify a smaller limit", approxSize < 1 * 1024 * 1024 ); - _add(k, o); + _add(k, o, loc); return; } BestMap::iterator i; assert( best.end() != best.begin() ); i = best.end(); i--; - _addIfBetter(k, o, i); + _addIfBetter(k, o, i, loc); } void _fill(BufBuilder& b, FieldMatcher *filter, int& nout, BestMap::iterator begin, BestMap::iterator end) { diff --git a/shell/query.js b/shell/query.js index c84feccbfd4..cf5815f9a95 100644 --- a/shell/query.js +++ b/shell/query.js @@ -33,6 +33,7 @@ DBQuery.prototype.help = function(){ print( "\t.forEach( func )" ) print( "\t.print() - output to console in full pretty format" ) print( "\t.map( func )" ) + print( "\t.showDiskLoc() - adds a $diskLoc field to each returned object" ) } @@ -199,6 +200,10 @@ DBQuery.prototype.max = function( max ) { return this._addSpecial( "$max" , max ); } +DBQuery.prototype.showDiskLoc = function() { + return this._addSpecial( "$showDiskLoc" , true); +} + DBQuery.prototype.forEach = function( func ){ while ( this.hasNext() ) func( this.next() );