1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
// count.cpp
/**
* Copyright (C) 2008 10gen Inc.
*
* 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.
*
* 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.
*
* 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/>.
*/
#include "count.h"
#include "../client.h"
#include "../clientcursor.h"
#include "../namespace.h"
#include "../queryutil.h"
namespace mongo {
long long runCount( const char *ns, const BSONObj &cmd, string &err ) {
Client::Context cx(ns);
NamespaceDetails *d = nsdetails( ns );
if ( !d ) {
err = "ns missing";
return -1;
}
BSONObj query = cmd.getObjectField("query");
// count of all objects
if ( query.isEmpty() ) {
return applySkipLimit( d->stats.nrecords , cmd );
}
string exceptionInfo;
long long count = 0;
long long skip = cmd["skip"].numberLong();
long long limit = cmd["limit"].numberLong();
bool simpleEqualityMatch;
shared_ptr<Cursor> cursor = NamespaceDetailsTransient::getCursor( ns, query, BSONObj(), false, &simpleEqualityMatch );
ClientCursor::CleanupPointer ccPointer;
ElapsedTracker timeToStartYielding( 256, 20 );
try {
while( cursor->ok() ) {
if ( !ccPointer ) {
if ( timeToStartYielding.intervalHasElapsed() ) {
// Lazily construct a ClientCursor, avoiding a performance regression when scanning a very
// small number of documents.
ccPointer.reset( new ClientCursor( QueryOption_NoCursorTimeout, cursor, ns ) );
}
}
else if ( !ccPointer->yieldSometimes( simpleEqualityMatch ? ClientCursor::DontNeed : ClientCursor::MaybeCovered ) ||
!cursor->ok() ) {
break;
}
// With simple equality matching there is no need to use the matcher because the bounds
// are enforced by the FieldRangeVectorIterator and only key fields have constraints. There
// is no need to do key deduping because an exact value is specified in the query for all key
// fields and duplicate keys are not allowed per document.
// NOTE In the distant past we used a min/max bounded BtreeCursor with a shallow
// equality comparison to check for matches in the simple match case. That may be
// more performant, but I don't think we've measured the performance.
if ( simpleEqualityMatch ||
( cursor->currentMatches() && !cursor->getsetdup( cursor->currLoc() ) ) ) {
if ( skip > 0 ) {
--skip;
}
else {
++count;
if ( limit > 0 && count >= limit ) {
break;
}
}
}
cursor->advance();
}
ccPointer.reset();
return count;
} catch ( const DBException &e ) {
exceptionInfo = e.toString();
} catch ( const std::exception &e ) {
exceptionInfo = e.what();
} catch ( ... ) {
exceptionInfo = "unknown exception";
}
// Historically we have returned zero in many count assertion cases - see SERVER-2291.
log() << "Count with ns: " << ns << " and query: " << query
<< " failed with exception: " << exceptionInfo
<< endl;
return 0;
}
} // namespace mongo
|