summaryrefslogtreecommitdiff
path: root/src/mongo/db/index/expression_keys_private.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/index/expression_keys_private.cpp')
-rw-r--r--src/mongo/db/index/expression_keys_private.cpp774
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 &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());
- }
+/**
+ * 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