S2NearCursor::S2NearCursor(const BSONObj &keyPattern, const IndexDetails *details, const BSONObj &query, const vector<GeoQueryField> &fields, const S2IndexingParams ¶ms, int numWanted, double maxDistance) : _details(details), _fields(fields), _params(params), _keyPattern(keyPattern), _numToReturn(numWanted), _maxDistance(maxDistance) { BSONObjBuilder geoFieldsToNuke; for (size_t i = 0; i < _fields.size(); ++i) { geoFieldsToNuke.append(_fields[i].field, ""); } // false means we want to filter OUT geoFieldsToNuke, not filter to include only that. _filteredQuery = query.filterFieldsUndotted(geoFieldsToNuke.obj(), false); _matcher.reset(new CoveredIndexMatcher(_filteredQuery, keyPattern)); // More indexing machinery. BSONObjBuilder specBuilder; BSONObjIterator specIt(_keyPattern); while (specIt.more()) { BSONElement e = specIt.next(); specBuilder.append(e.fieldName(), 1); } BSONObj spec = specBuilder.obj(); _specForFRV = IndexSpec(spec); // Start with a conservative _radiusIncrement. _radiusIncrement = S2::kAvgEdge.GetValue(_params.finestIndexedLevel) * _params.radius; _innerRadius = _outerRadius = 0; // Set up _outerRadius with proper checks (maybe maxDistance is really small?) nextAnnulus(); }
void S2NearStage::init() { _initted = true; // The field we're near-ing from is the n-th field. Figure out what that 'n' is. We // put the cover for the search annulus in this spot in the bounds. _nearFieldIndex = 0; BSONObjIterator specIt(_params.indexKeyPattern); while (specIt.more()) { if (specIt.next().fieldName() == _params.nearQuery.field) { break; } ++_nearFieldIndex; } verify(_nearFieldIndex < _params.indexKeyPattern.nFields()); // FLAT implies the input distances are in radians. Convert to meters. if (FLAT == _params.nearQuery.centroid.crs) { _params.nearQuery.minDistance *= kRadiusOfEarthInMeters; _params.nearQuery.maxDistance *= kRadiusOfEarthInMeters; } // Make sure distances are sane. Possibly redundant given the checking during parsing. _minDistance = max(0.0, _params.nearQuery.minDistance); _maxDistance = min(M_PI * kRadiusOfEarthInMeters, _params.nearQuery.maxDistance); _minDistance = min(_minDistance, _maxDistance); // We grow _outerRadius in nextAnnulus() below. _innerRadius = _outerRadius = _minDistance; _outerRadiusInclusive = false; // Grab the IndexDescriptor. if ( !_params.collection ) { _failed = true; return; } _descriptor = _params.collection->getIndexCatalog()->findIndexByKeyPattern(_params.indexKeyPattern); if (NULL == _descriptor) { _failed = true; return; } // The user can override this so we honor it. We could ignore it though -- it's just used // to set _radiusIncrement, not to do any covering. int finestIndexedLevel; BSONElement fl = _descriptor->infoObj()["finestIndexedLevel"]; if (fl.isNumber()) { finestIndexedLevel = fl.numberInt(); } else { finestIndexedLevel = S2::kAvgEdge.GetClosestLevel(500.0 / kRadiusOfEarthInMeters); } // Start with a conservative _radiusIncrement. When we're done searching a shell we // increment the two radii by this. _radiusIncrement = 5 * S2::kAvgEdge.GetValue(finestIndexedLevel) * kRadiusOfEarthInMeters; }
void S2NearIndexCursor::seek(const BSONObj& query, const NearQuery& nearQuery, const vector<GeoQuery>& regions) { _indexedGeoFields = regions; _nearQuery = nearQuery; _returnedDistance = 0; _nearFieldIndex = 0; _stats = Stats(); _returned = unordered_set<DiskLoc, DiskLoc::Hasher>(); _results = priority_queue<Result>(); BSONObjBuilder geoFieldsToNuke; for (size_t i = 0; i < _indexedGeoFields.size(); ++i) { geoFieldsToNuke.append(_indexedGeoFields[i].getField(), ""); } // false means we want to filter OUT geoFieldsToNuke, not filter to include only that. _filteredQuery = query.filterFieldsUndotted(geoFieldsToNuke.obj(), false); // More indexing machinery. BSONObjBuilder specBuilder; BSONObjIterator specIt(_descriptor->keyPattern()); while (specIt.more()) { BSONElement e = specIt.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() ); } } _specForFRV = specBuilder.obj(); specIt = BSONObjIterator(_descriptor->keyPattern()); while (specIt.more()) { if (specIt.next().fieldName() == _nearQuery.field) { break; } ++_nearFieldIndex; } _minDistance = max(0.0, _nearQuery.minDistance); // _outerRadius can't be greater than (pi * r) or we wrap around the opposite // side of the world. _maxDistance = min(M_PI * _params.radius, _nearQuery.maxDistance); uassert(16892, "$minDistance too large", _minDistance < _maxDistance); // Start with a conservative _radiusIncrement. _radiusIncrement = 5 * S2::kAvgEdge.GetValue(_params.finestIndexedLevel) * _params.radius; _innerRadius = _outerRadius = _minDistance; // We might want to adjust the sizes of our coverings if our search // isn't local to the start point. // Set up _outerRadius with proper checks (maybe maxDistance is really small?) nextAnnulus(); fillResults(); }
S2NearCursor::S2NearCursor(const BSONObj &keyPattern, const IndexDetails *details, const BSONObj &query, const NearQuery &nearQuery, const vector<GeoQuery> &indexedGeoFields, const S2IndexingParams ¶ms) : _details(details), _nearQuery(nearQuery), _indexedGeoFields(indexedGeoFields), _params(params), _keyPattern(keyPattern), _nearFieldIndex(0), _returnedDistance(0) { BSONObjBuilder geoFieldsToNuke; for (size_t i = 0; i < _indexedGeoFields.size(); ++i) { geoFieldsToNuke.append(_indexedGeoFields[i].getField(), ""); } // false means we want to filter OUT geoFieldsToNuke, not filter to include only that. _filteredQuery = query.filterFieldsUndotted(geoFieldsToNuke.obj(), false); _matcher.reset(new CoveredIndexMatcher(_filteredQuery, keyPattern)); // More indexing machinery. BSONObjBuilder specBuilder; BSONObjIterator specIt(_keyPattern); while (specIt.more()) { BSONElement e = specIt.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(); _specForFRV = IndexSpec(spec); specIt = BSONObjIterator(_keyPattern); while (specIt.more()) { if (specIt.next().fieldName() == _nearQuery.field) { break; } ++_nearFieldIndex; } // _outerRadius can't be greater than (pi * r) or we wrap around the opposite // side of the world. _maxDistance = min(M_PI * _params.radius, _nearQuery.maxDistance); // Start with a conservative _radiusIncrement. _radiusIncrement = 5 * S2::kAvgEdge.GetValue(_params.finestIndexedLevel) * _params.radius; _innerRadius = _outerRadius = 0; // We might want to adjust the sizes of our coverings if our search // isn't local to the start point. // Set up _outerRadius with proper checks (maybe maxDistance is really small?) nextAnnulus(); }
static int getFieldPosition(const IndexDescriptor* index, const string& fieldName) { int fieldPosition = 0; BSONObjIterator specIt(index->keyPattern()); while (specIt.more()) { if (specIt.next().fieldName() == fieldName) { break; } ++fieldPosition; } if (fieldPosition == index->keyPattern().nFields()) return -1; return fieldPosition; }
S2NearStage::S2NearStage(const string& ns, const BSONObj& indexKeyPattern, const NearQuery& nearQuery, const IndexBounds& baseBounds, MatchExpression* filter, WorkingSet* ws) { _ns = ns; _ws = ws; _indexKeyPattern = indexKeyPattern; _nearQuery = nearQuery; _baseBounds = baseBounds; _filter = filter; _worked = false; _failed = false; // The field we're near-ing from is the n-th field. Figure out what that 'n' is. We // put the cover for the search annulus in this spot in the bounds. _nearFieldIndex = 0; BSONObjIterator specIt(_indexKeyPattern); while (specIt.more()) { if (specIt.next().fieldName() == _nearQuery.field) { break; } ++_nearFieldIndex; } verify(_nearFieldIndex < _indexKeyPattern.nFields()); // FLAT implies the distances are in radians. Convert to meters. if (FLAT == _nearQuery.centroid.crs) { _nearQuery.minDistance *= kRadiusOfEarthInMeters; _nearQuery.maxDistance *= kRadiusOfEarthInMeters; } // Make sure distances are sane. Possibly redundant given the checking during parsing. _minDistance = max(0.0, _nearQuery.minDistance); _maxDistance = min(M_PI * kRadiusOfEarthInMeters, _nearQuery.maxDistance); _minDistance = min(_minDistance, _maxDistance); // We grow _outerRadius in nextAnnulus() below. _innerRadius = _outerRadius = _minDistance; // XXX: where do we grab finestIndexedLevel from really? idx descriptor? int finestIndexedLevel = S2::kAvgEdge.GetClosestLevel(500.0 / kRadiusOfEarthInMeters); // Start with a conservative _radiusIncrement. When we're done searching a shell we // increment the two radii by this. _radiusIncrement = 5 * S2::kAvgEdge.GetValue(finestIndexedLevel) * kRadiusOfEarthInMeters; }
// static bool QueryPlannerIXSelect::compatible(const BSONElement& elt, const IndexEntry& index, MatchExpression* node) { // Historically one could create indices with any particular value for the index spec, // including values that now indicate a special index. As such we have to make sure the // index type wasn't overridden before we pay attention to the string in the index key // pattern element. // // e.g. long ago we could have created an index {a: "2dsphere"} and it would // be treated as a btree index by an ancient version of MongoDB. To try to run // 2dsphere queries over it would be folly. string indexedFieldType; if (String != elt.type() || (INDEX_BTREE == index.type)) { indexedFieldType = ""; } else { indexedFieldType = elt.String(); } // We know elt.fieldname() == node->path(). MatchExpression::MatchType exprtype = node->matchType(); if (indexedFieldType.empty()) { // Can't check for null w/a sparse index. if (exprtype == MatchExpression::EQ && index.sparse) { const EqualityMatchExpression* expr = static_cast<const EqualityMatchExpression*>(node); if (expr->getData().isNull()) { return false; } } // We can't use a btree-indexed field for geo expressions. if (exprtype == MatchExpression::GEO || exprtype == MatchExpression::GEO_NEAR) { return false; } // There are restrictions on when we can use the index if // the expression is a NOT. if (exprtype == MatchExpression::NOT) { // Prevent negated preds from using sparse or // multikey indices. We do so for sparse indices because // we will fail to return the documents which do not contain // the indexed fields. // // We avoid multikey indices because of the semantics of // negations on multikey fields. For example, with multikey // index {a:1}, the document {a: [1,2,3]} does *not* match // the query {a: {$ne: 3}}. We'd mess this up if we used // an index scan over [MinKey, 3) and (3, MaxKey] without // a filter. if (index.sparse || index.multikey) { return false; } // Can't index negations of MOD or REGEX MatchExpression::MatchType childtype = node->getChild(0)->matchType(); if (MatchExpression::REGEX == childtype || MatchExpression::MOD == childtype) { return false; } } // We can only index EQ using text indices. This is an artificial limitation imposed by // FTSSpec::getIndexPrefix() which will fail if there is not an EQ predicate on each // index prefix field of the text index. // // Example for key pattern {a: 1, b: "text"}: // - Allowed: node = {a: 7} // - Not allowed: node = {a: {$gt: 7}} if (INDEX_TEXT != index.type) { return true; } // If we're here we know it's a text index. Equalities are OK anywhere in a text index. if (MatchExpression::EQ == exprtype) { return true; } // Not-equalities can only go in a suffix field of an index kp. We look through the key // pattern to see if the field we're looking at now appears as a prefix. If so, we // can't use this index for it. BSONObjIterator specIt(index.keyPattern); while (specIt.more()) { BSONElement elt = specIt.next(); // We hit the dividing mark between prefix and suffix, so whatever field we're // looking at is a suffix, since it appears *after* the dividing mark between the // two. As such, we can use the index. if (String == elt.type()) { return true; } // If we're here, we're still looking at prefix elements. We know that exprtype // isn't EQ so we can't use this index. if (node->path() == elt.fieldNameStringData()) { return false; } } // NOTE: This shouldn't be reached. Text index implies there is a separator implies we // will always hit the 'return true' above. invariant(0); return true; } else if (IndexNames::HASHED == indexedFieldType) { return exprtype == MatchExpression::MATCH_IN || exprtype == MatchExpression::EQ; } else if (IndexNames::GEO_2DSPHERE == indexedFieldType) { if (exprtype == MatchExpression::GEO) { // within or intersect. GeoMatchExpression* gme = static_cast<GeoMatchExpression*>(node); const GeoQuery& gq = gme->getGeoQuery(); const GeometryContainer& gc = gq.getGeometry(); return gc.hasS2Region(); } else if (exprtype == MatchExpression::GEO_NEAR) { GeoNearMatchExpression* gnme = static_cast<GeoNearMatchExpression*>(node); // Make sure the near query is compatible with 2dsphere. if (gnme->getData().centroid.crs == SPHERE || gnme->getData().isNearSphere) { return true; } } return false; } else if (IndexNames::GEO_2D == indexedFieldType) { if (exprtype == MatchExpression::GEO_NEAR) { GeoNearMatchExpression* gnme = static_cast<GeoNearMatchExpression*>(node); return gnme->getData().centroid.crs == FLAT; } else if (exprtype == MatchExpression::GEO) { // 2d only supports within. GeoMatchExpression* gme = static_cast<GeoMatchExpression*>(node); const GeoQuery& gq = gme->getGeoQuery(); if (GeoQuery::WITHIN != gq.getPred()) { return false; } const GeometryContainer& gc = gq.getGeometry(); // 2d indices answer flat queries. if (gc.hasFlatRegion()) { return true; } // 2d indices can answer centerSphere queries. if (NULL == gc._cap.get()) { return false; } verify(SPHERE == gc._cap->crs); const Circle& circle = gc._cap->circle; // No wrapping around the edge of the world is allowed in 2d centerSphere. return twoDWontWrap(circle, index); } return false; } else if (IndexNames::TEXT == indexedFieldType) { return (exprtype == MatchExpression::TEXT); } else if (IndexNames::GEO_HAYSTACK == indexedFieldType) { return false; } else { warning() << "Unknown indexing for node " << node->toString() << " and field " << elt.toString() << endl; verify(0); } }