// index.cpp
/**
* Copyright (C) 2008 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 .
*/
#include "../stdafx.h"
#include "namespace.h"
#include "index.h"
#include "btree.h"
#include "query.h"
namespace mongo {
/* delete this index. does NOT clean up the system catalog
(system.indexes or system.namespaces) -- only NamespaceIndex.
*/
void IndexDetails::kill_idx() {
string ns = indexNamespace(); // e.g. foo.coll.$ts_1
// clean up parent namespace index cache
NamespaceDetailsTransient::get_w( parentNS().c_str() ).deletedIndex();
BSONObjBuilder b;
b.append("name", indexName().c_str());
b.append("ns", parentNS().c_str());
BSONObj cond = b.done(); // e.g.: { name: "ts_1", ns: "foo.coll" }
/* important to catch exception here so we can finish cleanup below. */
try {
btreeStore->drop(ns.c_str());
}
catch(DBException& ) {
log(2) << "IndexDetails::kill(): couldn't drop ns " << ns << endl;
}
head.setInvalid();
info.setInvalid();
// clean up in system.indexes. we do this last on purpose. note we have
// to make the cond object before the drop() above though.
string system_indexes = cc().database()->name + ".system.indexes";
int n = deleteObjects(system_indexes.c_str(), cond, false, false, true);
wassert( n == 1 );
}
void getKeys( vector< const char * > fieldNames, vector< BSONElement > fixed, const BSONObj &obj, BSONObjSetDefaultOrder &keys ) {
BSONObjBuilder b;
b.appendNull( "" );
BSONElement nullElt = b.done().firstElement();
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(); allFound && i != fieldNames.end(); ++i )
if ( **i != '\0' )
allFound = false;
if ( allFound ) {
if ( arrElt.eoo() ) {
// no terminal array element to expand
BSONObjBuilder b;
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;
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 ){
// x : [] - need to insert undefined
BSONObjBuilder b;
for( unsigned j = 0; j < fixed.size(); ++j ) {
if ( j == arrIdx )
b.appendUndefined( "" );
else
b.appendAs( fixed[ j ], "" );
}
keys.insert( b.obj() );
}
}
} else {
// nonterminal array element to expand, so recurse
assert( !arrElt.eoo() );
BSONObjIterator i( arrElt.embeddedObject() );
while( i.more() ) {
BSONElement e = i.next();
if ( e.type() == Object )
getKeys( fieldNames, fixed, e.embeddedObject(), keys );
}
}
}
void getKeysFromObject( const IndexSpec &spec, const BSONObj &obj, BSONObjSetDefaultOrder &keys ) {
BSONObjIterator i( spec.keys );
vector< const char * > fieldNames;
vector< BSONElement > fixed;
BSONObjBuilder nullKey;
while( i.more() ) {
fieldNames.push_back( i.next().fieldName() );
fixed.push_back( BSONElement() );
nullKey.appendNull( "" );
}
getKeys( fieldNames, fixed, obj, keys );
if ( keys.empty() )
keys.insert( nullKey.obj() );
}
/* Pull out the relevant key objects from obj, so we
can index them. Note that the set is multiple elements
only when it's a "multikey" array.
Keys will be left empty if key not found in the object.
*/
void IndexDetails::getKeysFromObject( const BSONObj& obj, BSONObjSetDefaultOrder& keys) const {
mongo::getKeysFromObject( IndexSpec( info ) , obj, keys );
}
void setDifference(BSONObjSetDefaultOrder &l, BSONObjSetDefaultOrder &r, vector &diff) {
BSONObjSetDefaultOrder::iterator i = l.begin();
BSONObjSetDefaultOrder::iterator j = r.begin();
while ( 1 ) {
if ( i == l.end() )
break;
while ( j != r.end() && j->woCompare( *i ) < 0 )
j++;
if ( j == r.end() || i->woCompare(*j) != 0 ) {
const BSONObj *jo = &*i;
diff.push_back( (BSONObj *) jo );
}
i++;
}
}
void getIndexChanges(vector& v, NamespaceDetails& d, BSONObj newObj, BSONObj oldObj) {
v.resize(d.nIndexes);
NamespaceDetails::IndexIterator i = d.ii();
while( i.more() ) {
int j = i.pos();
IndexDetails& idx = i.next();
BSONObj idxKey = idx.info.obj().getObjectField("key"); // eg { ts : 1 }
IndexChanges& ch = v[j];
idx.getKeysFromObject(oldObj, ch.oldkeys);
idx.getKeysFromObject(newObj, ch.newkeys);
if( ch.newkeys.size() > 1 )
d.setIndexIsMultikey(j);
setDifference(ch.oldkeys, ch.newkeys, ch.removed);
setDifference(ch.newkeys, ch.oldkeys, ch.added);
}
}
void dupCheck(vector& v, NamespaceDetails& d) {
NamespaceDetails::IndexIterator i = d.ii();
while( i.more() ) {
int j = i.pos();
v[j].dupCheck(i.next());
}
}
}