// static void IndexBoundsBuilder::translateRegex(const RegexMatchExpression* rme, OrderedIntervalList* oilOut, bool* exact) { const string start = simpleRegex(rme->getString().c_str(), rme->getFlags().c_str(), exact); // QLOG() << "regex bounds start is " << start << endl; // Note that 'exact' is set by simpleRegex above. if (!start.empty()) { string end = start; end[end.size() - 1]++; oilOut->intervals.push_back(makeRangeInterval(start, end, true, false)); } else { BSONObjBuilder bob; bob.appendMinForType("", String); bob.appendMaxForType("", String); BSONObj dataObj = bob.obj(); verify(dataObj.isOwned()); oilOut->intervals.push_back(makeRangeInterval(dataObj, true, false)); } // Regexes are after strings. BSONObjBuilder bob; bob.appendRegex("", rme->getString(), rme->getFlags()); oilOut->intervals.push_back(makePointInterval(bob.obj())); }
TEST(KeyStringTest, AllTypesRoundtrip) { for (int i = 1; i <= JSTypeMax; i++) { { BSONObjBuilder b; b.appendMinForType("", i); BSONObj o = b.obj(); ROUNDTRIP(o); } { BSONObjBuilder b; b.appendMaxForType("", i); BSONObj o = b.obj(); ROUNDTRIP(o); } } }
TEST(KeyStringTest, AllTypesRoundtrip) { for (int i = 1; i <= JSTypeMax; i++) { // TODO: Currently KeyString does not support NumberDecimal // SERVER-19703 if (i == NumberDecimal) continue; { BSONObjBuilder b; b.appendMinForType("", i); BSONObj o = b.obj(); ROUNDTRIP(o); } { BSONObjBuilder b; b.appendMaxForType("", i); BSONObj o = b.obj(); ROUNDTRIP(o); } } }
// static void IndexBoundsBuilder::translate(const MatchExpression* expr, const BSONElement& elt, OrderedIntervalList* oilOut, bool* exactOut) { int direction = (elt.numberInt() >= 0) ? 1 : -1; Interval interval; bool exact = false; oilOut->name = elt.fieldName(); bool isHashed = false; if (mongoutils::str::equals("hashed", elt.valuestrsafe())) { isHashed = true; } if (isHashed) { verify(MatchExpression::EQ == expr->matchType() || MatchExpression::MATCH_IN == expr->matchType()); } if (MatchExpression::EQ == expr->matchType()) { const EqualityMatchExpression* node = static_cast<const EqualityMatchExpression*>(expr); // We have to copy the data out of the parse tree and stuff it into the index // bounds. BSONValue will be useful here. BSONObj dataObj; if (isHashed) { dataObj = ExpressionMapping::hash(node->getData()); } else { dataObj = objFromElement(node->getData()); } // UNITTEST 11738048 if (Array == dataObj.firstElement().type()) { // XXX: build better bounds warning() << "building lazy bounds for " << expr->toString() << endl; interval = allValues(); exact = false; } else { verify(dataObj.isOwned()); interval = makePointInterval(dataObj); // XXX: it's exact if the index isn't sparse if (dataObj.firstElement().isNull()) { exact = false; } else if (isHashed) { exact = false; } else { exact = true; } } } else if (MatchExpression::LTE == expr->matchType()) { const LTEMatchExpression* node = static_cast<const LTEMatchExpression*>(expr); BSONElement dataElt = node->getData(); BSONObjBuilder bob; bob.appendMinForType("", dataElt.type()); bob.append(dataElt); BSONObj dataObj = bob.obj(); verify(dataObj.isOwned()); interval = makeRangeInterval(dataObj, true, true); // XXX: only exact if not (null or array) exact = true; } else if (MatchExpression::LT == expr->matchType()) { const LTMatchExpression* node = static_cast<const LTMatchExpression*>(expr); BSONElement dataElt = node->getData(); BSONObjBuilder bob; bob.appendMinForType("", dataElt.type()); bob.append(dataElt); BSONObj dataObj = bob.obj(); verify(dataObj.isOwned()); interval = makeRangeInterval(dataObj, true, false); // XXX: only exact if not (null or array) exact = true; } else if (MatchExpression::GT == expr->matchType()) { const GTMatchExpression* node = static_cast<const GTMatchExpression*>(expr); BSONElement dataElt = node->getData(); BSONObjBuilder bob; bob.append(node->getData()); bob.appendMaxForType("", dataElt.type()); BSONObj dataObj = bob.obj(); verify(dataObj.isOwned()); interval = makeRangeInterval(dataObj, false, true); // XXX: only exact if not (null or array) exact = true; } else if (MatchExpression::GTE == expr->matchType()) { const GTEMatchExpression* node = static_cast<const GTEMatchExpression*>(expr); BSONElement dataElt = node->getData(); BSONObjBuilder bob; bob.append(dataElt); bob.appendMaxForType("", dataElt.type()); BSONObj dataObj = bob.obj(); verify(dataObj.isOwned()); interval = makeRangeInterval(dataObj, true, true); // XXX: only exact if not (null or array) exact = true; } else if (MatchExpression::REGEX == expr->matchType()) { warning() << "building lazy bounds for " << expr->toString() << endl; interval = allValues(); exact = false; } else if (MatchExpression::MOD == expr->matchType()) { BSONObjBuilder bob; bob.appendMinForType("", NumberDouble); bob.appendMaxForType("", NumberDouble); BSONObj dataObj = bob.obj(); verify(dataObj.isOwned()); interval = makeRangeInterval(dataObj, true, true); exact = false; } else if (MatchExpression::MATCH_IN == expr->matchType()) { warning() << "building lazy bounds for " << expr->toString() << endl; interval = allValues(); exact = false; } else if (MatchExpression::TYPE_OPERATOR == expr->matchType()) { const TypeMatchExpression* tme = static_cast<const TypeMatchExpression*>(expr); BSONObjBuilder bob; bob.appendMinForType("", tme->getData()); bob.appendMaxForType("", tme->getData()); BSONObj dataObj = bob.obj(); verify(dataObj.isOwned()); interval = makeRangeInterval(dataObj, true, true); exact = false; } else if (MatchExpression::MATCH_IN == expr->matchType()) { warning() << "building lazy bounds for " << expr->toString() << endl; interval = allValues(); exact = false; } else if (MatchExpression::GEO == expr->matchType()) { const GeoMatchExpression* gme = static_cast<const GeoMatchExpression*>(expr); // Can only do this for 2dsphere. if (!mongoutils::str::equals("2dsphere", elt.valuestrsafe())) { warning() << "Planner error trying to build geo bounds for " << elt.toString() << " index element."; verify(0); } const S2Region& region = gme->getGeoQuery().getRegion(); ExpressionMapping::cover2dsphere(region, oilOut); *exactOut = false; // XXX: restructure this method return; } else { warning() << "Planner error, trying to build bounds for expr " << expr->toString() << endl; verify(0); } if (-1 == direction) { reverseInterval(&interval); } oilOut->intervals.push_back(interval); *exactOut = exact; }
// static void IndexBoundsBuilder::translate(const MatchExpression* expr, const BSONElement& elt, OrderedIntervalList* oilOut, bool* exactOut) { oilOut->name = elt.fieldName(); bool isHashed = false; if (mongoutils::str::equals("hashed", elt.valuestrsafe())) { isHashed = true; } if (isHashed) { verify(MatchExpression::EQ == expr->matchType() || MatchExpression::MATCH_IN == expr->matchType()); } if (MatchExpression::ELEM_MATCH_VALUE == expr->matchType()) { OrderedIntervalList acc; bool exact; translate(expr->getChild(0), elt, &acc, &exact); if (!exact) { *exactOut = false; } for (size_t i = 1; i < expr->numChildren(); ++i) { OrderedIntervalList next; translate(expr->getChild(i), elt, &next, &exact); if (!exact) { *exactOut = false; } intersectize(next, &acc); } for (size_t i = 0; i < acc.intervals.size(); ++i) { oilOut->intervals.push_back(acc.intervals[i]); } if (!oilOut->intervals.empty()) { std::sort(oilOut->intervals.begin(), oilOut->intervals.end(), IntervalComparison); } } else if (MatchExpression::EQ == expr->matchType()) { const EqualityMatchExpression* node = static_cast<const EqualityMatchExpression*>(expr); translateEquality(node->getData(), isHashed, oilOut, exactOut); } else if (MatchExpression::LTE == expr->matchType()) { const LTEMatchExpression* node = static_cast<const LTEMatchExpression*>(expr); BSONElement dataElt = node->getData(); // Everything is <= MaxKey. if (MaxKey == dataElt.type()) { oilOut->intervals.push_back(allValues()); *exactOut = true; return; } BSONObjBuilder bob; bob.appendMinForType("", dataElt.type()); bob.appendAs(dataElt, ""); BSONObj dataObj = bob.obj(); verify(dataObj.isOwned()); oilOut->intervals.push_back(makeRangeInterval(dataObj, true, true)); // XXX: only exact if not (null or array) *exactOut = true; } else if (MatchExpression::LT == expr->matchType()) { const LTMatchExpression* node = static_cast<const LTMatchExpression*>(expr); BSONElement dataElt = node->getData(); // Everything is <= MaxKey. if (MaxKey == dataElt.type()) { oilOut->intervals.push_back(allValues()); *exactOut = true; return; } BSONObjBuilder bob; bob.appendMinForType("", dataElt.type()); bob.appendAs(dataElt, ""); BSONObj dataObj = bob.obj(); verify(dataObj.isOwned()); QLOG() << "data obj is " << dataObj.toString() << endl; oilOut->intervals.push_back(makeRangeInterval(dataObj, true, false)); // XXX: only exact if not (null or array) *exactOut = true; } else if (MatchExpression::GT == expr->matchType()) { const GTMatchExpression* node = static_cast<const GTMatchExpression*>(expr); BSONElement dataElt = node->getData(); // Everything is > MinKey. if (MinKey == dataElt.type()) { oilOut->intervals.push_back(allValues()); *exactOut = true; return; } BSONObjBuilder bob; bob.appendAs(node->getData(), ""); bob.appendMaxForType("", dataElt.type()); BSONObj dataObj = bob.obj(); verify(dataObj.isOwned()); oilOut->intervals.push_back(makeRangeInterval(dataObj, false, true)); // XXX: only exact if not (null or array) *exactOut = true; } else if (MatchExpression::GTE == expr->matchType()) { const GTEMatchExpression* node = static_cast<const GTEMatchExpression*>(expr); BSONElement dataElt = node->getData(); // Everything is >= MinKey. if (MinKey == dataElt.type()) { oilOut->intervals.push_back(allValues()); *exactOut = true; return; } BSONObjBuilder bob; bob.appendAs(dataElt, ""); bob.appendMaxForType("", dataElt.type()); BSONObj dataObj = bob.obj(); verify(dataObj.isOwned()); oilOut->intervals.push_back(makeRangeInterval(dataObj, true, true)); // XXX: only exact if not (null or array) *exactOut = true; } else if (MatchExpression::REGEX == expr->matchType()) { const RegexMatchExpression* rme = static_cast<const RegexMatchExpression*>(expr); translateRegex(rme, oilOut, exactOut); } else if (MatchExpression::MOD == expr->matchType()) { BSONObjBuilder bob; bob.appendMinForType("", NumberDouble); bob.appendMaxForType("", NumberDouble); BSONObj dataObj = bob.obj(); verify(dataObj.isOwned()); oilOut->intervals.push_back(makeRangeInterval(dataObj, true, true)); *exactOut = false; } else if (MatchExpression::TYPE_OPERATOR == expr->matchType()) { const TypeMatchExpression* tme = static_cast<const TypeMatchExpression*>(expr); BSONObjBuilder bob; bob.appendMinForType("", tme->getData()); bob.appendMaxForType("", tme->getData()); BSONObj dataObj = bob.obj(); verify(dataObj.isOwned()); oilOut->intervals.push_back(makeRangeInterval(dataObj, true, true)); *exactOut = false; } else if (MatchExpression::MATCH_IN == expr->matchType()) { const InMatchExpression* ime = static_cast<const InMatchExpression*>(expr); const ArrayFilterEntries& afr = ime->getData(); *exactOut = true; // Create our various intervals. bool thisBoundExact = false; for (BSONElementSet::iterator it = afr.equalities().begin(); it != afr.equalities().end(); ++it) { translateEquality(*it, isHashed, oilOut, &thisBoundExact); if (!thisBoundExact) { *exactOut = false; } } for (size_t i = 0; i < afr.numRegexes(); ++i) { translateRegex(afr.regex(i), oilOut, &thisBoundExact); if (!thisBoundExact) { *exactOut = false; } } // XXX: what happens here? if (afr.hasNull()) { } // XXX: what happens here as well? if (afr.hasEmptyArray()) { } unionize(oilOut); } else if (MatchExpression::GEO == expr->matchType()) { const GeoMatchExpression* gme = static_cast<const GeoMatchExpression*>(expr); // Can only do this for 2dsphere. if (!mongoutils::str::equals("2dsphere", elt.valuestrsafe())) { warning() << "Planner error trying to build geo bounds for " << elt.toString() << " index element."; verify(0); } const S2Region& region = gme->getGeoQuery().getRegion(); ExpressionMapping::cover2dsphere(region, oilOut); *exactOut = false; } else { warning() << "Planner error, trying to build bounds for expr " << expr->toString() << endl; verify(0); } }