diff options
author | dwight <dwight@10gen.com> | 2010-11-15 22:14:04 -0500 |
---|---|---|
committer | dwight <dwight@10gen.com> | 2010-11-15 22:14:04 -0500 |
commit | a4a1c54181f79f04addfbe1e9588051f123ee4d0 (patch) | |
tree | b5aa647bccd0ce9d3b3c35a91cbf12e4d5ea7776 /db | |
parent | 62eaedd3d21e9778c220e022f28eb67d67e81d8d (diff) | |
parent | b717404a1aca89d6dd75fc9c8c3f08277b4c19cb (diff) | |
download | mongo-a4a1c54181f79f04addfbe1e9588051f123ee4d0.tar.gz |
Merge branch 'master' of github.com:mongodb/mongo
Diffstat (limited to 'db')
-rw-r--r-- | db/clientcursor.h | 3 | ||||
-rw-r--r-- | db/commands/mr.cpp | 6 | ||||
-rw-r--r-- | db/dbwebserver.cpp | 2 | ||||
-rw-r--r-- | db/projection.cpp | 216 | ||||
-rw-r--r-- | db/projection.h | 95 | ||||
-rw-r--r-- | db/query.h | 11 | ||||
-rw-r--r-- | db/queryutil.cpp | 177 | ||||
-rw-r--r-- | db/queryutil.h | 38 | ||||
-rw-r--r-- | db/scanandorder.h | 19 |
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()); } |