0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-11-30 17:10:48 +01:00
mongodb/db/scanandorder.h

166 lines
5.2 KiB
C
Raw Normal View History

2008-11-14 22:19:47 +01:00
/* scanandorder.h
Order results (that aren't already indexes and in order.)
*/
/**
* Copyright (C) 2008 10gen Inc.
2008-12-29 02:28:49 +01:00
*
2008-11-14 22:19:47 +01:00
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
2008-12-29 02:28:49 +01:00
*
2008-11-14 22:19:47 +01:00
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
2008-12-29 02:28:49 +01:00
*
2008-11-14 22:19:47 +01:00
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
2008-10-13 03:09:59 +02:00
#pragma once
2009-01-14 23:09:51 +01:00
namespace mongo {
/* todo:
_ handle compound keys with differing directions. we don't handle this yet: neither here nor in indexes i think!!!
_ limit amount of data
*/
/* see also IndexDetails::getKeysFromObject, which needs some merging with this. */
class KeyType : boost::noncopyable {
public:
BSONObj pattern; // e.g., { ts : -1 }
public:
KeyType(BSONObj _keyPattern) {
pattern = _keyPattern;
assert( !pattern.isEmpty() );
}
2008-08-19 23:57:05 +02:00
// returns the key value for o
BSONObj getKeyFromObject(BSONObj o) {
return o.extractFields(pattern);
}
};
/* todo:
_ respect limit
_ check for excess mem usage
_ response size limit from runquery; push it up a bit.
*/
inline bool fillQueryResultFromObj(BufBuilder& bb, FieldMatcher *filter, BSONObj& js) {
if ( filter ) {
const int mark = bb.len();
2009-06-21 05:15:31 +02:00
BSONObjBuilder b( bb );
BSONObjIterator i( js );
int N = filter->size();
int n=0;
bool gotId = false;
while ( i.more() ){
BSONElement e = i.next();
const char * fname = e.fieldName();
if ( strcmp( fname , "_id" ) == 0 ){
b.append( e );
gotId = true;
if ( filter->matches( "_id" ) )
2009-06-22 19:56:27 +02:00
n++;
2009-06-21 05:15:31 +02:00
}
else if ( filter->matches( fname ) ){
filter->append( b , e );
2009-06-21 05:15:31 +02:00
n++;
if ( n == N && gotId )
break;
}
}
b.done();
if ( ! n )
bb.setlen( mark );
2009-06-21 05:15:31 +02:00
return n;
}
2009-06-21 05:15:31 +02:00
bb.append((void*) js.objdata(), js.objsize());
return true;
2008-12-29 02:28:49 +01:00
}
2009-06-21 05:15:31 +02:00
typedef multimap<BSONObj,BSONObj,BSONObjCmp> BestMap;
class ScanAndOrder {
BestMap best; // key -> full object
int startFrom;
int limit; // max to send back.
KeyType order;
unsigned approxSize;
2008-11-18 21:47:37 +01:00
void _add(BSONObj& k, BSONObj o) {
best.insert(make_pair(k,o));
2008-11-18 21:47:37 +01:00
}
void _addIfBetter(BSONObj& k, BSONObj o, BestMap::iterator i) {
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);
}
}
2008-12-29 02:28:49 +01:00
public:
ScanAndOrder(int _startFrom, int _limit, BSONObj _order) :
best( BSONObjCmp( _order ) ),
startFrom(_startFrom), order(_order) {
limit = _limit > 0 ? _limit + startFrom : 0x7fffffff;
approxSize = 0;
}
2008-12-29 02:28:49 +01:00
int size() const {
return best.size();
2008-12-29 02:28:49 +01:00
}
void add(BSONObj o) {
BSONObj k = order.getKeyFromObject(o);
if ( (int) best.size() < limit ) {
approxSize += k.objsize();
2009-09-09 22:38:52 +02:00
uassert( "too much key data for sort() with no index. add an index or specify a smaller limit", approxSize < 1 * 1024 * 1024 );
_add(k, o);
return;
2008-12-29 02:28:49 +01:00
}
BestMap::iterator i;
assert( best.end() != best.begin() );
i = best.end();
i--;
_addIfBetter(k, o, i);
2008-12-29 02:28:49 +01:00
}
void _fill(BufBuilder& b, FieldMatcher *filter, int& nout, BestMap::iterator begin, BestMap::iterator end) {
int n = 0;
int nFilled = 0;
for ( BestMap::iterator i = begin; i != end; i++ ) {
n++;
if ( n <= startFrom )
continue;
BSONObj& o = i->second;
if ( fillQueryResultFromObj(b, filter, o) ) {
nFilled++;
if ( nFilled >= limit )
goto done;
uassert( "too much data for sort() with no index", b.len() < 4000000 ); // appserver limit
}
}
2008-11-18 21:47:37 +01:00
done:
nout = nFilled;
}
2008-12-29 02:28:49 +01:00
/* scanning complete. stick the query result in b for n objects. */
void fill(BufBuilder& b, FieldMatcher *filter, int& nout) {
_fill(b, filter, nout, best.begin(), best.end());
}
2008-11-18 21:47:37 +01:00
};
2009-01-14 23:09:51 +01:00
} // namespace mongo