diff options
author | Eliot Horowitz <eliot@10gen.com> | 2010-11-15 16:23:56 -0500 |
---|---|---|
committer | Eliot Horowitz <eliot@10gen.com> | 2010-11-15 16:36:02 -0500 |
commit | a0de8044927534fcd80aa6f66f0be344a1c8505d (patch) | |
tree | 83b2b3b19d68735529ce93dc1a080d55ace8cafe | |
parent | 48ad70487a5e0e4ba5b6399e01dedcb1702e235d (diff) | |
download | mongo-a0de8044927534fcd80aa6f66f0be344a1c8505d.tar.gz |
more FieldMatcher cleaning
-rw-r--r-- | SConstruct | 2 | ||||
-rw-r--r-- | db/clientcursor.h | 1 | ||||
-rw-r--r-- | db/projection.cpp | 203 | ||||
-rw-r--r-- | db/projection.h | 88 | ||||
-rw-r--r-- | db/query.h | 1 | ||||
-rw-r--r-- | db/queryutil.cpp | 187 | ||||
-rw-r--r-- | db/queryutil.h | 62 |
7 files changed, 294 insertions, 250 deletions
diff --git a/SConstruct b/SConstruct index 065485b2ec3..59920256db0 100644 --- a/SConstruct +++ b/SConstruct @@ -416,7 +416,7 @@ if GetOption( "full" ): # ------ SOURCE FILE SETUP ----------- -commonFiles = Split( "pch.cpp buildinfo.cpp db/common.cpp db/jsobj.cpp db/json.cpp db/lasterror.cpp db/nonce.cpp db/queryutil.cpp shell/mongo.cpp" ) +commonFiles = Split( "pch.cpp buildinfo.cpp db/common.cpp db/jsobj.cpp db/json.cpp db/lasterror.cpp db/nonce.cpp db/queryutil.cpp db/projection.cpp shell/mongo.cpp" ) commonFiles += [ "util/background.cpp" , "util/mmap.cpp" , "util/sock.cpp" , "util/util.cpp" , "util/message.cpp" , "util/assert_util.cpp" , "util/log.cpp" , "util/httpclient.cpp" , "util/md5main.cpp" , "util/base64.cpp", "util/concurrency/vars.cpp", "util/concurrency/task.cpp", "util/debug_util.cpp", "util/concurrency/thread_pool.cpp", "util/password.cpp", "util/version.cpp", "util/signal_handlers.cpp", diff --git a/db/clientcursor.h b/db/clientcursor.h index ddd39a18b25..4793ed37c01 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 { diff --git a/db/projection.cpp b/db/projection.cpp new file mode 100644 index 00000000000..29d80fe703e --- /dev/null +++ b/db/projection.cpp @@ -0,0 +1,203 @@ +// 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" + +namespace mongo { + + void FieldMatcher::init( 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::transform( const BSONObj& in ) const { + BSONObjBuilder b; + BSONObjIterator i(in); + while ( i.more() ) + append( b , i.next() ); + return b.obj(); + } + + + //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()); + } + } + } +} diff --git a/db/projection.h b/db/projection.h new file mode 100644 index 00000000000..730fc8e69d4 --- /dev/null +++ b/db/projection.h @@ -0,0 +1,88 @@ +// 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 FieldMatcher { + public: + FieldMatcher() + : _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; } + + /** + * appends e to b if user wants it + * will descend into e if needed + */ + void append( BSONObjBuilder& b , const BSONElement& e ) const; + + + /** + * transforms in according to spec + * NOTE: this will stricy obey _id, which is not true + * for normal queries + */ + BSONObj transform( const BSONObj& in ) const; + + /** + * @return if _id should be returned + */ + 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; + }; + + +} diff --git a/db/query.h b/db/query.h index e4ba08d33a9..fb2949c3602 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 diff --git a/db/queryutil.cpp b/db/queryutil.cpp index 3500852f6a6..6a36487cf46 100644 --- a/db/queryutil.cpp +++ b/db/queryutil.cpp @@ -790,194 +790,7 @@ namespace mongo { return ret; } - /////////////////// - // FieldMatcher // - /////////////////// - - void FieldMatcher::init( 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; - } - - BSONObj FieldMatcher::transform( const BSONObj& in ) const { - BSONObjBuilder b; - BSONObjIterator i(in); - while ( i.more() ) - append( b , i.next() ); - return b.obj(); - } - - - //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 763cc7a6cd5..0747b40a771 100644 --- a/db/queryutil.h +++ b/db/queryutil.h @@ -540,68 +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) - {} - - /** - * called once per lifetime - */ - void init( const BSONObj& spec ); - - /** - * @return the spec init was called with - */ - BSONObj getSpec() const; - - /** - * appends e to b if user wants it - * will descend into e if needed - */ - void append( BSONObjBuilder& b , const BSONElement& e ) const; - - - /** - * transforms in according to spec - * NOTE: this will stricy obey _id, which is not true - * for normal queries - */ - BSONObj transform( const BSONObj& in ) const; - - /** - * @return if _id should be returned - */ - 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 '^' |