// DEPRECATED /** * 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/index/s2_simple_cursor.h" #include "mongo/db/btreecursor.h" #include "mongo/db/index/index_cursor.h" #include "mongo/db/index/index_descriptor.h" #include "mongo/db/pdfile.h" #include "mongo/db/queryutil.h" #include "third_party/s2/s2regionintersection.h" namespace mongo { S2SimpleCursor::S2SimpleCursor(IndexDescriptor* descriptor, const S2IndexingParams& params) : _descriptor(descriptor), _params(params) { } void S2SimpleCursor::seek(const BSONObj& query, const vector& regions) { _nscanned = 0; _matchTested = 0; _geoTested = 0; _fields = regions; _seen = unordered_set(); BSONObjBuilder geoFieldsToNuke; for (size_t i = 0; i < _fields.size(); ++i) { geoFieldsToNuke.append(_fields[i].getField(), ""); } // false means we want to filter OUT geoFieldsToNuke, not filter to include only that. _filteredQuery = query.filterFieldsUndotted(geoFieldsToNuke.obj(), false); BSONObjBuilder specBuilder; BSONObjIterator i(_descriptor->keyPattern()); while (i.more()) { BSONElement e = i.next(); // Checked in AccessMethod already, so we know this spec has only numbers and 2dsphere if ( e.type() == String ) { specBuilder.append( e.fieldName(), 1 ); } else { specBuilder.append( e.fieldName(), e.numberInt() ); } } BSONObj spec = specBuilder.obj(); BSONObj frsObj; BSONObjBuilder frsObjBuilder; frsObjBuilder.appendElements(_filteredQuery); S2RegionCoverer coverer; for (size_t i = 0; i < _fields.size(); ++i) { vector cover; double area = _fields[i].getRegion().GetRectBound().Area(); S2SearchUtil::setCoverLimitsBasedOnArea(area, &coverer, _params.coarsestIndexedLevel); coverer.GetCovering(_fields[i].getRegion(), &cover); uassert(16759, "No cover ARGH?!", cover.size() > 0); _cellsInCover = cover.size(); BSONObj fieldRange = S2SearchUtil::coverAsBSON(cover, _fields[i].getField(), _params.coarsestIndexedLevel); frsObjBuilder.appendElements(fieldRange); } frsObj = frsObjBuilder.obj(); FieldRangeSet frs(_descriptor->parentNS().c_str(), frsObj, false, false); shared_ptr frv(new FieldRangeVector(frs, spec, 1)); _btreeCursor.reset(BtreeCursor::make(nsdetails(_descriptor->parentNS()), _descriptor->getOnDisk(), frv, 0, 1)); next(); } Status S2SimpleCursor::seek(const BSONObj& position) { return Status::OK(); } Status S2SimpleCursor::setOptions(const CursorOptions& options) { return Status::OK(); } bool S2SimpleCursor::isEOF() const { return !_btreeCursor->ok(); } BSONObj S2SimpleCursor::getKey() const { return _btreeCursor->currKey(); } DiskLoc S2SimpleCursor::getValue() const { return _btreeCursor->currLoc(); } void S2SimpleCursor::next() { for (; _btreeCursor->ok(); _btreeCursor->advance()) { ++_nscanned; if (_seen.end() != _seen.find(_btreeCursor->currLoc())) { continue; } _seen.insert(_btreeCursor->currLoc()); const BSONObj &indexedObj = _btreeCursor->currLoc().obj(); ++_geoTested; size_t geoFieldsMatched = 0; // OK, cool, non-geo match satisfied. See if the object actually overlaps w/the geo // query fields. for (size_t i = 0; i < _fields.size(); ++i) { BSONElementSet geoFieldElements; indexedObj.getFieldsDotted(_fields[i].getField(), geoFieldElements, false); if (geoFieldElements.empty()) { continue; } bool match = false; for (BSONElementSet::iterator oi = geoFieldElements.begin(); !match && (oi != geoFieldElements.end()); ++oi) { if (!oi->isABSONObj()) { continue; } const BSONObj &geoObj = oi->Obj(); GeometryContainer geoContainer; uassert(16760, "malformed geometry: " + geoObj.toString(), geoContainer.parseFrom(geoObj)); match = _fields[i].satisfiesPredicate(geoContainer); } if (match) { ++geoFieldsMatched; } } if (geoFieldsMatched == _fields.size()) { // We have a winner! And we point at it. return; } } } string S2SimpleCursor::toString() { return "S2Cursor: " + _btreeCursor->toString(); } Status S2SimpleCursor::savePosition() { _btreeCursor->noteLocation(); _seen.clear(); return Status::OK(); } Status S2SimpleCursor::restorePosition() { _btreeCursor->checkLocation(); // We are pointing at a valid btree location now, but it may not be a valid result. // This ensures that we're pointing at a valid result that satisfies the query. // There is something subtle here: Say we point at something valid, and note the location // (yield), then checkLocation (unyield), when we call advance, we don't go past the object // that we were/are pointing at since we only do that if we've seen it before (that is, it's // in _seen, which we clear when we yield). next(); return Status::OK(); } } // namespace mongo