/**
* 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/db/query/index_bounds_builder.h"
namespace mongo {
// static
OrderedIntervalList IndexBoundsBuilder::allValuesForField(const BSONElement& elt) {
// ARGH, BSONValue would make this shorter.
BSONObjBuilder bob;
if (-1 == elt.number()) {
// Index should go from MaxKey to MinKey as it's descending.
bob.appendMaxKey("");
bob.appendMinKey("");
}
else {
// Index goes from MinKey to MaxKey as it's ascending.
bob.appendMinKey("");
bob.appendMaxKey("");
}
OrderedIntervalList oil(elt.fieldName());
oil.intervals.push_back(makeRangeInterval(bob.obj(), true, true));
return oil;
}
// static
void IndexBoundsBuilder::translate(const LeafMatchExpression* expr, const BSONElement& idxElt,
OrderedIntervalList* oilOut, bool* exactOut) {
Interval interval;
bool exact = false;
if (MatchExpression::EQ == expr->matchType()) {
const EqualityMatchExpression* node = static_cast(expr);
// We have to copy the data out of the parse tree and stuff it into the index bounds.
// BSONValue will be useful here.
// XXX: This is wrong when idxElt is an array.
// XXX: equality with arrays is weird, see queryutil.cpp:203
BSONObj dataObj = objFromElement(node->getData());
verify(dataObj.isOwned());
interval = makePointInterval(dataObj);
// TODO ALBERTO
// XXX: This is false when idxElt is an array.
exact = true;
}
else if (MatchExpression::LTE == expr->matchType()) {
const LTEMatchExpression* node = static_cast(expr);
BSONObjBuilder bob;
bob.appendMinKey("");
bob.append(node->getData());
BSONObj dataObj = bob.obj();
verify(dataObj.isOwned());
interval = makeRangeInterval(dataObj, true, true);
exact = true;
}
else if (MatchExpression::LT == expr->matchType()) {
const LTMatchExpression* node = static_cast(expr);
BSONObjBuilder bob;
bob.appendMinKey("");
bob.append(node->getData());
BSONObj dataObj = bob.obj();
verify(dataObj.isOwned());
interval = makeRangeInterval(dataObj, true, false);
exact = true;
}
else if (MatchExpression::GT == expr->matchType()) {
const GTMatchExpression* node = static_cast(expr);
BSONObjBuilder bob;
bob.append(node->getData());
bob.appendMaxKey("");
BSONObj dataObj = bob.obj();
verify(dataObj.isOwned());
interval = makeRangeInterval(dataObj, false, true);
exact = true;
}
else if (MatchExpression::GTE == expr->matchType()) {
const GTEMatchExpression* node = static_cast(expr);
BSONObjBuilder bob;
bob.append(node->getData());
bob.appendMaxKey("");
BSONObj dataObj = bob.obj();
verify(dataObj.isOwned());
interval = makeRangeInterval(dataObj, true, true);
exact = true;
}
else {
verify(0);
}
if (-1 == idxElt.number()) {
reverseInterval(&interval);
}
oilOut->intervals.push_back(interval);
*exactOut = exact;
}
// static
Interval IndexBoundsBuilder::makeRangeInterval(const BSONObj& obj, bool startInclusive,
bool endInclusive) {
Interval ret;
ret._intervalData = obj;
ret.startInclusive = startInclusive;
ret.endInclusive = endInclusive;
BSONObjIterator it(obj);
verify(it.more());
ret.start = it.next();
verify(it.more());
ret.end = it.next();
return ret;
}
// static
Interval IndexBoundsBuilder::makePointInterval(const BSONObj& obj) {
Interval ret;
ret._intervalData = obj;
ret.startInclusive = ret.endInclusive = true;
ret.start = ret.end = obj.firstElement();
return ret;
}
// static
BSONObj IndexBoundsBuilder::objFromElement(const BSONElement& elt) {
BSONObjBuilder bob;
bob.append(elt);
return bob.obj();
}
// static
void IndexBoundsBuilder::reverseInterval(Interval* ival) {
BSONElement tmp = ival->start;
ival->start = ival->end;
ival->end = tmp;
bool tmpInc = ival->startInclusive;
ival->startInclusive = ival->endInclusive;
ival->endInclusive = tmpInc;
}
} // namespace mongo