Exemplo n.º 1
0
        /**
         * @param arrayNestedArray - set if the returned element is an array nested directly within arr.
         */
        BSONElement extractNextElement( const BSONObj &obj, const BSONObj &arr, const char *&field, bool &arrayNestedArray ) const {
            string firstField = mongoutils::str::before( field, '.' );
            bool haveObjField = !obj.getField( firstField ).eoo();
            BSONElement arrField = arr.getField( firstField );
            bool haveArrField = !arrField.eoo();

            // An index component field name cannot exist in both a document array and one of that array's children.
            uassert( 15855,
                     mongoutils::str::stream() <<
                         "Ambiguous field name found in array (do not use numeric field names in "
                         "embedded elements in an array), field: '" << arrField.fieldName() <<
                         "' for array: " << arr,
                     !haveObjField || !haveArrField );

            arrayNestedArray = false;
			if ( haveObjField ) {
                return obj.getFieldDottedOrArray( field );
            }
            else if ( haveArrField ) {
                if ( arrField.type() == Array ) {
                    arrayNestedArray = true;
                }
                return arr.getFieldDottedOrArray( field );
            }
            return BSONElement();
        }
// static
void ExpressionKeysPrivate::getHashKeys(const BSONObj& obj,
                                        const string& hashedField,
                                        HashSeed seed,
                                        int hashVersion,
                                        bool isSparse,
                                        const CollatorInterface* collator,
                                        BSONObjSet* keys) {
    const char* cstr = hashedField.c_str();
    BSONElement fieldVal = obj.getFieldDottedOrArray(cstr);

    // Convert strings to comparison keys.
    BSONObj fieldValObj;
    if (!fieldVal.eoo()) {
        BSONObjBuilder bob;
        CollationIndexKey::collationAwareIndexKeyAppend(fieldVal, collator, &bob);
        fieldValObj = bob.obj();
        fieldVal = fieldValObj.firstElement();
    }

    uassert(16766,
            "Error: hashed indexes do not currently support array values",
            fieldVal.type() != Array);

    if (!fieldVal.eoo()) {
        BSONObj key = BSON("" << makeSingleHashKey(fieldVal, seed, hashVersion));
        keys->insert(key);
    } else if (!isSparse) {
        BSONObj nullObj = BSON("" << BSONNULL);
        keys->insert(BSON("" << makeSingleHashKey(nullObj.firstElement(), seed, hashVersion)));
    }
}
Exemplo n.º 3
0
        /**
         * Checks if phrase is exactly matched in obj, returns true if so, false otherwise
         * @param phrase, the string to be matched
         * @param obj, document in the collection to match against
         */
        bool FTSMatcher::phraseMatch( const string& phrase, const BSONObj& obj ) const {

            if ( _spec.wildcard() ) {
                // case where everything is indexed (all fields)
                return _phraseRecurse( phrase, obj );
            }

            for ( Weights::const_iterator i = _spec.weights().begin();
                  i != _spec.weights().end();
                  ++i ) {

                //  figure out what the indexed field is.. ie. is it "field" or "field.subfield" etc.
                const char * leftOverName = i->first.c_str();
                BSONElement e = obj.getFieldDottedOrArray(leftOverName);

                if ( e.type() == Array ) {
                    BSONObjIterator j( e.Obj() );
                    while ( j.more() ) {
                        BSONElement x = j.next();

                        if ( leftOverName[0] && x.isABSONObj() )
                            x = x.Obj().getFieldDotted( leftOverName );

                        if ( x.type() == String )
                            if ( _phraseMatches( phrase, x.String() ) )
                                return true;
                    }
                }
                else if ( e.type() == String ) {
                    if ( _phraseMatches( phrase, e.String() ) )
                        return true;
                }
            }
            return false;
        }
Exemplo n.º 4
0
BSONElement BtreeKeyGeneratorV1::extractNextElement(const BSONObj& obj,
                                                    const PositionalPathInfo& positionalInfo,
                                                    const char** field,
                                                    bool* arrayNestedArray) const {
    std::string firstField = mongoutils::str::before(*field, '.');
    bool haveObjField = !obj.getField(firstField).eoo();
    BSONElement arrField = positionalInfo.positionallyIndexedElt;

    // An index component field name cannot exist in both a document
    // array and one of that array's children.
    uassert(16746,
            mongoutils::str::stream()
                << "Ambiguous field name found in array (do not use numeric field names in "
                   "embedded elements in an array), field: '" << arrField.fieldName()
                << "' for array: " << positionalInfo.arrayObj,
            !haveObjField || !positionalInfo.hasPositionallyIndexedElt());

    *arrayNestedArray = false;
    if (haveObjField) {
        return obj.getFieldDottedOrArray(*field);
    } else if (positionalInfo.hasPositionallyIndexedElt()) {
        if (arrField.type() == Array) {
            *arrayNestedArray = true;
        }
        *field = positionalInfo.remainingPath;
        return positionalInfo.dottedElt;
    }
    return BSONElement();
}
Exemplo n.º 5
0
    void HashAccessMethod::getKeys(const BSONObj& obj, BSONObjSet* keys) {
        const char* cstr = _hashedField.c_str();
        BSONElement fieldVal = obj.getFieldDottedOrArray(cstr);
        uassert(16766, "Error: hashed indexes do not currently support array values",
                fieldVal.type() != Array );

        if (!fieldVal.eoo()) {
            BSONObj key = BSON( "" << makeSingleKey( fieldVal , _seed , _hashVersion  ) );
            keys->insert( key );
        } else if (!_descriptor->isSparse()) {
            keys->insert( _missingKey.copy() );
        }
    }
Exemplo n.º 6
0
    void HashedIndexType::getKeys( const BSONObj &obj, BSONObjSet &keys ) const {
        string hashedFieldCopy = string( _hashedField );
        const char* hashedFieldCopyPtr = hashedFieldCopy.c_str();
        BSONElement fieldVal = obj.getFieldDottedOrArray( hashedFieldCopyPtr );

        uassert( 16244 , "Error: hashed indexes do not currently support array values" , fieldVal.type() != Array );

        if ( ! fieldVal.eoo() ) {
            BSONObj key = BSON( "" << makeSingleKey( fieldVal , _seed , _hashVersion  ) );
            keys.insert( key );
        }
        else if (! _isSparse ) {
            keys.insert( _missingKey.copy() );
        }
    }
Exemplo n.º 7
0
    // static
    void HashAccessMethod::getKeysImpl(const BSONObj& obj, const string& hashedField, HashSeed seed,
                                       int hashVersion, bool isSparse, BSONObjSet* keys) {
        const char* cstr = hashedField.c_str();
        BSONElement fieldVal = obj.getFieldDottedOrArray(cstr);
        uassert(16766, "Error: hashed indexes do not currently support array values",
                fieldVal.type() != Array );

        if (!fieldVal.eoo()) {
            BSONObj key = BSON( "" << makeSingleKey(fieldVal, seed, hashVersion));
            keys->insert(key);
        }
        else if (!isSparse) {
            BSONObj nullObj = BSON("" << BSONNULL);
            keys->insert(BSON("" << makeSingleKey(nullObj.firstElement(), seed, hashVersion)));
        }
    }
Exemplo n.º 8
0
        /*
         * Calculates the score for all terms in a document of a collection
         * @param obj, the document in the collection being parsed
         * @param term_freqs, map<string,double> to fill up
         */
        void FTSSpec::scoreDocument( const BSONObj& obj, TermFrequencyMap* term_freqs ) const {

            string language = getLanguageToUse( obj );

            Stemmer stemmer(language);
            Tools tools(language);
            tools.stemmer = &stemmer;
            tools.stopwords = StopWords::getStopWords( language );

            if ( wildcard() ) {
                // if * is specified for weight, we can recurse over all fields.
                _scoreRecurse(tools, obj, term_freqs);
                return;
            }

            // otherwise, we need to remember the different weights for each field
            // and act accordingly (in other words, call _score)
            for ( Weights::const_iterator i = _weights.begin(); i != _weights.end(); i++ ) {
                const char * leftOverName = i->first.c_str();
                // name of field
                BSONElement e = obj.getFieldDottedOrArray(leftOverName);
                // weight associated to name of field
                double weight = i->second;

                if ( e.eoo() ) {
                    // do nothing
                }
                else if ( e.type() == Array ) {
                    BSONObjIterator j( e.Obj() );
                    while ( j.more() ) {
                        BSONElement x = j.next();
                        if ( leftOverName[0] && x.isABSONObj() )
                            x = x.Obj().getFieldDotted( leftOverName );
                        if ( x.type() == String )
                            _scoreString( tools, x.valuestr(), term_freqs, weight );
                    }
                }
                else if ( e.type() == String ) {
                    _scoreString( tools, e.valuestr(), term_freqs, weight );
                }

            }
        }
Exemplo n.º 9
0
        /*
         * Checks if the obj contains any of the negTerms, if so returns true, otherwise false
         * @param obj, object to be checked
         */
        bool FTSMatcher::hasNegativeTerm(const BSONObj& obj ) const {
            // called during search. deals with the case in which we have a term
            // flagged for exclusion, i.e. "hello -world" we want to remove all
            // results that include "world"

            if ( _query.getNegatedTerms().size() == 0 )
                return false;

            if ( _spec.wildcard() ) {
                return _hasNegativeTerm_recurse(obj);
            }

            /* otherwise look at fields where weights are defined */
            for ( Weights::const_iterator i = _spec.weights().begin();
                  i != _spec.weights().end();
                  i++ ) {
                const char * leftOverName = i->first.c_str();
                BSONElement e = obj.getFieldDottedOrArray(leftOverName);

                if ( e.type() == Array ) {
                    BSONObjIterator j( e.Obj() );
                    while ( j.more() ) {
                        BSONElement x = j.next();
                        if ( leftOverName[0] && x.isABSONObj() )
                            x = x.Obj().getFieldDotted( leftOverName );
                        if ( x.type() == String )
                            if ( _hasNegativeTerm_string( x.String() ) )
                                return true;
                    }
                }
                else if ( e.type() == String ) {
                    if ( _hasNegativeTerm_string( e.String() ) )
                        return true;
                }
            }
            return false;
        }
Exemplo n.º 10
0
    void IndexSpec::_getKeys( vector<const char*> fieldNames , vector<BSONElement> fixed , const BSONObj &obj, BSONObjSetDefaultOrder &keys ) const {
        BSONElement arrElt;
        unsigned arrIdx = ~0;
        for( unsigned i = 0; i < fieldNames.size(); ++i ) {
            if ( *fieldNames[ i ] == '\0' )
                continue;
            BSONElement e = obj.getFieldDottedOrArray( fieldNames[ i ] );
            if ( e.eoo() )
                e = _nullElt; // no matching field
            if ( e.type() != Array )
                fieldNames[ i ] = ""; // no matching field or non-array match
            if ( *fieldNames[ i ] == '\0' )
                fixed[ i ] = e; // no need for further object expansion (though array expansion still possible)
            if ( e.type() == Array && arrElt.eoo() ) { // we only expand arrays on a single path -- track the path here
                arrIdx = i;
                arrElt = e;
            }
            // enforce single array path here
            uassert( 10088 ,  "cannot index parallel arrays", e.type() != Array || e.rawdata() == arrElt.rawdata() );
        }

        bool allFound = true; // have we found elements for all field names in the key spec?
        for( vector<const char*>::const_iterator i = fieldNames.begin(); i != fieldNames.end(); ++i ){
            if ( **i != '\0' ){
                allFound = false;
                break;
            }
        }

        bool insertArrayNull = false;

        if ( allFound ) {
            if ( arrElt.eoo() ) {
                // no terminal array element to expand
                BSONObjBuilder b(_sizeTracker);
                for( vector< BSONElement >::iterator i = fixed.begin(); i != fixed.end(); ++i )
                    b.appendAs( *i, "" );
                keys.insert( b.obj() );
            } 
            else {
                // terminal array element to expand, so generate all keys
                BSONObjIterator i( arrElt.embeddedObject() );
                if ( i.more() ){
                    while( i.more() ) {
                        BSONObjBuilder b(_sizeTracker);
                        for( unsigned j = 0; j < fixed.size(); ++j ) {
                            if ( j == arrIdx )
                                b.appendAs( i.next(), "" );
                            else
                                b.appendAs( fixed[ j ], "" );
                        }
                        keys.insert( b.obj() );
                    }
                }
                else if ( fixed.size() > 1 ){
                    insertArrayNull = true;
                }
            }
        } else {
            // nonterminal array element to expand, so recurse
            assert( !arrElt.eoo() );
            BSONObjIterator i( arrElt.embeddedObject() );
            if ( i.more() ){
                while( i.more() ) {
                    BSONElement e = i.next();
                    if ( e.type() == Object ){
                        _getKeys( fieldNames, fixed, e.embeddedObject(), keys );
                    }
                }
            }
            else {
                insertArrayNull = true;
            }
        }
        
        if ( insertArrayNull ) {
            // x : [] - need to insert undefined
            BSONObjBuilder b(_sizeTracker);
            for( unsigned j = 0; j < fixed.size(); ++j ) {
                if ( j == arrIdx ){
                    b.appendUndefined( "" );
                }
                else {
                    BSONElement e = fixed[j];
                    if ( e.eoo() )
                        b.appendNull( "" );
                    else
                        b.appendAs( e , "" );
                }
            }
            keys.insert( b.obj() );
        }
    }
Exemplo n.º 11
0
    void BtreeKeyGeneratorV0::getKeysImpl(vector<const char*> fieldNames, vector<BSONElement> fixed,
                                          const BSONObj &obj, BSONObjSet *keys) const {
        BSONElement arrElt;
        unsigned arrIdx = ~0;
        unsigned numNotFound = 0;

        for ( unsigned i = 0; i < fieldNames.size(); ++i ) {
            if ( *fieldNames[ i ] == '\0' )
                continue;

            BSONElement e = obj.getFieldDottedOrArray( fieldNames[ i ] );

            if ( e.eoo() ) {
                e = _nullElt; // no matching field
                numNotFound++;
            }

            if ( e.type() != Array )
                fieldNames[ i ] = ""; // no matching field or non-array match

            if ( *fieldNames[ i ] == '\0' )
                // no need for further object expansion (though array expansion still possible)
                fixed[ i ] = e;

            if ( e.type() == Array && arrElt.eoo() ) {
                // we only expand arrays on a single path -- track the path here
                arrIdx = i;
                arrElt = e;
            }

            // enforce single array path here
            if ( e.type() == Array && e.rawdata() != arrElt.rawdata() ) {
                assertParallelArrays( e.fieldName(), arrElt.fieldName() );
            }
        }

        bool allFound = true; // have we found elements for all field names in the key spec?
        for (vector<const char*>::const_iterator i = fieldNames.begin(); i != fieldNames.end();
             ++i ) {
            if ( **i != '\0' ) {
                allFound = false;
                break;
            }
        }

        if ( _isSparse && numNotFound == _fieldNames.size()) {
            // we didn't find any fields
            // so we're not going to index this document
            return;
        }

        bool insertArrayNull = false;

        if ( allFound ) {
            if ( arrElt.eoo() ) {
                // no terminal array element to expand
                BSONObjBuilder b(_sizeTracker);
                for( vector< BSONElement >::iterator i = fixed.begin(); i != fixed.end(); ++i )
                    b.appendAs( *i, "" );
                keys->insert( b.obj() );
            }
            else {
                // terminal array element to expand, so generate all keys
                BSONObjIterator i( arrElt.embeddedObject() );
                if ( i.more() ) {
                    while( i.more() ) {
                        BSONObjBuilder b(_sizeTracker);
                        for( unsigned j = 0; j < fixed.size(); ++j ) {
                            if ( j == arrIdx )
                                b.appendAs( i.next(), "" );
                            else
                                b.appendAs( fixed[ j ], "" );
                        }
                        keys->insert( b.obj() );
                    }
                }
                else if ( fixed.size() > 1 ) {
                    insertArrayNull = true;
                }
            }
        }
        else {
            // nonterminal array element to expand, so recurse
            verify( !arrElt.eoo() );
            BSONObjIterator i( arrElt.embeddedObject() );
            if ( i.more() ) {
                while( i.more() ) {
                    BSONElement e = i.next();
                    if ( e.type() == Object ) {
                        getKeysImpl( fieldNames, fixed, e.embeddedObject(), keys );
                    }
                }
            }
            else {
                insertArrayNull = true;
            }
        }

        if ( insertArrayNull ) {
            // x : [] - need to insert undefined
            BSONObjBuilder b(_sizeTracker);
            for( unsigned j = 0; j < fixed.size(); ++j ) {
                if ( j == arrIdx ) {
                    b.appendUndefined( "" );
                }
                else {
                    BSONElement e = fixed[j];
                    if ( e.eoo() )
                        b.appendNull( "" );
                    else
                        b.appendAs( e , "" );
                }
            }
            keys->insert( b.obj() );
        }
    }
Exemplo n.º 12
0
      // PD_TRACE_DECLARE_FUNCTION ( SDB__IXMKEYGEN__GETKEYS, "_ixmKeyGenerator::_getKeys" )
      INT32 _getKeys( vector<const CHAR *> &fieldNames,
                      const BSONObj &obj,
                      BSONObjSet &keys,
                      BSONElement *arrEle ) const
      {
         INT32 rc = SDB_OK ;
         PD_TRACE_ENTRY ( SDB__IXMKEYGEN__GETKEYS );
#define IXM_DEFAULT_FIELD_NUM 3
         BSONElement eleOnStack[IXM_DEFAULT_FIELD_NUM] ;
         BSONElement *keyEles = NULL ;
         const CHAR *arrEleName = NULL ;
         UINT32 arrElePos = 0 ;
         UINT32 eooNum = 0 ;

         if ( IXM_DEFAULT_FIELD_NUM < fieldNames.size() )
         {
            keyEles = new(std::nothrow) BSONElement[fieldNames.size()] ;
            if ( NULL == keyEles )
            {
               PD_LOG( PDERROR, "failed to allocalte mem." ) ;
               rc = SDB_OOM ;
               goto error ;
            }
         }
         else
         {
            keyEles = ( BSONElement* )eleOnStack ;
         }

         for ( UINT32 i = 0; i < fieldNames.size(); i++ )
         {
            const CHAR *name = fieldNames.at( i ) ;
            SDB_ASSERT( '\0' != name[0], "can not be empty" ) ;
            BSONElement &e = keyEles[i] ;
            e = obj.getFieldDottedOrArray( name ) ;
            if ( e.eoo() )
            {
               ++eooNum ;
               continue ;
            }
            else if ( Array == e.type() )
            {
               if ( !arrEle->eoo() )
               {
                  PD_LOG( PDERROR, "At most one array can be in the key:",
                          arrEle->fieldName(), e.fieldName() ) ;
                  rc = SDB_IXM_MULTIPLE_ARRAY ;
                  goto error ;
               }
               else
               {
                  *arrEle = e ;
                  arrEleName = name ;
                  arrElePos = i ;
               }
            }
            else
            {
               continue ;
            }
         }

         if ( fieldNames.size() == eooNum )
         {
            rc = SDB_OK ;
            goto done ;
         }
         else if ( !arrEle->eoo() )
         {
            rc = _genKeyWithArrayEle( keyEles, fieldNames.size(),
                                      arrEle,
                                      arrEleName, arrElePos,
                                      keys ) ;
            if ( SDB_OK != rc )
            {
               PD_LOG( PDERROR, "failed to gen keys with array element:%d", rc ) ;
               goto error ;
            }
         }
         else
         {
            rc = _genKeyWithNormalEle( keyEles, fieldNames.size(), keys ) ;
            if ( SDB_OK != rc )
            {
               PD_LOG( PDERROR, "failed to gen keys with normal element:%d", rc ) ;
               goto error ;
            }
         }
      done:
         if ( IXM_DEFAULT_FIELD_NUM < fieldNames.size() &&
              NULL != keyEles )
         {
            delete []keyEles ;
         }
         PD_TRACE_EXITRC ( SDB__IXMKEYGEN__GETKEYS, rc );
         return rc ;
      error:
         goto done ;
      }
Exemplo n.º 13
0
void BtreeKeyGeneratorV1::getKeysImplWithArray(
    std::vector<const char*> fieldNames,
    std::vector<BSONElement> fixed,
    const BSONObj& obj,
    BSONObjSet* keys,
    unsigned numNotFound,
    const std::vector<PositionalPathInfo>& positionalInfo,
    MultikeyPaths* multikeyPaths) const {
    BSONElement arrElt;

    // A set containing the position of any indexed fields in the key pattern that traverse through
    // the 'arrElt' array value.
    std::set<size_t> arrIdxs;

    // A vector with size equal to the number of elements in the index key pattern. Each element in
    // the vector, if initialized, refers to the component within the indexed field that traverses
    // through the 'arrElt' array value. We say that this component within the indexed field
    // corresponds to a path that causes the index to be multikey if the 'arrElt' array value
    // contains multiple elements.
    //
    // For example, consider the index {'a.b': 1, 'a.c'} and the document
    // {a: [{b: 1, c: 'x'}, {b: 2, c: 'y'}]}. The path "a" causes the index to be multikey, so we'd
    // have a std::vector<boost::optional<size_t>>{{0U}, {0U}}.
    //
    // Furthermore, due to how positional key patterns are specified, it's possible for an indexed
    // field to cause the index to be multikey at a different component than another indexed field
    // that also traverses through the 'arrElt' array value. It's then also possible for an indexed
    // field not to cause the index to be multikey, even if it traverses through the 'arrElt' array
    // value, because only a particular element would be indexed.
    //
    // For example, consider the index {'a.b': 1, 'a.b.0'} and the document {a: {b: [1, 2]}}. The
    // path "a.b" causes the index to be multikey, but the key pattern "a.b.0" only indexes the
    // first element of the array, so we'd have a
    // std::vector<boost::optional<size_t>>{{1U}, boost::none}.
    std::vector<boost::optional<size_t>> arrComponents(fieldNames.size());

    bool mayExpandArrayUnembedded = true;
    for (size_t i = 0; i < fieldNames.size(); ++i) {
        if (*fieldNames[i] == '\0') {
            continue;
        }

        bool arrayNestedArray;
        // Extract element matching fieldName[ i ] from object xor array.
        BSONElement e =
            extractNextElement(obj, positionalInfo[i], &fieldNames[i], &arrayNestedArray);

        if (e.eoo()) {
            // if field not present, set to null
            fixed[i] = nullElt;
            // done expanding this field name
            fieldNames[i] = "";
            numNotFound++;
        } else if (e.type() == Array) {
            arrIdxs.insert(i);
            if (arrElt.eoo()) {
                // we only expand arrays on a single path -- track the path here
                arrElt = e;
            } else if (e.rawdata() != arrElt.rawdata()) {
                // enforce single array path here
                assertParallelArrays(e.fieldName(), arrElt.fieldName());
            }
            if (arrayNestedArray) {
                mayExpandArrayUnembedded = false;
            }
        } else {
            // not an array - no need for further expansion
            fixed[i] = e;
        }
    }

    if (arrElt.eoo()) {
        // No array, so generate a single key.
        if (_isSparse && numNotFound == fieldNames.size()) {
            return;
        }
        BSONObjBuilder b(_sizeTracker);
        for (std::vector<BSONElement>::iterator i = fixed.begin(); i != fixed.end(); ++i) {
            b.appendAs(*i, "");
        }
        keys->insert(b.obj());
    } else if (arrElt.embeddedObject().firstElement().eoo()) {
        // Empty array, so set matching fields to undefined.
        _getKeysArrEltFixed(&fieldNames,
                            &fixed,
                            undefinedElt,
                            keys,
                            numNotFound,
                            arrElt,
                            arrIdxs,
                            true,
                            _emptyPositionalInfo,
                            multikeyPaths);
    } else {
        BSONObj arrObj = arrElt.embeddedObject();

        // For positional key patterns, e.g. {'a.1.b': 1}, we lookup the indexed array element
        // and then traverse the remainder of the field path up front. This prevents us from
        // having to look up the indexed element again on each recursive call (i.e. once per
        // array element).
        std::vector<PositionalPathInfo> subPositionalInfo(fixed.size());
        for (size_t i = 0; i < fieldNames.size(); ++i) {
            const bool fieldIsArray = arrIdxs.find(i) != arrIdxs.end();

            if (*fieldNames[i] == '\0') {
                // We've reached the end of the path.
                if (multikeyPaths && fieldIsArray && mayExpandArrayUnembedded) {
                    // The 'arrElt' array value isn't expanded into multiple elements when the last
                    // component of the indexed field is positional and 'arrElt' contains nested
                    // array values. In all other cases, the 'arrElt' array value may be expanded
                    // into multiple element and can therefore cause the index to be multikey.
                    arrComponents[i] = _pathLengths[i] - 1;
                }
                continue;
            }

            // The earlier call to BSONObj::getFieldDottedOrArray(fieldNames[i]) modified
            // fieldNames[i] to refer to the suffix of the path immediately following the 'arrElt'
            // array value. If we haven't reached the end of this indexed field yet, then we must
            // have traversed through 'arrElt'.
            invariant(fieldIsArray);

            StringData part = fieldNames[i];
            part = part.substr(0, part.find('.'));
            subPositionalInfo[i].positionallyIndexedElt = arrObj[part];
            if (subPositionalInfo[i].positionallyIndexedElt.eoo()) {
                // We aren't indexing a particular element of the 'arrElt' array value, so it may be
                // expanded into multiple elements. It can therefore cause the index to be multikey.
                if (multikeyPaths) {
                    // We need to determine which component of the indexed field causes the index to
                    // be multikey as a result of the 'arrElt' array value. Since
                    //
                    //   NumComponents("<pathPrefix>") + NumComponents("<pathSuffix>")
                    //       = NumComponents("<pathPrefix>.<pathSuffix>"),
                    //
                    // we can compute the number of components in a prefix of the indexed field by
                    // subtracting the number of components in the suffix 'fieldNames[i]' from the
                    // number of components in the indexed field '_fieldNames[i]'.
                    //
                    // For example, consider the indexed field "a.b.c" and the suffix "c". The path
                    // "a.b.c" has 3 components and the suffix "c" has 1 component. Subtracting the
                    // latter from the former yields the number of components in the prefix "a.b",
                    // i.e. 2.
                    size_t fullPathLength = _pathLengths[i];
                    size_t suffixPathLength = FieldRef{fieldNames[i]}.numParts();
                    invariant(suffixPathLength < fullPathLength);
                    arrComponents[i] = fullPathLength - suffixPathLength - 1;
                }
                continue;
            }

            // We're indexing an array element by its position. Traverse the remainder of the
            // field path now.
            //
            // Indexing an array element by its position selects a particular element of the
            // 'arrElt' array value when generating keys. It therefore cannot cause the index to be
            // multikey.
            subPositionalInfo[i].arrayObj = arrObj;
            subPositionalInfo[i].remainingPath = fieldNames[i];
            subPositionalInfo[i].dottedElt =
                arrObj.getFieldDottedOrArray(subPositionalInfo[i].remainingPath);
        }

        // Generate a key for each element of the indexed array.
        size_t nArrObjFields = 0;
        for (const auto arrObjElem : arrObj) {
            _getKeysArrEltFixed(&fieldNames,
                                &fixed,
                                arrObjElem,
                                keys,
                                numNotFound,
                                arrElt,
                                arrIdxs,
                                mayExpandArrayUnembedded,
                                subPositionalInfo,
                                multikeyPaths);
            ++nArrObjFields;
        }

        if (multikeyPaths && nArrObjFields > 1) {
            // The 'arrElt' array value contains multiple elements, so we say that it causes the
            // index to be multikey.
            for (size_t i = 0; i < arrComponents.size(); ++i) {
                if (auto arrComponent = arrComponents[i]) {
                    (*multikeyPaths)[i].insert(*arrComponent);
                }
            }
        }
    }
}
Exemplo n.º 14
0
int dmsFile::insert ( BSONObj &record, BSONObj &outRecord, dmsRecordID &rid )
{
   int rc                     = EDB_OK ;
   PAGEID pageID              = 0 ;
   char *page                 = NULL ;
   dmsPageHeader *pageHeader  = NULL ;
   int recordSize             = 0 ;
   SLOTOFF offsetTemp         = 0 ;
   const char *pGKeyFieldName = NULL ;
   dmsRecord recordHeader ;

   recordSize                 = record.objsize() ;
   // when we attempt to insert record, first we have to verify it include _id field
   if ( (unsigned int)recordSize > DMS_MAX_RECORD )
   {
      rc = EDB_INVALIDARG ;
      PD_LOG ( PDERROR, "record cannot bigger than 4MB" ) ;
      goto error ;
   }
   pGKeyFieldName = gKeyFieldName ;

   // make sure _id exists
   if ( record.getFieldDottedOrArray ( pGKeyFieldName ).eoo () )
   {
      rc = EDB_INVALIDARG ;
      PD_LOG ( PDERROR, "record must be with _id" ) ;
      goto error ;
   }

retry :
   // lock the database
   _mutex.get() ;
   // and then we should get the required record size
   pageID = _findPage ( recordSize + sizeof(dmsRecord) ) ;
   // if there's not enough space in any existing pages, let's release db lock
   if ( DMS_INVALID_PAGEID == pageID )
   {
      _mutex.release () ;
      // if there's not enough space in any existing pages, let's release db lock and
      // try to allocate a new segment by calling _extendSegment
      if ( _extendMutex.try_get() )
      {
         // calling _extendSegment
         rc = _extendSegment () ;
         if ( rc )
         {
            PD_LOG ( PDERROR, "Failed to extend segment, rc = %d", rc ) ;
            _extendMutex.release () ;
            goto error ;
         }
      }
      else
      {
         // if we cannot get the extendmutex, that means someone else is trying to extend
         // so let's wait until getting the mutex, and release it and try again
         _extendMutex.get() ;
      }
      _extendMutex.release () ;
      goto retry ;
   }
   // find the in-memory offset for the page
   page = pageToOffset ( pageID ) ;
   // if something wrong, let's return error
   if ( !page )
   {
      rc = EDB_SYS ;
      PD_LOG ( PDERROR, "Failed to find the page" ) ;
      goto error_releasemutex ;
   }
   // set page header
   pageHeader = (dmsPageHeader *)page ;
   if ( memcmp ( pageHeader->_eyeCatcher, DMS_PAGE_EYECATCHER,
                 DMS_PAGE_EYECATCHER_LEN ) != 0 )
   {
      rc = EDB_SYS ;
      PD_LOG ( PDERROR, "Invalid page header" ) ;
      goto error_releasemutex ;
   }
   // slot offset is the last byte of slots
   // free offset is the first byte of data
   // so freeOffset - slotOffset is the actual free space excluding holes
   if (
      // make sure there's still holes to recover
      ( pageHeader->_freeSpace >
        pageHeader->_freeOffset - pageHeader->_slotOffset ) &&
      // if there's no free space excluding holes
      ( pageHeader->_slotOffset + recordSize + sizeof(dmsRecord) + sizeof(SLOTID) >
        pageHeader->_freeOffset )
   )
   {
      // recover empty hole from page
      _recoverSpace ( page ) ;
   }
   if (
      // make sure there is enough free space
      ( pageHeader->_freeSpace < recordSize + sizeof(dmsRecord) + sizeof(SLOTID) ) ||
       ( pageHeader->_freeOffset - pageHeader->_slotOffset <
         recordSize + sizeof(dmsRecord) + sizeof(SLOTID) )
   )
   {
      PD_LOG ( PDERROR, "Something big wrong!!" ) ;
      rc = EDB_SYS ;
      goto error_releasemutex ;
   }
   offsetTemp = pageHeader->_freeOffset - recordSize - sizeof(dmsRecord) ;
   recordHeader._size = recordSize + sizeof( dmsRecord ) ;
   recordHeader._flag = DMS_RECORD_FLAG_NORMAL ;
   // copy the slot
   *(SLOTOFF*)( page + sizeof( dmsPageHeader ) +
                pageHeader->_numSlots * sizeof(SLOTOFF) ) = offsetTemp ;
   // copy the record header
   memcpy ( page + offsetTemp, ( char* )&recordHeader, sizeof(dmsRecord) ) ;
   // copy the record body
   memcpy ( page + offsetTemp + sizeof(dmsRecord),
            record.objdata(),
            recordSize ) ;
   outRecord = BSONObj ( page + offsetTemp + sizeof(dmsRecord) ) ;
   rid._pageID = pageID ;
   rid._slotID = pageHeader->_numSlots ;
   // modify metadata in page
   pageHeader->_numSlots ++ ;
   pageHeader->_slotOffset += sizeof(SLOTID) ;
   pageHeader->_freeOffset = offsetTemp ;
   // modify database metadata
   _updateFreeSpace ( pageHeader,
                      -(recordSize+sizeof(SLOTID)+sizeof(dmsRecord)),
                      pageID ) ;
   // release lock for database
   _mutex.release () ;
done :
   return rc ;
error_releasemutex :
   _mutex.release() ;
error :
   goto done ;
}