summaryrefslogtreecommitdiff
path: root/db
diff options
context:
space:
mode:
authordwight <dwight@10gen.com>2010-11-15 22:14:04 -0500
committerdwight <dwight@10gen.com>2010-11-15 22:14:04 -0500
commita4a1c54181f79f04addfbe1e9588051f123ee4d0 (patch)
treeb5aa647bccd0ce9d3b3c35a91cbf12e4d5ea7776 /db
parent62eaedd3d21e9778c220e022f28eb67d67e81d8d (diff)
parentb717404a1aca89d6dd75fc9c8c3f08277b4c19cb (diff)
downloadmongo-a4a1c54181f79f04addfbe1e9588051f123ee4d0.tar.gz
Merge branch 'master' of github.com:mongodb/mongo
Diffstat (limited to 'db')
-rw-r--r--db/clientcursor.h3
-rw-r--r--db/commands/mr.cpp6
-rw-r--r--db/dbwebserver.cpp2
-rw-r--r--db/projection.cpp216
-rw-r--r--db/projection.h95
-rw-r--r--db/query.h11
-rw-r--r--db/queryutil.cpp177
-rw-r--r--db/queryutil.h38
-rw-r--r--db/scanandorder.h19
9 files changed, 329 insertions, 238 deletions
diff --git a/db/clientcursor.h b/db/clientcursor.h
index ddd39a18b25..98cbdd8f7c2 100644
--- a/db/clientcursor.h
+++ b/db/clientcursor.h
@@ -33,6 +33,7 @@
#include "dbhelpers.h"
#include "matcher.h"
#include "../client/dbclient.h"
+#include "projection.h"
namespace mongo {
@@ -354,7 +355,7 @@ namespace mongo {
public:
shared_ptr< ParsedQuery > pq;
- shared_ptr< FieldMatcher > fields; // which fields query wants returned
+ shared_ptr< Projection > fields; // which fields query wants returned
Message originalMessage; // this is effectively an auto ptr for data the matcher points to
diff --git a/db/commands/mr.cpp b/db/commands/mr.cpp
index 6398ed7bc7f..58d8ab65660 100644
--- a/db/commands/mr.cpp
+++ b/db/commands/mr.cpp
@@ -720,7 +720,11 @@ namespace mongo {
}
- long long finalCount = mr.renameIfNeeded( db , &state );
+ long long finalCount;
+ {
+ dblock lk;
+ finalCount = mr.renameIfNeeded( db , &state );
+ }
log(0) << " mapreducefinishcommand " << mr.finalLong << " " << finalCount << endl;
for ( set<ServerAndQuery>::iterator i=servers.begin(); i!=servers.end(); i++ ){
diff --git a/db/dbwebserver.cpp b/db/dbwebserver.cpp
index 3ade296a162..138b163207d 100644
--- a/db/dbwebserver.cpp
+++ b/db/dbwebserver.cpp
@@ -503,7 +503,7 @@ namespace mongo {
responseCode = 200;
- string j = result.done().jsonString(JS, text );
+ string j = result.done().jsonString(Strict, text );
responseMsg = j;
if( text ){
diff --git a/db/projection.cpp b/db/projection.cpp
new file mode 100644
index 00000000000..519e3c5162f
--- /dev/null
+++ b/db/projection.cpp
@@ -0,0 +1,216 @@
+// projection.cpp
+
+/* Copyright 2009 10gen Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "pch.h"
+#include "projection.h"
+#include "../util/mongoutils/str.h"
+
+namespace mongo {
+
+ void Projection::init( const BSONObj& o ){
+ massert( 10371 , "can only add to Projection once", _source.isEmpty());
+ _source = o;
+
+ BSONObjIterator i( o );
+ int true_false = -1;
+ while ( i.more() ){
+ BSONElement e = i.next();
+
+ if (e.type() == Object){
+ BSONObj obj = e.embeddedObject();
+ BSONElement e2 = obj.firstElement();
+ if ( strcmp(e2.fieldName(), "$slice") == 0 ){
+ if (e2.isNumber()){
+ int i = e2.numberInt();
+ if (i < 0)
+ add(e.fieldName(), i, -i); // limit is now positive
+ else
+ add(e.fieldName(), 0, i);
+
+ } else if (e2.type() == Array) {
+ BSONObj arr = e2.embeddedObject();
+ uassert(13099, "$slice array wrong size", arr.nFields() == 2 );
+
+ BSONObjIterator it(arr);
+ int skip = it.next().numberInt();
+ int limit = it.next().numberInt();
+ uassert(13100, "$slice limit must be positive", limit > 0 );
+ add(e.fieldName(), skip, limit);
+
+ } else {
+ uassert(13098, "$slice only supports numbers and [skip, limit] arrays", false);
+ }
+ } else {
+ uassert(13097, string("Unsupported projection option: ") + obj.firstElement().fieldName(), false);
+ }
+
+ } else if (!strcmp(e.fieldName(), "_id") && !e.trueValue()){
+ _includeID = false;
+
+ } else {
+
+ add (e.fieldName(), e.trueValue());
+
+ // validate input
+ if (true_false == -1){
+ true_false = e.trueValue();
+ _include = !e.trueValue();
+ }
+ else{
+ uassert( 10053 , "You cannot currently mix including and excluding fields. Contact us if this is an issue." ,
+ (bool)true_false == e.trueValue() );
+ }
+ }
+ }
+ }
+
+ void Projection::add(const string& field, bool include){
+ if (field.empty()){ // this is the field the user referred to
+ _include = include;
+ }
+ else {
+ _include = !include;
+
+ const size_t dot = field.find('.');
+ const string subfield = field.substr(0,dot);
+ const string rest = (dot == string::npos ? "" : field.substr(dot+1,string::npos));
+
+ boost::shared_ptr<Projection>& fm = _fields[subfield];
+ if (!fm)
+ fm.reset(new Projection());
+
+ fm->add(rest, include);
+ }
+ }
+
+ void Projection::add(const string& field, int skip, int limit){
+ _special = true; // can't include or exclude whole object
+
+ if (field.empty()){ // this is the field the user referred to
+ _skip = skip;
+ _limit = limit;
+ } else {
+ const size_t dot = field.find('.');
+ const string subfield = field.substr(0,dot);
+ const string rest = (dot == string::npos ? "" : field.substr(dot+1,string::npos));
+
+ boost::shared_ptr<Projection>& fm = _fields[subfield];
+ if (!fm)
+ fm.reset(new Projection());
+
+ fm->add(rest, skip, limit);
+ }
+ }
+
+ void Projection::transform( const BSONObj& in , BSONObjBuilder& b ) const {
+ BSONObjIterator i(in);
+ while ( i.more() ){
+ BSONElement e = i.next();
+ if ( mongoutils::str::equals( "_id" , e.fieldName() ) ){
+ if ( _includeID )
+ b.append( e );
+ }
+ else {
+ append( b , e );
+ }
+ }
+ }
+
+ BSONObj Projection::transform( const BSONObj& in ) const {
+ BSONObjBuilder b;
+ transform( in , b );
+ return b.obj();
+ }
+
+
+ //b will be the value part of an array-typed BSONElement
+ void Projection::appendArray( BSONObjBuilder& b , const BSONObj& a , bool nested) const {
+ int skip = nested ? 0 : _skip;
+ int limit = nested ? -1 : _limit;
+
+ if (skip < 0){
+ skip = max(0, skip + a.nFields());
+ }
+
+ int i=0;
+ BSONObjIterator it(a);
+ while (it.more()){
+ BSONElement e = it.next();
+
+ if (skip){
+ skip--;
+ continue;
+ }
+
+ if (limit != -1 && (limit-- == 0)){
+ break;
+ }
+
+ switch(e.type()){
+ case Array:{
+ BSONObjBuilder subb;
+ appendArray(subb , e.embeddedObject(), true);
+ b.appendArray(b.numStr(i++), subb.obj());
+ break;
+ }
+ case Object:{
+ BSONObjBuilder subb;
+ BSONObjIterator jt(e.embeddedObject());
+ while (jt.more()){
+ append(subb , jt.next());
+ }
+ b.append(b.numStr(i++), subb.obj());
+ break;
+ }
+ default:
+ if (_include)
+ b.appendAs(e, b.numStr(i++));
+ }
+ }
+ }
+
+ void Projection::append( BSONObjBuilder& b , const BSONElement& e ) const {
+ FieldMap::const_iterator field = _fields.find( e.fieldName() );
+
+ if (field == _fields.end()){
+ if (_include)
+ b.append(e);
+ }
+ else {
+ Projection& subfm = *field->second;
+
+ if ((subfm._fields.empty() && !subfm._special) || !(e.type()==Object || e.type()==Array) ){
+ if (subfm._include)
+ b.append(e);
+ }
+ else if (e.type() == Object){
+ BSONObjBuilder subb;
+ BSONObjIterator it(e.embeddedObject());
+ while (it.more()){
+ subfm.append(subb, it.next());
+ }
+ b.append(e.fieldName(), subb.obj());
+
+ }
+ else { //Array
+ BSONObjBuilder subb;
+ subfm.appendArray(subb, e.embeddedObject());
+ b.appendArray(e.fieldName(), subb.obj());
+ }
+ }
+ }
+}
diff --git a/db/projection.h b/db/projection.h
new file mode 100644
index 00000000000..65e27fc7a84
--- /dev/null
+++ b/db/projection.h
@@ -0,0 +1,95 @@
+// projection.h
+
+/* Copyright 2009 10gen Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "pch.h"
+#include "jsobj.h"
+
+namespace mongo {
+
+ /**
+ used for doing field limiting
+ */
+ class Projection {
+ public:
+ Projection()
+ : _include(true)
+ , _special(false)
+ , _includeID(true)
+ , _skip(0)
+ , _limit(-1)
+ {}
+
+ /**
+ * called once per lifetime
+ */
+ void init( const BSONObj& spec );
+
+ /**
+ * @return the spec init was called with
+ */
+ BSONObj getSpec() const { return _source; }
+
+ /**
+ * transforms in according to spec
+ */
+ BSONObj transform( const BSONObj& in ) const;
+
+
+ /**
+ * transforms in according to spec
+ */
+ void transform( const BSONObj& in , BSONObjBuilder& b ) const;
+
+
+ /**
+ * @return if the key has all the information needed to return
+ * NOTE: a key may have modified the actual data
+ * which has to be handled above this
+ */
+ bool keyEnough( const BSONObj& keyPattern ) const;
+
+ private:
+
+ /**
+ * appends e to b if user wants it
+ * will descend into e if needed
+ */
+ void append( BSONObjBuilder& b , const BSONElement& e ) const;
+
+
+ void add( const string& field, bool include );
+ void add( const string& field, int skip, int limit );
+ void appendArray( BSONObjBuilder& b , const BSONObj& a , bool nested=false) const;
+
+ bool _include; // true if default at this level is to include
+ bool _special; // true if this level can't be skipped or included without recursing
+
+ //TODO: benchmark vector<pair> vs map
+ typedef map<string, boost::shared_ptr<Projection> > FieldMap;
+ FieldMap _fields;
+ BSONObj _source;
+ bool _includeID;
+
+ // used for $slice operator
+ int _skip;
+ int _limit;
+ };
+
+
+}
diff --git a/db/query.h b/db/query.h
index 2b597fff9f3..6a66ec2dc65 100644
--- a/db/query.h
+++ b/db/query.h
@@ -23,6 +23,7 @@
#include "dbmessage.h"
#include "jsobj.h"
#include "diskloc.h"
+#include "projection.h"
/* db request message format
@@ -160,8 +161,8 @@ namespace mongo {
bool isLocalDB() const { return strncmp(_ns, "local.", 6) == 0; }
const BSONObj& getFilter() const { return _filter; }
- FieldMatcher* getFields() const { return _fields.get(); }
- shared_ptr<FieldMatcher> getFieldPtr() const { return _fields; }
+ Projection* getFields() const { return _fields.get(); }
+ shared_ptr<Projection> getFieldPtr() const { return _fields; }
int getSkip() const { return _ntoskip; }
int getNumToReturn() const { return _ntoreturn; }
@@ -292,8 +293,8 @@ namespace mongo {
void initFields( const BSONObj& fields ){
if ( fields.isEmpty() )
return;
- _fields.reset( new FieldMatcher() );
- _fields->add( fields );
+ _fields.reset( new Projection() );
+ _fields->init( fields );
}
ParsedQuery( const ParsedQuery& other ){
@@ -306,7 +307,7 @@ namespace mongo {
int _options;
BSONObj _filter;
- shared_ptr< FieldMatcher > _fields;
+ shared_ptr< Projection > _fields;
bool _wantMore;
diff --git a/db/queryutil.cpp b/db/queryutil.cpp
index 2b6322ecbc3..6a36487cf46 100644
--- a/db/queryutil.cpp
+++ b/db/queryutil.cpp
@@ -790,184 +790,7 @@ namespace mongo {
return ret;
}
- ///////////////////
- // FieldMatcher //
- ///////////////////
-
- void FieldMatcher::add( const BSONObj& o ){
- massert( 10371 , "can only add to FieldMatcher once", _source.isEmpty());
- _source = o;
-
- BSONObjIterator i( o );
- int true_false = -1;
- while ( i.more() ){
- BSONElement e = i.next();
-
- if (e.type() == Object){
- BSONObj obj = e.embeddedObject();
- BSONElement e2 = obj.firstElement();
- if ( strcmp(e2.fieldName(), "$slice") == 0 ){
- if (e2.isNumber()){
- int i = e2.numberInt();
- if (i < 0)
- add(e.fieldName(), i, -i); // limit is now positive
- else
- add(e.fieldName(), 0, i);
-
- } else if (e2.type() == Array) {
- BSONObj arr = e2.embeddedObject();
- uassert(13099, "$slice array wrong size", arr.nFields() == 2 );
-
- BSONObjIterator it(arr);
- int skip = it.next().numberInt();
- int limit = it.next().numberInt();
- uassert(13100, "$slice limit must be positive", limit > 0 );
- add(e.fieldName(), skip, limit);
-
- } else {
- uassert(13098, "$slice only supports numbers and [skip, limit] arrays", false);
- }
- } else {
- uassert(13097, string("Unsupported projection option: ") + obj.firstElement().fieldName(), false);
- }
-
- } else if (!strcmp(e.fieldName(), "_id") && !e.trueValue()){
- _includeID = false;
-
- } else {
-
- add (e.fieldName(), e.trueValue());
-
- // validate input
- if (true_false == -1){
- true_false = e.trueValue();
- _include = !e.trueValue();
- }
- else{
- uassert( 10053 , "You cannot currently mix including and excluding fields. Contact us if this is an issue." ,
- (bool)true_false == e.trueValue() );
- }
- }
- }
- }
-
- void FieldMatcher::add(const string& field, bool include){
- if (field.empty()){ // this is the field the user referred to
- _include = include;
- } else {
- _include = !include;
-
- const size_t dot = field.find('.');
- const string subfield = field.substr(0,dot);
- const string rest = (dot == string::npos ? "" : field.substr(dot+1,string::npos));
-
- boost::shared_ptr<FieldMatcher>& fm = _fields[subfield];
- if (!fm)
- fm.reset(new FieldMatcher());
-
- fm->add(rest, include);
- }
- }
-
- void FieldMatcher::add(const string& field, int skip, int limit){
- _special = true; // can't include or exclude whole object
-
- if (field.empty()){ // this is the field the user referred to
- _skip = skip;
- _limit = limit;
- } else {
- const size_t dot = field.find('.');
- const string subfield = field.substr(0,dot);
- const string rest = (dot == string::npos ? "" : field.substr(dot+1,string::npos));
-
- boost::shared_ptr<FieldMatcher>& fm = _fields[subfield];
- if (!fm)
- fm.reset(new FieldMatcher());
-
- fm->add(rest, skip, limit);
- }
- }
-
- BSONObj FieldMatcher::getSpec() const{
- return _source;
- }
-
- //b will be the value part of an array-typed BSONElement
- void FieldMatcher::appendArray( BSONObjBuilder& b , const BSONObj& a , bool nested) const {
- int skip = nested ? 0 : _skip;
- int limit = nested ? -1 : _limit;
- if (skip < 0){
- skip = max(0, skip + a.nFields());
- }
-
- int i=0;
- BSONObjIterator it(a);
- while (it.more()){
- BSONElement e = it.next();
-
- if (skip){
- skip--;
- continue;
- }
-
- if (limit != -1 && (limit-- == 0)){
- break;
- }
-
- switch(e.type()){
- case Array:{
- BSONObjBuilder subb;
- appendArray(subb , e.embeddedObject(), true);
- b.appendArray(b.numStr(i++), subb.obj());
- break;
- }
- case Object:{
- BSONObjBuilder subb;
- BSONObjIterator jt(e.embeddedObject());
- while (jt.more()){
- append(subb , jt.next());
- }
- b.append(b.numStr(i++), subb.obj());
- break;
- }
- default:
- if (_include)
- b.appendAs(e, b.numStr(i++));
- }
- }
- }
-
- void FieldMatcher::append( BSONObjBuilder& b , const BSONElement& e ) const {
- FieldMap::const_iterator field = _fields.find( e.fieldName() );
-
- if (field == _fields.end()){
- if (_include)
- b.append(e);
- }
- else {
- FieldMatcher& subfm = *field->second;
-
- if ((subfm._fields.empty() && !subfm._special) || !(e.type()==Object || e.type()==Array) ){
- if (subfm._include)
- b.append(e);
- }
- else if (e.type() == Object){
- BSONObjBuilder subb;
- BSONObjIterator it(e.embeddedObject());
- while (it.more()){
- subfm.append(subb, it.next());
- }
- b.append(e.fieldName(), subb.obj());
-
- }
- else { //Array
- BSONObjBuilder subb;
- subfm.appendArray(subb, e.embeddedObject());
- b.appendArray(e.fieldName(), subb.obj());
- }
- }
- }
bool FieldRangeVector::matchesElement( const BSONElement &e, int i, bool forward ) const {
bool eq;
diff --git a/db/queryutil.h b/db/queryutil.h
index e73c0c5d9c5..0747b40a771 100644
--- a/db/queryutil.h
+++ b/db/queryutil.h
@@ -540,44 +540,6 @@ namespace mongo {
bool _orFound;
};
- /**
- used for doing field limiting
- */
- class FieldMatcher {
- public:
- FieldMatcher()
- : _include(true)
- , _special(false)
- , _includeID(true)
- , _skip(0)
- , _limit(-1)
- {}
-
- void add( const BSONObj& o );
-
- void append( BSONObjBuilder& b , const BSONElement& e ) const;
-
- BSONObj getSpec() const;
- bool includeID() { return _includeID; }
- private:
-
- void add( const string& field, bool include );
- void add( const string& field, int skip, int limit );
- void appendArray( BSONObjBuilder& b , const BSONObj& a , bool nested=false) const;
-
- bool _include; // true if default at this level is to include
- bool _special; // true if this level can't be skipped or included without recursing
- //TODO: benchmark vector<pair> vs map
- typedef map<string, boost::shared_ptr<FieldMatcher> > FieldMap;
- FieldMap _fields;
- BSONObj _source;
- bool _includeID;
-
- // used for $slice operator
- int _skip;
- int _limit;
- };
-
/** returns a string that when used as a matcher, would match a super set of regex()
returns "" for complex regular expressions
used to optimize queries in some simple regex cases that start with '^'
diff --git a/db/scanandorder.h b/db/scanandorder.h
index 8d63b9ab34c..ee4af2e03d9 100644
--- a/db/scanandorder.h
+++ b/db/scanandorder.h
@@ -50,21 +50,10 @@ namespace mongo {
_ response size limit from runquery; push it up a bit.
*/
- inline void fillQueryResultFromObj(BufBuilder& bb, FieldMatcher *filter, BSONObj& js, DiskLoc* loc=NULL) {
+ inline void fillQueryResultFromObj(BufBuilder& bb, Projection *filter, BSONObj& js, DiskLoc* loc=NULL) {
if ( filter ) {
BSONObjBuilder b( bb );
- BSONObjIterator i( js );
- while ( i.more() ){
- BSONElement e = i.next();
- const char * fname = e.fieldName();
-
- if ( strcmp( fname , "_id" ) == 0 ){
- if (filter->includeID())
- b.append( e );
- } else {
- filter->append( b , e );
- }
- }
+ filter->transform( js , b );
if (loc)
b.append("$diskLoc", loc->toBSONObj());
b.done();
@@ -140,7 +129,7 @@ namespace mongo {
_addIfBetter(k, o, i, loc);
}
- void _fill(BufBuilder& b, FieldMatcher *filter, int& nout, BestMap::iterator begin, BestMap::iterator end) {
+ void _fill(BufBuilder& b, Projection *filter, int& nout, BestMap::iterator begin, BestMap::iterator end) {
int n = 0;
int nFilled = 0;
for ( BestMap::iterator i = begin; i != end; i++ ) {
@@ -158,7 +147,7 @@ namespace mongo {
}
/* scanning complete. stick the query result in b for n objects. */
- void fill(BufBuilder& b, FieldMatcher *filter, int& nout) {
+ void fill(BufBuilder& b, Projection *filter, int& nout) {
_fill(b, filter, nout, best.begin(), best.end());
}