diff options
Diffstat (limited to 'src/mongo/db/index/expression_keys_private.cpp')
-rw-r--r-- | src/mongo/db/index/expression_keys_private.cpp | 774 |
1 files changed, 389 insertions, 385 deletions
diff --git a/src/mongo/db/index/expression_keys_private.cpp b/src/mongo/db/index/expression_keys_private.cpp index 1202b77ce0f..559175444bf 100644 --- a/src/mongo/db/index/expression_keys_private.cpp +++ b/src/mongo/db/index/expression_keys_private.cpp @@ -48,471 +48,475 @@ namespace { - using namespace mongo; - - // - // Helper functions for getHaystackKeys - // - - /** - * Build a new BSONObj with root in it. If e is non-empty, append that to the key. - * Insert the BSONObj into keys. - * Used by getHaystackKeys. - */ - void addKey(const string& root, const BSONElement& e, BSONObjSet* keys) { - BSONObjBuilder buf; - buf.append("", root); - - if (e.eoo()) - buf.appendNull(""); - else - buf.appendAs(e, ""); - - keys->insert(buf.obj()); - } +using namespace mongo; - // - // Helper functions for getS2Keys - // +// +// Helper functions for getHaystackKeys +// - static void S2KeysFromRegion(S2RegionCoverer *coverer, const S2Region ®ion, - vector<string> *out) { - vector<S2CellId> covering; - coverer->GetCovering(region, &covering); - for (size_t i = 0; i < covering.size(); ++i) { - out->push_back(covering[i].toString()); - } +/** + * Build a new BSONObj with root in it. If e is non-empty, append that to the key. + * Insert the BSONObj into keys. + * Used by getHaystackKeys. + */ +void addKey(const string& root, const BSONElement& e, BSONObjSet* keys) { + BSONObjBuilder buf; + buf.append("", root); + + if (e.eoo()) + buf.appendNull(""); + else + buf.appendAs(e, ""); + + keys->insert(buf.obj()); +} + +// +// Helper functions for getS2Keys +// + +static void S2KeysFromRegion(S2RegionCoverer* coverer, + const S2Region& region, + vector<string>* out) { + vector<S2CellId> covering; + coverer->GetCovering(region, &covering); + for (size_t i = 0; i < covering.size(); ++i) { + out->push_back(covering[i].toString()); } +} - Status S2GetKeysForElement(const BSONElement& element, - const S2IndexingParams& params, - vector<string>* out) { - GeometryContainer geoContainer; - Status status = geoContainer.parseFromStorage(element); - if (!status.isOK()) return status; - - S2RegionCoverer coverer; - params.configureCoverer(&coverer); +Status S2GetKeysForElement(const BSONElement& element, + const S2IndexingParams& params, + vector<string>* out) { + GeometryContainer geoContainer; + Status status = geoContainer.parseFromStorage(element); + if (!status.isOK()) + return status; - // Don't index big polygon - if (geoContainer.getNativeCRS() == STRICT_SPHERE) { - return Status(ErrorCodes::BadValue, "can't index geometry with strict winding order"); - } + S2RegionCoverer coverer; + params.configureCoverer(&coverer); - // Only certain geometries can be indexed in the old index format S2_INDEX_VERSION_1. See - // definition of S2IndexVersion for details. - if (params.indexVersion == S2_INDEX_VERSION_1 && !geoContainer.isSimpleContainer()) { - return Status(ErrorCodes::BadValue, - str::stream() - << "given geometry can't be indexed in the old index format"); - } - - // Project the geometry into spherical space - if (!geoContainer.supportsProject(SPHERE)) { - return Status(ErrorCodes::BadValue, - str::stream() << "can't project geometry into spherical CRS: " - << element.toString(false)); - } - geoContainer.projectInto(SPHERE); - - invariant(geoContainer.hasS2Region()); + // Don't index big polygon + if (geoContainer.getNativeCRS() == STRICT_SPHERE) { + return Status(ErrorCodes::BadValue, "can't index geometry with strict winding order"); + } - S2KeysFromRegion(&coverer, geoContainer.getS2Region(), out); - return Status::OK(); + // Only certain geometries can be indexed in the old index format S2_INDEX_VERSION_1. See + // definition of S2IndexVersion for details. + if (params.indexVersion == S2_INDEX_VERSION_1 && !geoContainer.isSimpleContainer()) { + return Status(ErrorCodes::BadValue, + str::stream() << "given geometry can't be indexed in the old index format"); } + // Project the geometry into spherical space + if (!geoContainer.supportsProject(SPHERE)) { + return Status(ErrorCodes::BadValue, + str::stream() << "can't project geometry into spherical CRS: " + << element.toString(false)); + } + geoContainer.projectInto(SPHERE); - /** - * Get the index keys for elements that are GeoJSON. - * Used by getS2Keys. - */ - void getS2GeoKeys(const BSONObj& document, const BSONElementSet& elements, - const S2IndexingParams& params, - BSONObjSet* out) { - for (BSONElementSet::iterator i = elements.begin(); i != elements.end(); ++i) { - vector<string> cells; - Status status = S2GetKeysForElement(*i, params, &cells); - uassert(16755, str::stream() << "Can't extract geo keys: " << document << " " - << status.reason(), status.isOK()); + invariant(geoContainer.hasS2Region()); - uassert(16756, "Unable to generate keys for (likely malformed) geometry: " - + document.toString(), - cells.size() > 0); + S2KeysFromRegion(&coverer, geoContainer.getS2Region(), out); + return Status::OK(); +} - for (vector<string>::const_iterator it = cells.begin(); it != cells.end(); ++it) { - BSONObjBuilder b; - b.append("", *it); - out->insert(b.obj()); - } - } - if (0 == out->size()) { +/** + * Get the index keys for elements that are GeoJSON. + * Used by getS2Keys. + */ +void getS2GeoKeys(const BSONObj& document, + const BSONElementSet& elements, + const S2IndexingParams& params, + BSONObjSet* out) { + for (BSONElementSet::iterator i = elements.begin(); i != elements.end(); ++i) { + vector<string> cells; + Status status = S2GetKeysForElement(*i, params, &cells); + uassert(16755, + str::stream() << "Can't extract geo keys: " << document << " " << status.reason(), + status.isOK()); + + uassert(16756, + "Unable to generate keys for (likely malformed) geometry: " + document.toString(), + cells.size() > 0); + + for (vector<string>::const_iterator it = cells.begin(); it != cells.end(); ++it) { BSONObjBuilder b; - b.appendNull(""); + b.append("", *it); out->insert(b.obj()); } } - /** - * Expands array and appends items to 'out'. - * Used by getOneLiteralKey. - */ - void getS2LiteralKeysArray(const BSONObj& obj, BSONObjSet* out) { - BSONObjIterator objIt(obj); - if (!objIt.more()) { - // Empty arrays are indexed as undefined. - BSONObjBuilder b; - b.appendUndefined(""); - out->insert(b.obj()); - } else { - // Non-empty arrays are exploded. - while (objIt.more()) { - BSONObjBuilder b; - b.appendAs(objIt.next(), ""); - out->insert(b.obj()); - } - } + if (0 == out->size()) { + BSONObjBuilder b; + b.appendNull(""); + out->insert(b.obj()); } +} - /** - * If 'elt' is an array, expands elt and adds items to 'out'. - * Otherwise, adds 'elt' as a single element. - * Used by getLiteralKeys. - */ - void getS2OneLiteralKey(const BSONElement& elt, BSONObjSet* out) { - if (Array == elt.type()) { - getS2LiteralKeysArray(elt.Obj(), out); - } else { - // One thing, not an array, index as-is. +/** + * Expands array and appends items to 'out'. + * Used by getOneLiteralKey. + */ +void getS2LiteralKeysArray(const BSONObj& obj, BSONObjSet* out) { + BSONObjIterator objIt(obj); + if (!objIt.more()) { + // Empty arrays are indexed as undefined. + BSONObjBuilder b; + b.appendUndefined(""); + out->insert(b.obj()); + } else { + // Non-empty arrays are exploded. + while (objIt.more()) { BSONObjBuilder b; - b.appendAs(elt, ""); + b.appendAs(objIt.next(), ""); out->insert(b.obj()); } } +} - /** - * elements is a non-geo field. Add the values literally, expanding arrays. - * Used by getS2Keys. - */ - void getS2LiteralKeys(const BSONElementSet& elements, BSONObjSet* out) { - if (0 == elements.size()) { - // Missing fields are indexed as null. - BSONObjBuilder b; - b.appendNull(""); - out->insert(b.obj()); - } else { - for (BSONElementSet::iterator i = elements.begin(); i != elements.end(); ++i) { - getS2OneLiteralKey(*i, out); - } +/** + * If 'elt' is an array, expands elt and adds items to 'out'. + * Otherwise, adds 'elt' as a single element. + * Used by getLiteralKeys. + */ +void getS2OneLiteralKey(const BSONElement& elt, BSONObjSet* out) { + if (Array == elt.type()) { + getS2LiteralKeysArray(elt.Obj(), out); + } else { + // One thing, not an array, index as-is. + BSONObjBuilder b; + b.appendAs(elt, ""); + out->insert(b.obj()); + } +} + +/** + * elements is a non-geo field. Add the values literally, expanding arrays. + * Used by getS2Keys. + */ +void getS2LiteralKeys(const BSONElementSet& elements, BSONObjSet* out) { + if (0 == elements.size()) { + // Missing fields are indexed as null. + BSONObjBuilder b; + b.appendNull(""); + out->insert(b.obj()); + } else { + for (BSONElementSet::iterator i = elements.begin(); i != elements.end(); ++i) { + getS2OneLiteralKey(*i, out); } } +} -} // namespace +} // namespace namespace mongo { - using std::pair; - using std::string; - using std::vector; - - // static - void ExpressionKeysPrivate::get2DKeys(const BSONObj &obj, - const TwoDIndexingParams& params, - BSONObjSet* keys, - std::vector<BSONObj>* locs) { - BSONElementMSet bSet; +using std::pair; +using std::string; +using std::vector; - // Get all the nested location fields, but don't return individual elements from - // the last array, if it exists. - obj.getFieldsDotted(params.geo.c_str(), bSet, false); +// static +void ExpressionKeysPrivate::get2DKeys(const BSONObj& obj, + const TwoDIndexingParams& params, + BSONObjSet* keys, + std::vector<BSONObj>* locs) { + BSONElementMSet bSet; - if (bSet.empty()) - return; + // Get all the nested location fields, but don't return individual elements from + // the last array, if it exists. + obj.getFieldsDotted(params.geo.c_str(), bSet, false); - for (BSONElementMSet::iterator setI = bSet.begin(); setI != bSet.end(); ++setI) { - BSONElement geo = *setI; + if (bSet.empty()) + return; - if (geo.eoo() || !geo.isABSONObj()) - continue; + for (BSONElementMSet::iterator setI = bSet.begin(); setI != bSet.end(); ++setI) { + BSONElement geo = *setI; - // - // Grammar for location lookup: - // locs ::= [loc,loc,...,loc]|{<k>:loc,<k>:loc,...,<k>:loc}|loc - // loc ::= { <k1> : #, <k2> : # }|[#, #]|{} - // - // Empty locations are ignored, preserving single-location semantics - // + if (geo.eoo() || !geo.isABSONObj()) + continue; - BSONObj embed = geo.embeddedObject(); - if (embed.isEmpty()) - continue; + // + // Grammar for location lookup: + // locs ::= [loc,loc,...,loc]|{<k>:loc,<k>:loc,...,<k>:loc}|loc + // loc ::= { <k1> : #, <k2> : # }|[#, #]|{} + // + // Empty locations are ignored, preserving single-location semantics + // - // Differentiate between location arrays and locations - // by seeing if the first element value is a number - bool singleElement = embed.firstElement().isNumber(); + BSONObj embed = geo.embeddedObject(); + if (embed.isEmpty()) + continue; - BSONObjIterator oi(embed); + // Differentiate between location arrays and locations + // by seeing if the first element value is a number + bool singleElement = embed.firstElement().isNumber(); - while (oi.more()) { - BSONObj locObj; + BSONObjIterator oi(embed); - if (singleElement) { - locObj = embed; - } else { - BSONElement locElement = oi.next(); + while (oi.more()) { + BSONObj locObj; - uassert(16804, mongoutils::str::stream() << - "location object expected, location array not in correct format", - locElement.isABSONObj()); + if (singleElement) { + locObj = embed; + } else { + BSONElement locElement = oi.next(); - locObj = locElement.embeddedObject(); - if(locObj.isEmpty()) - continue; - } + uassert(16804, + mongoutils::str::stream() + << "location object expected, location array not in correct format", + locElement.isABSONObj()); - BSONObjBuilder b(64); + locObj = locElement.embeddedObject(); + if (locObj.isEmpty()) + continue; + } - // Remember the actual location object if needed - if (locs) - locs->push_back(locObj); + BSONObjBuilder b(64); - // Stop if we don't need to get anything but location objects - if (!keys) { - if (singleElement) break; - else continue; - } + // Remember the actual location object if needed + if (locs) + locs->push_back(locObj); - params.geoHashConverter->hash(locObj, &obj).appendHashMin(&b, ""); - - // Go through all the other index keys - for (vector<pair<string, int> >::const_iterator i = params.other.begin(); - i != params.other.end(); ++i) { - // Get *all* fields for the index key - BSONElementSet eSet; - obj.getFieldsDotted(i->first, eSet); - - if (eSet.size() == 0) - b.appendNull(""); - else if (eSet.size() == 1) - b.appendAs(*(eSet.begin()), ""); - else { - // If we have more than one key, store as an array of the objects - BSONArrayBuilder aBuilder; - - for (BSONElementSet::iterator ei = eSet.begin(); ei != eSet.end(); - ++ei) { - aBuilder.append(*ei); - } + // Stop if we don't need to get anything but location objects + if (!keys) { + if (singleElement) + break; + else + continue; + } - b.append("", aBuilder.arr()); + params.geoHashConverter->hash(locObj, &obj).appendHashMin(&b, ""); + + // Go through all the other index keys + for (vector<pair<string, int>>::const_iterator i = params.other.begin(); + i != params.other.end(); + ++i) { + // Get *all* fields for the index key + BSONElementSet eSet; + obj.getFieldsDotted(i->first, eSet); + + if (eSet.size() == 0) + b.appendNull(""); + else if (eSet.size() == 1) + b.appendAs(*(eSet.begin()), ""); + else { + // If we have more than one key, store as an array of the objects + BSONArrayBuilder aBuilder; + + for (BSONElementSet::iterator ei = eSet.begin(); ei != eSet.end(); ++ei) { + aBuilder.append(*ei); } + + b.append("", aBuilder.arr()); } - keys->insert(b.obj()); - if(singleElement) break; } + keys->insert(b.obj()); + if (singleElement) + break; } } - - // static - void ExpressionKeysPrivate::getFTSKeys(const BSONObj &obj, - const fts::FTSSpec& ftsSpec, - BSONObjSet* keys) { - fts::FTSIndexFormat::getKeys(ftsSpec, obj, keys); +} + +// static +void ExpressionKeysPrivate::getFTSKeys(const BSONObj& obj, + const fts::FTSSpec& ftsSpec, + BSONObjSet* keys) { + fts::FTSIndexFormat::getKeys(ftsSpec, obj, keys); +} + +// static +void ExpressionKeysPrivate::getHashKeys(const BSONObj& obj, + const string& hashedField, + HashSeed seed, + int hashVersion, + bool isSparse, + BSONObjSet* keys) { + const char* cstr = hashedField.c_str(); + BSONElement fieldVal = obj.getFieldDottedOrArray(cstr); + uassert(16766, + "Error: hashed indexes do not currently support array values", + fieldVal.type() != Array); + + if (!fieldVal.eoo()) { + BSONObj key = BSON("" << makeSingleHashKey(fieldVal, seed, hashVersion)); + keys->insert(key); + } else if (!isSparse) { + BSONObj nullObj = BSON("" << BSONNULL); + keys->insert(BSON("" << makeSingleHashKey(nullObj.firstElement(), seed, hashVersion))); } - - // static - void ExpressionKeysPrivate::getHashKeys(const BSONObj& obj, - const string& hashedField, - HashSeed seed, - int hashVersion, - bool isSparse, +} + +// static +long long int ExpressionKeysPrivate::makeSingleHashKey(const BSONElement& e, HashSeed seed, int v) { + massert(16767, "Only HashVersion 0 has been defined", v == 0); + return BSONElementHasher::hash64(e, seed); +} + +// static +void ExpressionKeysPrivate::getHaystackKeys(const BSONObj& obj, + const std::string& geoField, + const std::vector<std::string>& otherFields, + double bucketSize, BSONObjSet* keys) { + BSONElement loc = obj.getFieldDotted(geoField); - const char* cstr = hashedField.c_str(); - BSONElement fieldVal = obj.getFieldDottedOrArray(cstr); - uassert(16766, "Error: hashed indexes do not currently support array values", - fieldVal.type() != Array ); - - if (!fieldVal.eoo()) { - BSONObj key = BSON( "" << makeSingleHashKey(fieldVal, seed, hashVersion)); - keys->insert(key); - } - else if (!isSparse) { - BSONObj nullObj = BSON("" << BSONNULL); - keys->insert(BSON("" << makeSingleHashKey(nullObj.firstElement(), seed, hashVersion))); - } + if (loc.eoo()) { + return; } - // static - long long int ExpressionKeysPrivate::makeSingleHashKey(const BSONElement& e, - HashSeed seed, - int v) { - massert(16767, "Only HashVersion 0 has been defined" , v == 0 ); - return BSONElementHasher::hash64(e, seed); - } - - // static - void ExpressionKeysPrivate::getHaystackKeys(const BSONObj& obj, - const std::string& geoField, - const std::vector<std::string>& otherFields, - double bucketSize, - BSONObjSet* keys) { - - BSONElement loc = obj.getFieldDotted(geoField); - - if (loc.eoo()) { return; } - - // NOTE: We explicitly test nFields >= 2 to support legacy users who may have indexed - // (intentionally or unintentionally) objects/arrays with more than two fields. - uassert(16775, str::stream() << "cannot extract [lng, lat] array or object from " << obj, + // NOTE: We explicitly test nFields >= 2 to support legacy users who may have indexed + // (intentionally or unintentionally) objects/arrays with more than two fields. + uassert(16775, + str::stream() << "cannot extract [lng, lat] array or object from " << obj, loc.isABSONObj() && loc.Obj().nFields() >= 2); - string root; - { - BSONObjIterator i(loc.Obj()); - BSONElement x = i.next(); - BSONElement y = i.next(); - root = makeHaystackString(hashHaystackElement(x, bucketSize), - hashHaystackElement(y, bucketSize)); - } - - verify(otherFields.size() == 1); - - BSONElementSet all; - - // This is getFieldsDotted (plural not singular) since the object we're indexing - // may be an array. - obj.getFieldsDotted(otherFields[0], all); - - if (all.size() == 0) { - // We're indexing a document that doesn't have the secondary non-geo field present. - // XXX: do we want to add this even if all.size() > 0? result:empty search terms - // match everything instead of only things w/empty search terms) - addKey(root, BSONElement(), keys); - } else { - // Ex:If our secondary field is type: "foo" or type: {a:"foo", b:"bar"}, - // all.size()==1. We can query on the complete field. - // Ex: If our secondary field is type: ["A", "B"] all.size()==2 and all has values - // "A" and "B". The query looks for any of the fields in the array. - for (BSONElementSet::iterator i = all.begin(); i != all.end(); ++i) { - addKey(root, *i, keys); - } - } - } - - // static - int ExpressionKeysPrivate::hashHaystackElement(const BSONElement& e, double bucketSize) { - uassert(16776, "geo field is not a number", e.isNumber()); - double d = e.numberDouble(); - d += 180; - d /= bucketSize; - return static_cast<int>(d); + string root; + { + BSONObjIterator i(loc.Obj()); + BSONElement x = i.next(); + BSONElement y = i.next(); + root = makeHaystackString(hashHaystackElement(x, bucketSize), + hashHaystackElement(y, bucketSize)); } - // static - std::string ExpressionKeysPrivate::makeHaystackString(int hashedX, int hashedY) { - mongoutils::str::stream ss; - ss << hashedX << "_" << hashedY; - return ss; + verify(otherFields.size() == 1); + + BSONElementSet all; + + // This is getFieldsDotted (plural not singular) since the object we're indexing + // may be an array. + obj.getFieldsDotted(otherFields[0], all); + + if (all.size() == 0) { + // We're indexing a document that doesn't have the secondary non-geo field present. + // XXX: do we want to add this even if all.size() > 0? result:empty search terms + // match everything instead of only things w/empty search terms) + addKey(root, BSONElement(), keys); + } else { + // Ex:If our secondary field is type: "foo" or type: {a:"foo", b:"bar"}, + // all.size()==1. We can query on the complete field. + // Ex: If our secondary field is type: ["A", "B"] all.size()==2 and all has values + // "A" and "B". The query looks for any of the fields in the array. + for (BSONElementSet::iterator i = all.begin(); i != all.end(); ++i) { + addKey(root, *i, keys); + } } - - void ExpressionKeysPrivate::getS2Keys(const BSONObj& obj, - const BSONObj& keyPattern, - const S2IndexingParams& params, - BSONObjSet* keys) { - BSONObjSet keysToAdd; - - // Does one of our documents have a geo field? - bool haveGeoField = false; - - // We output keys in the same order as the fields we index. - BSONObjIterator i(keyPattern); - while (i.more()) { - BSONElement e = i.next(); - - // First, we get the keys that this field adds. Either they're added literally from - // the value of the field, or they're transformed if the field is geo. - BSONElementSet fieldElements; - // false means Don't expand the last array, duh. - obj.getFieldsDotted(e.fieldName(), fieldElements, false); - - BSONObjSet keysForThisField; - if (IndexNames::GEO_2DSPHERE == e.valuestr()) { - if (S2_INDEX_VERSION_2 == params.indexVersion) { - // For V2, - // geo: null, - // geo: undefined - // geo: [] - // should all behave like there is no geo field. So we look for these cases and - // throw out the field elements if we find them. - if (1 == fieldElements.size()) { - BSONElement elt = *fieldElements.begin(); - // Get the :null and :undefined cases. - if (elt.isNull() || Undefined == elt.type()) { +} + +// static +int ExpressionKeysPrivate::hashHaystackElement(const BSONElement& e, double bucketSize) { + uassert(16776, "geo field is not a number", e.isNumber()); + double d = e.numberDouble(); + d += 180; + d /= bucketSize; + return static_cast<int>(d); +} + +// static +std::string ExpressionKeysPrivate::makeHaystackString(int hashedX, int hashedY) { + mongoutils::str::stream ss; + ss << hashedX << "_" << hashedY; + return ss; +} + +void ExpressionKeysPrivate::getS2Keys(const BSONObj& obj, + const BSONObj& keyPattern, + const S2IndexingParams& params, + BSONObjSet* keys) { + BSONObjSet keysToAdd; + + // Does one of our documents have a geo field? + bool haveGeoField = false; + + // We output keys in the same order as the fields we index. + BSONObjIterator i(keyPattern); + while (i.more()) { + BSONElement e = i.next(); + + // First, we get the keys that this field adds. Either they're added literally from + // the value of the field, or they're transformed if the field is geo. + BSONElementSet fieldElements; + // false means Don't expand the last array, duh. + obj.getFieldsDotted(e.fieldName(), fieldElements, false); + + BSONObjSet keysForThisField; + if (IndexNames::GEO_2DSPHERE == e.valuestr()) { + if (S2_INDEX_VERSION_2 == params.indexVersion) { + // For V2, + // geo: null, + // geo: undefined + // geo: [] + // should all behave like there is no geo field. So we look for these cases and + // throw out the field elements if we find them. + if (1 == fieldElements.size()) { + BSONElement elt = *fieldElements.begin(); + // Get the :null and :undefined cases. + if (elt.isNull() || Undefined == elt.type()) { + fieldElements.clear(); + } else if (elt.isABSONObj()) { + // And this is the :[] case. + BSONObj obj = elt.Obj(); + if (0 == obj.nFields()) { fieldElements.clear(); } - else if (elt.isABSONObj()) { - // And this is the :[] case. - BSONObj obj = elt.Obj(); - if (0 == obj.nFields()) { - fieldElements.clear(); - } - } - } - - // V2 2dsphere indices require that at least one geo field to be present in a - // document in order to index it. - if (fieldElements.size() > 0) { - haveGeoField = true; } } - getS2GeoKeys(obj, fieldElements, params, &keysForThisField); - } else { - getS2LiteralKeys(fieldElements, &keysForThisField); + // V2 2dsphere indices require that at least one geo field to be present in a + // document in order to index it. + if (fieldElements.size() > 0) { + haveGeoField = true; + } } - // We expect there to be the missing field element present in the keys if data is - // missing. So, this should be non-empty. - verify(!keysForThisField.empty()); + getS2GeoKeys(obj, fieldElements, params, &keysForThisField); + } else { + getS2LiteralKeys(fieldElements, &keysForThisField); + } - // We take the Cartesian product of all of the keys. This requires that we have - // some keys to take the Cartesian product with. If keysToAdd.empty(), we - // initialize it. - if (keysToAdd.empty()) { - keysToAdd = keysForThisField; - continue; - } + // We expect there to be the missing field element present in the keys if data is + // missing. So, this should be non-empty. + verify(!keysForThisField.empty()); - BSONObjSet updatedKeysToAdd; - for (BSONObjSet::const_iterator it = keysToAdd.begin(); it != keysToAdd.end(); - ++it) { - for (BSONObjSet::const_iterator newIt = keysForThisField.begin(); - newIt!= keysForThisField.end(); ++newIt) { - BSONObjBuilder b; - b.appendElements(*it); - b.append(newIt->firstElement()); - updatedKeysToAdd.insert(b.obj()); - } - } - keysToAdd = updatedKeysToAdd; + // We take the Cartesian product of all of the keys. This requires that we have + // some keys to take the Cartesian product with. If keysToAdd.empty(), we + // initialize it. + if (keysToAdd.empty()) { + keysToAdd = keysForThisField; + continue; } - // Make sure that if we're V2 there's at least one geo field present in the doc. - if (S2_INDEX_VERSION_2 == params.indexVersion) { - if (!haveGeoField) { - return; + BSONObjSet updatedKeysToAdd; + for (BSONObjSet::const_iterator it = keysToAdd.begin(); it != keysToAdd.end(); ++it) { + for (BSONObjSet::const_iterator newIt = keysForThisField.begin(); + newIt != keysForThisField.end(); + ++newIt) { + BSONObjBuilder b; + b.appendElements(*it); + b.append(newIt->firstElement()); + updatedKeysToAdd.insert(b.obj()); } } + keysToAdd = updatedKeysToAdd; + } - if (keysToAdd.size() > params.maxKeysPerInsert) { - warning() << "Insert of geo object generated a high number of keys." - << " num keys: " << keysToAdd.size() - << " obj inserted: " << obj; + // Make sure that if we're V2 there's at least one geo field present in the doc. + if (S2_INDEX_VERSION_2 == params.indexVersion) { + if (!haveGeoField) { + return; } + } - *keys = keysToAdd; + if (keysToAdd.size() > params.maxKeysPerInsert) { + warning() << "Insert of geo object generated a high number of keys." + << " num keys: " << keysToAdd.size() << " obj inserted: " << obj; } + *keys = keysToAdd; +} + } // namespace mongo |