// Entry point for a search. virtual shared_ptr<Cursor> newCursor(const BSONObj& query, const BSONObj& order, int numWanted) const { vector<QueryGeometry> regions; double maxDistance = DBL_MAX; bool isNear = false; bool isIntersect = false; // Go through the fields that we index, and for each geo one, make a QueryGeometry // object for the S2Cursor class to do intersection testing/cover generating with. for (size_t i = 0; i < _fields.size(); ++i) { const IndexedField &field = _fields[i]; if (IndexedField::GEO != field.type) { continue; } BSONElement e = query.getFieldDotted(field.name); if (e.eoo()) { continue; } if (!e.isABSONObj()) { continue; } BSONObj obj = e.Obj(); QueryGeometry geoQueryField(field.name); if (parseLegacy(obj, &geoQueryField, &isNear, &isIntersect, &maxDistance)) { regions.push_back(geoQueryField); } else if (parseQuery(obj, &geoQueryField, &isNear, &isIntersect, &maxDistance)) { regions.push_back(geoQueryField); } else { uasserted(16535, "can't parse query for *2d geo search: " + obj.toString()); } } if (isNear && isIntersect ) { uasserted(16474, "Can't do both near and intersect, query: " + query.toString()); } // I copied this from 2d.cpp. Guard against perversion. if (numWanted < 0) numWanted *= -1; if (0 == numWanted) numWanted = INT_MAX; BSONObjBuilder geoFieldsToNuke; for (size_t i = 0; i < _fields.size(); ++i) { const IndexedField &field = _fields[i]; if (IndexedField::GEO != field.type) { continue; } geoFieldsToNuke.append(field.name, ""); } // false means we want to filter OUT geoFieldsToNuke, not filter to include only that. BSONObj filteredQuery = query.filterFieldsUndotted(geoFieldsToNuke.obj(), false); if (isNear) { S2NearCursor *cursor = new S2NearCursor(keyPattern(), getDetails(), filteredQuery, regions, _params, numWanted, maxDistance); return shared_ptr<Cursor>(cursor); } else { // Default to intersect. S2Cursor *cursor = new S2Cursor(keyPattern(), getDetails(), filteredQuery, regions, _params, numWanted); return shared_ptr<Cursor>(cursor); } }
Status S2IndexCursor::seek(const BSONObj &position) { vector<GeoQuery> regions; bool isNearQuery = false; NearQuery nearQuery; // Go through the fields that we index, and for each geo one, make // a GeoQuery object for the S2*Cursor class to do intersection // testing/cover generating with. BSONObjIterator keyIt(_descriptor->keyPattern()); while (keyIt.more()) { BSONElement keyElt = keyIt.next(); if (keyElt.type() != String || IndexNames::GEO_2DSPHERE != keyElt.valuestr()) { continue; } BSONElement e = position.getFieldDotted(keyElt.fieldName()); if (e.eoo()) { continue; } if (!e.isABSONObj()) { continue; } BSONObj obj = e.Obj(); if (nearQuery.parseFrom(obj, _params.radius)) { if (isNearQuery) { return Status(ErrorCodes::BadValue, "Only one $near clause allowed: " + position.toString(), 16685); } isNearQuery = true; nearQuery.field = keyElt.fieldName(); continue; } GeoQuery geoQueryField(keyElt.fieldName()); if (!geoQueryField.parseFrom(obj)) { return Status(ErrorCodes::BadValue, "can't parse query (2dsphere): " + obj.toString(), 16535); } if (!geoQueryField.hasS2Region()) { return Status(ErrorCodes::BadValue, "Geometry unsupported: " + obj.toString(), 16684); } regions.push_back(geoQueryField); } // Remove all the indexed geo regions from the query. The s2*cursor will // instead create a covering for that key to speed up the search. // // One thing to note is that we create coverings for indexed geo keys during // a near search to speed it up further. BSONObjBuilder geoFieldsToNuke; if (isNearQuery) { geoFieldsToNuke.append(nearQuery.field, ""); } for (size_t i = 0; i < regions.size(); ++i) { geoFieldsToNuke.append(regions[i].getField(), ""); } // false means we want to filter OUT geoFieldsToNuke, not filter to include only that. BSONObj filteredQuery = position.filterFieldsUndotted(geoFieldsToNuke.obj(), false); if (isNearQuery) { S2NearIndexCursor* nearCursor = new S2NearIndexCursor(_descriptor, _params); _underlyingCursor.reset(nearCursor); nearCursor->seek(filteredQuery, nearQuery, regions); } else { S2SimpleCursor* simpleCursor = new S2SimpleCursor(_descriptor, _params); _underlyingCursor.reset(simpleCursor); simpleCursor->seek(filteredQuery, regions); } return Status::OK(); }