// update_index_data.cpp
/**
* Copyright (C) 2013 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 .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "mongo/bson/util/builder.h"
#include "mongo/db/field_ref.h"
#include "mongo/db/update_index_data.h"
namespace mongo {
UpdateIndexData::UpdateIndexData() : _allPathsIndexed( false ) { }
void UpdateIndexData::addPath( const StringData& path ) {
string s;
if ( getCanonicalIndexField( path, &s ) ) {
_canonicalPaths.insert( s );
}
else {
_canonicalPaths.insert( path.toString() );
}
}
void UpdateIndexData::addPathComponent( const StringData& pathComponent ) {
_pathComponents.insert( pathComponent.toString() );
}
void UpdateIndexData::allPathsIndexed() {
_allPathsIndexed = true;
}
void UpdateIndexData::clear() {
_canonicalPaths.clear();
_pathComponents.clear();
_allPathsIndexed = false;
}
bool UpdateIndexData::mightBeIndexed( const StringData& path ) const {
if ( _allPathsIndexed ) {
return true;
}
StringData use = path;
string x;
if ( getCanonicalIndexField( path, &x ) )
use = StringData( x );
for ( std::set::const_iterator i = _canonicalPaths.begin();
i != _canonicalPaths.end();
++i ) {
StringData idx( *i );
if ( _startsWith( use, idx ) )
return true;
if ( _startsWith( idx, use ) )
return true;
}
FieldRef pathFieldRef( path );
for ( std::set::const_iterator i = _pathComponents.begin();
i != _pathComponents.end();
++i ) {
const string& pathComponent = *i;
for ( size_t partIdx = 0; partIdx < pathFieldRef.numParts(); ++partIdx ) {
if ( pathComponent == pathFieldRef.getPart( partIdx ) ) {
return true;
}
}
}
return false;
}
bool UpdateIndexData::_startsWith( const StringData& a, const StringData& b ) const {
if ( !a.startsWith( b ) )
return false;
// make sure there is a dot or EOL right after
if ( a.size() == b.size() )
return true;
return a[b.size()] == '.';
}
bool getCanonicalIndexField( const StringData& fullName, string* out ) {
// check if fieldName contains ".$" or ".###" substrings (#=digit) and skip them
// however do not skip the first field even if it meets these criteria
if ( fullName.find( '.' ) == string::npos )
return false;
bool modified = false;
StringBuilder buf;
for ( size_t i=0; i= fullName.size() || fullName[i+2] == '.' ) {
i++;
modified = true;
continue;
}
}
// check for ".###" for any number of digits (no letters)
if ( isdigit( fullName[i+1] ) ) {
size_t j = i;
// skip digits
while ( j+1 < fullName.size() && isdigit( fullName[j+1] ) )
j++;
if ( j+1 == fullName.size() || fullName[j+1] == '.' ) {
// only digits found, skip forward
i = j;
modified = true;
continue;
}
}
buf << c;
}
if ( !modified )
return false;
*out = buf.str();
return true;
}
}