diff options
author | David Storch <david.storch@10gen.com> | 2016-05-23 17:55:01 -0400 |
---|---|---|
committer | David Storch <david.storch@10gen.com> | 2016-05-25 14:40:31 -0400 |
commit | 439df4c3bdff0194cff402a6268a9bdb9de44a7a (patch) | |
tree | c1d0c8d613d65516cce48327d183776464016634 /src | |
parent | 15deafbf2aa6524f3b05dd729b4ff378e18a2162 (diff) | |
download | mongo-439df4c3bdff0194cff402a6268a9bdb9de44a7a.tar.gz |
SERVER-23945 make IndexCatalog::_fixIndexSpec() use Status instead of throwing
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/catalog/index_catalog.cpp | 19 | ||||
-rw-r--r-- | src/mongo/db/catalog/index_catalog.h | 2 | ||||
-rw-r--r-- | src/mongo/db/fts/fts_element_iterator_test.cpp | 17 | ||||
-rw-r--r-- | src/mongo/db/fts/fts_index_format_test.cpp | 56 | ||||
-rw-r--r-- | src/mongo/db/fts/fts_matcher_test.cpp | 46 | ||||
-rw-r--r-- | src/mongo/db/fts/fts_spec.cpp | 137 | ||||
-rw-r--r-- | src/mongo/db/fts/fts_spec.h | 5 | ||||
-rw-r--r-- | src/mongo/db/fts/fts_spec_legacy.cpp | 18 | ||||
-rw-r--r-- | src/mongo/db/fts/fts_spec_test.cpp | 58 | ||||
-rw-r--r-- | src/mongo/db/index/s2_access_method.cpp | 39 | ||||
-rw-r--r-- | src/mongo/db/index/s2_access_method.h | 7 | ||||
-rw-r--r-- | src/mongo/db/index_legacy.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/index_legacy.h | 3 |
13 files changed, 230 insertions, 179 deletions
diff --git a/src/mongo/db/catalog/index_catalog.cpp b/src/mongo/db/catalog/index_catalog.cpp index 2c3408a71fc..da3d0553d9f 100644 --- a/src/mongo/db/catalog/index_catalog.cpp +++ b/src/mongo/db/catalog/index_catalog.cpp @@ -276,18 +276,21 @@ StatusWith<BSONObj> IndexCatalog::prepareSpecForCreate(OperationContext* txn, if (!status.isOK()) return StatusWith<BSONObj>(status); - BSONObj fixed = _fixIndexSpec(original); + auto fixed = _fixIndexSpec(original); + if (!fixed.isOK()) { + return fixed; + } // we double check with new index spec - status = _isSpecOk(txn, fixed); + status = _isSpecOk(txn, fixed.getValue()); if (!status.isOK()) return StatusWith<BSONObj>(status); - status = _doesSpecConflictWithExisting(txn, fixed); + status = _doesSpecConflictWithExisting(txn, fixed.getValue()); if (!status.isOK()) return StatusWith<BSONObj>(status); - return StatusWith<BSONObj>(fixed); + return fixed; } Status IndexCatalog::createIndexOnEmptyCollection(OperationContext* txn, BSONObj spec) { @@ -1257,8 +1260,12 @@ BSONObj IndexCatalog::fixIndexKey(const BSONObj& key) { return key; } -BSONObj IndexCatalog::_fixIndexSpec(const BSONObj& spec) { - BSONObj o = IndexLegacy::adjustIndexSpecObject(spec); +StatusWith<BSONObj> IndexCatalog::_fixIndexSpec(const BSONObj& spec) { + auto statusWithSpec = IndexLegacy::adjustIndexSpecObject(spec); + if (!statusWithSpec.isOK()) { + return statusWithSpec; + } + BSONObj o = statusWithSpec.getValue(); BSONObjBuilder b; diff --git a/src/mongo/db/catalog/index_catalog.h b/src/mongo/db/catalog/index_catalog.h index 108f8b9966e..dac775501a2 100644 --- a/src/mongo/db/catalog/index_catalog.h +++ b/src/mongo/db/catalog/index_catalog.h @@ -364,7 +364,7 @@ private: // conform to the standard for insertion. This function adds the 'v' field if it didn't // exist, removes the '_id' field if it exists, applies plugin-level transformations if // appropriate, etc. - static BSONObj _fixIndexSpec(const BSONObj& spec); + static StatusWith<BSONObj> _fixIndexSpec(const BSONObj& spec); Status _isSpecOk(OperationContext* txn, const BSONObj& spec) const; diff --git a/src/mongo/db/fts/fts_element_iterator_test.cpp b/src/mongo/db/fts/fts_element_iterator_test.cpp index 26e390d0f28..014eeec119d 100644 --- a/src/mongo/db/fts/fts_element_iterator_test.cpp +++ b/src/mongo/db/fts/fts_element_iterator_test.cpp @@ -37,6 +37,7 @@ namespace mongo { namespace fts { using std::string; +using unittest::assertGet; TEST(FTSElementIterator, Test1) { BSONObj obj = fromjson( @@ -47,7 +48,7 @@ TEST(FTSElementIterator, Test1) { BSONObj indexSpec = fromjson("{ key : { a : \"text\" }, weights : { b : 10, d : 5 } }"); - FTSSpec spec(FTSSpec::fixSpec(indexSpec)); + FTSSpec spec(assertGet(FTSSpec::fixSpec(indexSpec))); Weights::const_iterator itt = spec.weights().begin(); ASSERT(itt != spec.weights().end()); ASSERT_EQUALS("a", itt->first); @@ -92,7 +93,7 @@ TEST(FTSElementIterator, Test2) { BSONObj indexSpec = fromjson("{ key : { \"a.b.c\" : \"text\", d : \"text\" } }"); - FTSSpec spec(FTSSpec::fixSpec(indexSpec)); + FTSSpec spec(assertGet(FTSSpec::fixSpec(indexSpec))); FTSElementIterator it(spec, obj); @@ -137,7 +138,7 @@ TEST(FTSElementIterator, Test3) { BSONObj indexSpec = fromjson("{ key : { a : \"text\", \"a.b.c\" : \"text\" }, weights : { \"a.b.c\" : 5 } }"); - FTSSpec spec(FTSSpec::fixSpec(indexSpec)); + FTSSpec spec(assertGet(FTSSpec::fixSpec(indexSpec))); Weights::const_iterator itt = spec.weights().begin(); ASSERT(itt != spec.weights().end()); ASSERT_EQUALS("a", itt->first); @@ -183,7 +184,7 @@ TEST(FTSElementIterator, Test4) { BSONObj indexSpec = fromjson("{ key : { \"a.b.c\" : \"text\" }, weights : { \"a.b.c\" : 5 } }"); - FTSSpec spec(FTSSpec::fixSpec(indexSpec)); + FTSSpec spec(assertGet(FTSSpec::fixSpec(indexSpec))); FTSElementIterator it(spec, obj); ASSERT(it.more()); @@ -223,7 +224,7 @@ TEST(FTSElementIterator, Test5) { BSONObj indexSpec = fromjson("{ key : { a : \"text\" }, weights : { b : 20, c : 10, \"d.e.f\" : 5 } }"); - FTSSpec spec(FTSSpec::fixSpec(indexSpec)); + FTSSpec spec(assertGet(FTSSpec::fixSpec(indexSpec))); FTSElementIterator it(spec, obj); ASSERT(it.more()); @@ -269,7 +270,7 @@ TEST(FTSElementIterator, Test6) { BSONObj indexSpec = fromjson("{ key : { a : \"text\" }, weights : { b : 20, c : 10, \"d.e.f\" : 5 } }"); - FTSSpec spec(FTSSpec::fixSpec(indexSpec)); + FTSSpec spec(assertGet(FTSSpec::fixSpec(indexSpec))); FTSElementIterator it(spec, obj); ASSERT(it.more()); @@ -313,7 +314,7 @@ TEST(FTSElementIterator, LanguageOverrideV2) { BSONObj indexSpec = fromjson("{ key : { \"a.b.c\" : \"text\", d : \"text\" }, textIndexVersion: 2.0 }"); - FTSSpec spec(FTSSpec::fixSpec(indexSpec)); + FTSSpec spec(assertGet(FTSSpec::fixSpec(indexSpec))); FTSElementIterator it(spec, obj); @@ -362,7 +363,7 @@ TEST(FTSElementIterator, LanguageOverrideV3) { BSONObj indexSpec = fromjson("{ key : { \"a.b.c\" : \"text\", d : \"text\" }, textIndexVersion: 3.0 }"); - FTSSpec spec(FTSSpec::fixSpec(indexSpec)); + FTSSpec spec(assertGet(FTSSpec::fixSpec(indexSpec))); FTSElementIterator it(spec, obj); diff --git a/src/mongo/db/fts/fts_index_format_test.cpp b/src/mongo/db/fts/fts_index_format_test.cpp index 8d19e975cab..af353d51f26 100644 --- a/src/mongo/db/fts/fts_index_format_test.cpp +++ b/src/mongo/db/fts/fts_index_format_test.cpp @@ -45,10 +45,11 @@ namespace mongo { namespace fts { using std::string; +using unittest::assertGet; TEST(FTSIndexFormat, Simple1) { - FTSSpec spec(FTSSpec::fixSpec(BSON("key" << BSON("data" - << "text")))); + FTSSpec spec(assertGet(FTSSpec::fixSpec(BSON("key" << BSON("data" + << "text"))))); BSONObjSet keys; FTSIndexFormat::getKeys(spec, BSON("data" @@ -64,9 +65,9 @@ TEST(FTSIndexFormat, Simple1) { } TEST(FTSIndexFormat, ExtraBack1) { - FTSSpec spec(FTSSpec::fixSpec(BSON("key" << BSON("data" - << "text" - << "x" << 1)))); + FTSSpec spec(assertGet(FTSSpec::fixSpec(BSON("key" << BSON("data" + << "text" + << "x" << 1))))); BSONObjSet keys; FTSIndexFormat::getKeys(spec, BSON("data" @@ -83,31 +84,9 @@ TEST(FTSIndexFormat, ExtraBack1) { ASSERT_EQUALS(5, i.next().numberInt()); } -/* -TEST( FTSIndexFormat, ExtraBackArray1 ) { - FTSSpec spec( FTSSpec::fixSpec( BSON( "key" << BSON( "data" << "text" << - "x.y" << 1 ) ) ) ); - BSONObjSet keys; - FTSIndexFormat::getKeys( spec, - BSON( "data" << "cat" << - "x" << BSON_ARRAY( BSON( "y" << 1 ) << - BSON( "y" << 2 ) ) ), - &keys ); - - ASSERT_EQUALS( 1U, keys.size() ); - BSONObj key = *(keys.begin()); - log() << "e: " << key << endl; - ASSERT_EQUALS( 3, key.nFields() ); - BSONObjIterator i( key ); - ASSERT_EQUALS( StringData("cat"), i.next().valuestr() ); - ASSERT( i.next().numberDouble() > 0 ); - ASSERT_EQUALS( 5, i.next().numberInt() ); -} -*/ - TEST(FTSIndexFormat, ExtraFront1) { - FTSSpec spec(FTSSpec::fixSpec(BSON("key" << BSON("x" << 1 << "data" - << "text")))); + FTSSpec spec(assertGet(FTSSpec::fixSpec(BSON("key" << BSON("x" << 1 << "data" + << "text"))))); BSONObjSet keys; FTSIndexFormat::getKeys(spec, BSON("data" @@ -125,8 +104,8 @@ TEST(FTSIndexFormat, ExtraFront1) { } TEST(FTSIndexFormat, StopWords1) { - FTSSpec spec(FTSSpec::fixSpec(BSON("key" << BSON("data" - << "text")))); + FTSSpec spec(assertGet(FTSSpec::fixSpec(BSON("key" << BSON("data" + << "text"))))); BSONObjSet keys1; FTSIndexFormat::getKeys(spec, @@ -173,8 +152,9 @@ void assertEqualsIndexKeys(std::set<std::string>& expectedKeys, const BSONObjSet * Terms that are too long are not truncated in version 1. */ TEST(FTSIndexFormat, LongWordsTextIndexVersion1) { - FTSSpec spec(FTSSpec::fixSpec(BSON("key" << BSON("data" - << "text") << "textIndexVersion" << 1))); + FTSSpec spec( + assertGet(FTSSpec::fixSpec(BSON("key" << BSON("data" + << "text") << "textIndexVersion" << 1)))); BSONObjSet keys; string longPrefix(1024U, 'a'); // "aaa...aaacat" @@ -201,8 +181,9 @@ TEST(FTSIndexFormat, LongWordsTextIndexVersion1) { * characters of the term to form the index key. */ TEST(FTSIndexFormat, LongWordTextIndexVersion2) { - FTSSpec spec(FTSSpec::fixSpec(BSON("key" << BSON("data" - << "text") << "textIndexVersion" << 2))); + FTSSpec spec( + assertGet(FTSSpec::fixSpec(BSON("key" << BSON("data" + << "text") << "textIndexVersion" << 2)))); BSONObjSet keys; string longPrefix(1024U, 'a'); // "aaa...aaacat" @@ -234,8 +215,9 @@ TEST(FTSIndexFormat, LongWordTextIndexVersion2) { * characters of the term to form the index key. */ TEST(FTSIndexFormat, LongWordTextIndexVersion3) { - FTSSpec spec(FTSSpec::fixSpec(BSON("key" << BSON("data" - << "text") << "textIndexVersion" << 3))); + FTSSpec spec( + assertGet(FTSSpec::fixSpec(BSON("key" << BSON("data" + << "text") << "textIndexVersion" << 3)))); BSONObjSet keys; string longPrefix(1024U, 'a'); // "aaa...aaacat" diff --git a/src/mongo/db/fts/fts_matcher_test.cpp b/src/mongo/db/fts/fts_matcher_test.cpp index 9b590fa67a3..8fdb705222e 100644 --- a/src/mongo/db/fts/fts_matcher_test.cpp +++ b/src/mongo/db/fts/fts_matcher_test.cpp @@ -36,6 +36,8 @@ namespace mongo { namespace fts { +using unittest::assertGet; + TEST(FTSMatcher, NegWild1) { FTSQueryImpl q; q.setQuery("foo -bar"); @@ -44,8 +46,8 @@ TEST(FTSMatcher, NegWild1) { q.setDiacriticSensitive(false); ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK()); FTSMatcher m(q, - FTSSpec(FTSSpec::fixSpec(BSON("key" << BSON("$**" - << "text"))))); + FTSSpec(assertGet(FTSSpec::fixSpec(BSON("key" << BSON("$**" + << "text")))))); ASSERT(m.hasNegativeTerm(BSON("x" << BSON("y" << "bar")))); @@ -62,8 +64,8 @@ TEST(FTSMatcher, NegWild2) { q.setDiacriticSensitive(false); ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK()); FTSMatcher m(q, - FTSSpec(FTSSpec::fixSpec(BSON("key" << BSON("$**" - << "text"))))); + FTSSpec(assertGet(FTSSpec::fixSpec(BSON("key" << BSON("$**" + << "text")))))); ASSERT(m.hasNegativeTerm(BSON("x" << BSON("y" << "pizza restaurant")))); @@ -79,8 +81,8 @@ TEST(FTSMatcher, Phrase1) { q.setDiacriticSensitive(false); ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK()); FTSMatcher m(q, - FTSSpec(FTSSpec::fixSpec(BSON("key" << BSON("$**" - << "text"))))); + FTSSpec(assertGet(FTSSpec::fixSpec(BSON("key" << BSON("$**" + << "text")))))); ASSERT(m.positivePhrasesMatch(BSON("x" << "table top"))); @@ -105,8 +107,8 @@ TEST(FTSMatcher, Phrase2) { q.setDiacriticSensitive(false); ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK()); FTSMatcher m(q, - FTSSpec(FTSSpec::fixSpec(BSON("key" << BSON("x" - << "text"))))); + FTSSpec(assertGet(FTSSpec::fixSpec(BSON("key" << BSON("x" + << "text")))))); ASSERT(m.positivePhrasesMatch(BSON("x" << BSON_ARRAY("table top")))); } @@ -120,8 +122,8 @@ TEST(FTSMatcher, ParsesUsingDocLanguage) { q.setDiacriticSensitive(false); ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK()); FTSMatcher m(q, - FTSSpec(FTSSpec::fixSpec(BSON("key" << BSON("x" - << "text"))))); + FTSSpec(assertGet(FTSSpec::fixSpec(BSON("key" << BSON("x" + << "text")))))); // Even though the search language is "none", the document {x: "gladly"} should be // parsed using the English stemmer, and as such should match the negated term "glad". @@ -138,8 +140,8 @@ TEST(FTSMatcher, MatcherDoesNotFilterStopWordsNeg) { q.setDiacriticSensitive(false); ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK()); FTSMatcher m(q, - FTSSpec(FTSSpec::fixSpec(BSON("key" << BSON("x" - << "text"))))); + FTSSpec(assertGet(FTSSpec::fixSpec(BSON("key" << BSON("x" + << "text")))))); ASSERT(m.hasNegativeTerm(BSON("x" << "the"))); @@ -154,8 +156,8 @@ TEST(FTSMatcher, MatcherDoesNotFilterStopWordsPos) { q.setDiacriticSensitive(false); ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK()); FTSMatcher m(q, - FTSSpec(FTSSpec::fixSpec(BSON("key" << BSON("x" - << "text"))))); + FTSSpec(assertGet(FTSSpec::fixSpec(BSON("key" << BSON("x" + << "text")))))); ASSERT(m.hasPositiveTerm(BSON("x" << "the"))); @@ -171,8 +173,8 @@ static bool docHasPositiveTermWithCase(const std::string& doc, const std::string q.setDiacriticSensitive(false); ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK()); FTSMatcher m(q, - FTSSpec(FTSSpec::fixSpec(BSON("key" << BSON("x" - << "text"))))); + FTSSpec(assertGet(FTSSpec::fixSpec(BSON("key" << BSON("x" + << "text")))))); return m.hasPositiveTerm(BSON("x" << doc)); } @@ -202,8 +204,8 @@ static bool docHasNegativeTermWithCase(const std::string& doc, const std::string q.setDiacriticSensitive(false); ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK()); FTSMatcher m(q, - FTSSpec(FTSSpec::fixSpec(BSON("key" << BSON("x" - << "text"))))); + FTSSpec(assertGet(FTSSpec::fixSpec(BSON("key" << BSON("x" + << "text")))))); return m.hasNegativeTerm(BSON("x" << doc)); } @@ -233,8 +235,8 @@ static bool docPositivePhrasesMatchWithCase(const std::string& doc, const std::s q.setDiacriticSensitive(false); ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK()); FTSMatcher m(q, - FTSSpec(FTSSpec::fixSpec(BSON("key" << BSON("x" - << "text"))))); + FTSSpec(assertGet(FTSSpec::fixSpec(BSON("key" << BSON("x" + << "text")))))); return m.positivePhrasesMatch(BSON("x" << doc)); } @@ -260,8 +262,8 @@ static bool docNegativePhrasesMatchWithCase(const std::string& doc, const std::s q.setDiacriticSensitive(false); ASSERT(q.parse(TEXT_INDEX_VERSION_3).isOK()); FTSMatcher m(q, - FTSSpec(FTSSpec::fixSpec(BSON("key" << BSON("x" - << "text"))))); + FTSSpec(assertGet(FTSSpec::fixSpec(BSON("key" << BSON("x" + << "text")))))); return m.negativePhrasesMatch(BSON("x" << doc)); } diff --git a/src/mongo/db/fts/fts_spec.cpp b/src/mongo/db/fts/fts_spec.cpp index 5663d338bc6..05248d157b7 100644 --- a/src/mongo/db/fts/fts_spec.cpp +++ b/src/mongo/db/fts/fts_spec.cpp @@ -259,14 +259,17 @@ void _addFTSStuff(BSONObjBuilder* b) { b->append("_ftsx", 1); } -void verifyFieldNameNotReserved(StringData s) { - uassert(17289, - "text index with reserved fields _fts/_ftsx not allowed", - s != "_fts" && s != "_ftsx"); +Status verifyFieldNameNotReserved(StringData s) { + if (s == "_fts" || s == "_ftsx") { + return {ErrorCodes::CannotCreateIndex, + "text index with reserved fields _fts/_ftsx not allowed"}; + } + + return Status::OK(); } } -BSONObj FTSSpec::fixSpec(const BSONObj& spec) { +StatusWith<BSONObj> FTSSpec::fixSpec(const BSONObj& spec) { if (spec["textIndexVersion"].numberInt() == TEXT_INDEX_VERSION_1) { return _fixSpecV1(spec); } @@ -284,11 +287,15 @@ BSONObj FTSSpec::fixSpec(const BSONObj& spec) { while (i.more()) { BSONElement e = i.next(); if (str::equals(e.fieldName(), "_fts")) { - uassert(17271, "expecting _fts:\"text\"", INDEX_NAME == e.valuestrsafe()); + if (INDEX_NAME != e.valuestrsafe()) { + return {ErrorCodes::CannotCreateIndex, "expecting _fts:\"text\""}; + } addedFtsStuff = true; b.append(e); } else if (str::equals(e.fieldName(), "_ftsx")) { - uassert(17272, "expecting _ftsx:1", e.numberInt() == 1); + if (e.numberInt() != 1) { + return {ErrorCodes::CannotCreateIndex, "expecting _ftsx:1"}; + } b.append(e); } else if (e.type() == String && INDEX_NAME == e.valuestr()) { if (!addedFtsStuff) { @@ -298,9 +305,10 @@ BSONObj FTSSpec::fixSpec(const BSONObj& spec) { m[e.fieldName()] = 1; } else { - uassert(17273, - "expected value 1 or -1 for non-text key in compound index", - e.numberInt() == 1 || e.numberInt() == -1); + if (e.numberInt() != 1 && e.numberInt() != -1) { + return {ErrorCodes::CannotCreateIndex, + "expected value 1 or -1 for non-text key in compound index"}; + } b.append(e); } } @@ -317,29 +325,50 @@ BSONObj FTSSpec::fixSpec(const BSONObj& spec) { // extraBefore fields while (String != e.type()) { - verifyFieldNameNotReserved(e.fieldNameStringData()); - verify(i.more()); + Status notReservedStatus = verifyFieldNameNotReserved(e.fieldNameStringData()); + if (!notReservedStatus.isOK()) { + return notReservedStatus; + } + + if (!i.more()) { + return {ErrorCodes::CannotCreateIndex, + "expected additional fields in text index key pattern"}; + } + e = i.next(); } // text fields bool alreadyFixed = str::equals(e.fieldName(), "_fts"); if (alreadyFixed) { - uassert(17288, "expected _ftsx after _fts", i.more()); + if (!i.more()) { + return {ErrorCodes::CannotCreateIndex, "expected _ftsx after _fts"}; + } e = i.next(); - uassert(17274, "expected _ftsx after _fts", str::equals(e.fieldName(), "_ftsx")); + if (!str::equals(e.fieldName(), "_ftsx")) { + return {ErrorCodes::CannotCreateIndex, "expected _ftsx after _fts"}; + } e = i.next(); } else { do { - verifyFieldNameNotReserved(e.fieldNameStringData()); + Status notReservedStatus = verifyFieldNameNotReserved(e.fieldNameStringData()); + if (!notReservedStatus.isOK()) { + return notReservedStatus; + } e = i.next(); } while (!e.eoo() && e.type() == String); } // extraAfterFields while (!e.eoo()) { - uassert(17389, "'text' fields in index must all be adjacent", e.type() != String); - verifyFieldNameNotReserved(e.fieldNameStringData()); + if (e.type() == BSONType::String) { + return {ErrorCodes::CannotCreateIndex, + "'text' fields in index must all be adjacent"}; + } + Status notReservedStatus = verifyFieldNameNotReserved(e.fieldNameStringData()); + if (!notReservedStatus.isOK()) { + return notReservedStatus; + } e = i.next(); } } @@ -349,36 +378,50 @@ BSONObj FTSSpec::fixSpec(const BSONObj& spec) { BSONObjIterator i(spec["weights"].Obj()); while (i.more()) { BSONElement e = i.next(); - uassert(17283, "weight for text index needs numeric type", e.isNumber()); + if (!e.isNumber()) { + return {ErrorCodes::CannotCreateIndex, "weight for text index needs numeric type"}; + } m[e.fieldName()] = e.numberInt(); } } else if (spec["weights"].str() == WILDCARD) { m[WILDCARD] = 1; } else if (!spec["weights"].eoo()) { - uasserted(17284, "text index option 'weights' must be an object"); + return {ErrorCodes::CannotCreateIndex, "text index option 'weights' must be an object"}; } - uassert(28823, "text index option 'weights' must specify fields or the wildcard", !m.empty()); + if (m.empty()) { + return {ErrorCodes::CannotCreateIndex, + "text index option 'weights' must specify fields or the wildcard"}; + } BSONObj weights; { BSONObjBuilder b; for (map<string, int>::iterator i = m.begin(); i != m.end(); ++i) { - uassert(16674, - str::stream() << "text index weight must be in the exclusive interval (0," - << MAX_WORD_WEIGHT << ") but found: " << i->second, - i->second > 0 && i->second < MAX_WORD_WEIGHT); + if (i->second <= 0 || i->second >= MAX_WORD_WEIGHT) { + return {ErrorCodes::CannotCreateIndex, + str::stream() << "text index weight must be in the exclusive interval (0," + << MAX_WORD_WEIGHT << ") but found: " << i->second}; + } // Verify weight refers to a valid field. if (i->first != "$**") { FieldRef keyField(i->first); - uassert(17294, "weight cannot be on an empty field", keyField.numParts() != 0); + if (keyField.numParts() == 0) { + return {ErrorCodes::CannotCreateIndex, "weight cannot be on an empty field"}; + } + for (size_t partNum = 0; partNum < keyField.numParts(); partNum++) { StringData part = keyField.getPart(partNum); - uassert(17291, "weight cannot have empty path component", !part.empty()); - uassert(17292, - "weight cannot have path component with $ prefix", - !part.startsWith("$")); + if (part.empty()) { + return {ErrorCodes::CannotCreateIndex, + "weight cannot have empty path component"}; + } + + if (part.startsWith("$")) { + return {ErrorCodes::CannotCreateIndex, + "weight cannot have path component with $ prefix"}; + } } } @@ -391,22 +434,22 @@ BSONObj FTSSpec::fixSpec(const BSONObj& spec) { string default_language(default_language_elt.str()); if (default_language_elt.eoo()) { default_language = moduleDefaultLanguage; - } else { - uassert( - 17263, "default_language needs a string type", default_language_elt.type() == String); + } else if (default_language_elt.type() != BSONType::String) { + return {ErrorCodes::CannotCreateIndex, "default_language needs a string type"}; + } + + if (!FTSLanguage::make(default_language, TEXT_INDEX_VERSION_3).getStatus().isOK()) { + return {ErrorCodes::CannotCreateIndex, "default_language is not valid"}; } - uassert(17264, - "default_language is not valid", - FTSLanguage::make(default_language, TEXT_INDEX_VERSION_3).getStatus().isOK()); BSONElement language_override_elt = spec["language_override"]; string language_override(language_override_elt.str()); if (language_override_elt.eoo()) { language_override = "language"; - } else { - uassert(17136, - "language_override is not valid", - language_override_elt.type() == String && validateOverride(language_override)); + } else if (language_override_elt.type() != BSONType::String) { + return {ErrorCodes::CannotCreateIndex, "language_override must be a string"}; + } else if (!validateOverride(language_override)) { + return {ErrorCodes::CannotCreateIndex, "language_override is not valid"}; } int version = -1; @@ -430,13 +473,17 @@ BSONObj FTSSpec::fixSpec(const BSONObj& spec) { } else if (str::equals(e.fieldName(), "v")) { version = e.numberInt(); } else if (str::equals(e.fieldName(), "textIndexVersion")) { - uassert(17293, "text index option 'textIndexVersion' must be a number", e.isNumber()); - textIndexVersion = e.numberInt(); - uassert(16730, - str::stream() << "bad textIndexVersion: " << textIndexVersion, - textIndexVersion == TEXT_INDEX_VERSION_2 || - textIndexVersion == TEXT_INDEX_VERSION_3); // supported indexes + if (!e.isNumber()) { + return {ErrorCodes::CannotCreateIndex, + "text index option 'textIndexVersion' must be a number"}; + } + textIndexVersion = e.numberInt(); + if (textIndexVersion != TEXT_INDEX_VERSION_2 && + textIndexVersion != TEXT_INDEX_VERSION_3) { + return {ErrorCodes::CannotCreateIndex, + str::stream() << "bad textIndexVersion: " << textIndexVersion}; + } } else { b.append(e); } diff --git a/src/mongo/db/fts/fts_spec.h b/src/mongo/db/fts/fts_spec.h index 6356f3a58a8..a00e04f7052 100644 --- a/src/mongo/db/fts/fts_spec.h +++ b/src/mongo/db/fts/fts_spec.h @@ -34,6 +34,7 @@ #include <vector> #include <string> +#include "mongo/base/status_with.h" #include "mongo/db/fts/fts_language.h" #include "mongo/db/fts/fts_util.h" #include "mongo/db/fts/stemmer.h" @@ -113,7 +114,7 @@ public: const Weights& weights() const { return _weights; } - static BSONObj fixSpec(const BSONObj& spec); + static StatusWith<BSONObj> fixSpec(const BSONObj& spec); /** * Returns text index version. @@ -164,7 +165,7 @@ private: const FTSLanguage& _getLanguageToUseV1(const BSONObj& userDoc) const; - static BSONObj _fixSpecV1(const BSONObj& spec); + static StatusWith<BSONObj> _fixSpecV1(const BSONObj& spec); // // Instance variables. diff --git a/src/mongo/db/fts/fts_spec_legacy.cpp b/src/mongo/db/fts/fts_spec_legacy.cpp index b7867dba520..15739d8787d 100644 --- a/src/mongo/db/fts/fts_spec_legacy.cpp +++ b/src/mongo/db/fts/fts_spec_legacy.cpp @@ -191,7 +191,7 @@ void FTSSpec::_scoreDocumentV1(const BSONObj& obj, TermFrequencyMap* term_freqs) } } -BSONObj FTSSpec::_fixSpecV1(const BSONObj& spec) { +StatusWith<BSONObj> FTSSpec::_fixSpecV1(const BSONObj& spec) { map<string, int> m; BSONObj keyPattern; @@ -238,10 +238,11 @@ BSONObj FTSSpec::_fixSpecV1(const BSONObj& spec) { { BSONObjBuilder b; for (map<string, int>::iterator i = m.begin(); i != m.end(); ++i) { - uassert(17365, - str::stream() << "text index weight must be in the exclusive interval (0," - << MAX_WORD_WEIGHT << ") but found: " << i->second, - i->second > 0 && i->second < MAX_WORD_WEIGHT); + if (i->second <= 0 || i->second >= MAX_WORD_WEIGHT) { + return {ErrorCodes::CannotCreateIndex, + str::stream() << "text index weight must be in the exclusive interval (0," + << MAX_WORD_WEIGHT << ") but found: " << i->second}; + } b.append(i->first, i->second); } weights = b.obj(); @@ -277,9 +278,10 @@ BSONObj FTSSpec::_fixSpecV1(const BSONObj& spec) { version = e.numberInt(); } else if (str::equals(e.fieldName(), "textIndexVersion")) { textIndexVersion = e.numberInt(); - uassert(17366, - str::stream() << "bad textIndexVersion: " << textIndexVersion, - textIndexVersion == 1); + if (textIndexVersion != 1) { + return {ErrorCodes::CannotCreateIndex, + str::stream() << "bad textIndexVersion: " << textIndexVersion}; + } } else { b.append(e); } diff --git a/src/mongo/db/fts/fts_spec_test.cpp b/src/mongo/db/fts/fts_spec_test.cpp index deca03a01e6..5ecc0109333 100644 --- a/src/mongo/db/fts/fts_spec_test.cpp +++ b/src/mongo/db/fts/fts_spec_test.cpp @@ -38,6 +38,7 @@ namespace mongo { using std::set; using std::string; +using unittest::assertGet; namespace fts { @@ -49,10 +50,10 @@ void assertFixSuccess(const std::string& s) { try { // fixSpec() should not throw on a valid spec. - BSONObj fixed = FTSSpec::fixSpec(user); + BSONObj fixed = assertGet(FTSSpec::fixSpec(user)); // fixSpec() on an already-fixed spec shouldn't change it. - BSONObj fixed2 = FTSSpec::fixSpec(fixed); + BSONObj fixed2 = assertGet(FTSSpec::fixSpec(fixed)); ASSERT_EQUALS(fixed, fixed2); } catch (UserException&) { ASSERT(false); @@ -64,14 +65,7 @@ void assertFixSuccess(const std::string& s) { */ void assertFixFailure(const std::string& s) { BSONObj user = fromjson(s); - - try { - // fixSpec() on an invalid spec should uassert. - BSONObj fixed = FTSSpec::fixSpec(user); - } catch (UserException&) { - return; - } - ASSERT(false); + ASSERT_NOT_OK(FTSSpec::fixSpec(user)); } TEST(FTSSpec, FixNormalKey1) { @@ -192,7 +186,7 @@ TEST(FTSSpec, ScoreSingleField1) { << "text" << "text") << "weights" << BSON("title" << 10)); - FTSSpec spec(FTSSpec::fixSpec(user)); + FTSSpec spec(assertGet(FTSSpec::fixSpec(user))); TermFrequencyMap m; spec.scoreDocument(BSON("title" @@ -210,7 +204,7 @@ TEST(FTSSpec, ScoreMultipleField1) { << "text" << "text") << "weights" << BSON("title" << 10)); - FTSSpec spec(FTSSpec::fixSpec(user)); + FTSSpec spec(assertGet(FTSSpec::fixSpec(user))); TermFrequencyMap m; spec.scoreDocument(BSON("title" @@ -236,7 +230,7 @@ TEST(FTSSpec, ScoreMultipleField2) { << "a.b" << "text")); - FTSSpec spec(FTSSpec::fixSpec(user)); + FTSSpec spec(assertGet(FTSSpec::fixSpec(user))); TermFrequencyMap m; spec.scoreDocument(BSON("a" << BSON("b" @@ -251,7 +245,7 @@ TEST(FTSSpec, ScoreRepeatWord) { << "text" << "text") << "weights" << BSON("title" << 10)); - FTSSpec spec(FTSSpec::fixSpec(user)); + FTSSpec spec(assertGet(FTSSpec::fixSpec(user))); TermFrequencyMap m; spec.scoreDocument(BSON("title" @@ -266,7 +260,7 @@ TEST(FTSSpec, ScoreRepeatWord) { TEST(FTSSpec, Extra1) { BSONObj user = BSON("key" << BSON("data" << "text")); - FTSSpec spec(FTSSpec::fixSpec(user)); + FTSSpec spec(assertGet(FTSSpec::fixSpec(user))); ASSERT_EQUALS(0U, spec.numExtraBefore()); ASSERT_EQUALS(0U, spec.numExtraAfter()); } @@ -275,20 +269,20 @@ TEST(FTSSpec, Extra2) { BSONObj user = BSON("key" << BSON("data" << "text" << "x" << 1)); - BSONObj fixed = FTSSpec::fixSpec(user); + BSONObj fixed = assertGet(FTSSpec::fixSpec(user)); FTSSpec spec(fixed); ASSERT_EQUALS(0U, spec.numExtraBefore()); ASSERT_EQUALS(1U, spec.numExtraAfter()); ASSERT_EQUALS(StringData("x"), spec.extraAfter(0)); - BSONObj fixed2 = FTSSpec::fixSpec(fixed); + BSONObj fixed2 = assertGet(FTSSpec::fixSpec(fixed)); ASSERT_EQUALS(fixed, fixed2); } TEST(FTSSpec, Extra3) { BSONObj user = BSON("key" << BSON("x" << 1 << "data" << "text")); - BSONObj fixed = FTSSpec::fixSpec(user); + BSONObj fixed = assertGet(FTSSpec::fixSpec(user)); ASSERT_EQUALS(BSON("x" << 1 << "_fts" << "text" @@ -296,7 +290,7 @@ TEST(FTSSpec, Extra3) { fixed["key"].Obj()); ASSERT_EQUALS(BSON("data" << 1), fixed["weights"].Obj()); - BSONObj fixed2 = FTSSpec::fixSpec(fixed); + BSONObj fixed2 = assertGet(FTSSpec::fixSpec(fixed)); ASSERT_EQUALS(fixed, fixed2); FTSSpec spec(fixed); @@ -323,7 +317,7 @@ TEST(FTSSpec, Extra3) { TEST(FTSSpec, NestedArraysPos1) { BSONObj user = BSON("key" << BSON("a.b" << "text")); - FTSSpec spec(FTSSpec::fixSpec(user)); + FTSSpec spec(assertGet(FTSSpec::fixSpec(user))); // The following document matches {"a.b": {$type: 2}}, so "term" should be indexed. BSONObj obj = fromjson("{a: [{b: ['term']}]}"); // indirectly nested arrays @@ -335,7 +329,7 @@ TEST(FTSSpec, NestedArraysPos1) { TEST(FTSSpec, NestedArraysPos2) { BSONObj user = BSON("key" << BSON("$**" << "text")); - FTSSpec spec(FTSSpec::fixSpec(user)); + FTSSpec spec(assertGet(FTSSpec::fixSpec(user))); // The wildcard spec implies a full recursive traversal, so "term" should be indexed. BSONObj obj = fromjson("{a: {b: [['term']]}}"); // directly nested arrays @@ -347,7 +341,7 @@ TEST(FTSSpec, NestedArraysPos2) { TEST(FTSSpec, NestedArraysNeg1) { BSONObj user = BSON("key" << BSON("a.b" << "text")); - FTSSpec spec(FTSSpec::fixSpec(user)); + FTSSpec spec(assertGet(FTSSpec::fixSpec(user))); // The following document does not match {"a.b": {$type: 2}}, so "term" should not be // indexed. @@ -361,7 +355,7 @@ TEST(FTSSpec, NestedArraysNeg1) { TEST(FTSSpec, NestedLanguages_PerArrayItemStemming) { BSONObj indexSpec = BSON("key" << BSON("a.b.c" << "text")); - FTSSpec spec(FTSSpec::fixSpec(indexSpec)); + FTSSpec spec(assertGet(FTSSpec::fixSpec(indexSpec))); TermFrequencyMap tfm; BSONObj obj = fromjson( @@ -390,7 +384,7 @@ TEST(FTSSpec, NestedLanguages_PerArrayItemStemming) { TEST(FTSSpec, NestedLanguages_PerSubdocStemming) { BSONObj indexSpec = BSON("key" << BSON("a.b.c" << "text")); - FTSSpec spec(FTSSpec::fixSpec(indexSpec)); + FTSSpec spec(assertGet(FTSSpec::fixSpec(indexSpec))); TermFrequencyMap tfm; BSONObj obj = fromjson( @@ -421,7 +415,7 @@ TEST(FTSSpec, NestedLanguages_PerSubdocStemming) { TEST(FTSSpec, NestedLanguages_NestedArrays) { BSONObj indexSpec = BSON("key" << BSON("a.b.c" << "text")); - FTSSpec spec(FTSSpec::fixSpec(indexSpec)); + FTSSpec spec(assertGet(FTSSpec::fixSpec(indexSpec))); TermFrequencyMap tfm; BSONObj obj = fromjson( @@ -452,7 +446,7 @@ TEST(FTSSpec, NestedLanguages_NestedArrays) { TEST(FTSSpec, NestedLanguages_PathPruning) { BSONObj indexSpec = BSON("key" << BSON("a.b.c" << "text")); - FTSSpec spec(FTSSpec::fixSpec(indexSpec)); + FTSSpec spec(assertGet(FTSSpec::fixSpec(indexSpec))); TermFrequencyMap tfm; BSONObj obj = fromjson( @@ -485,7 +479,7 @@ TEST(FTSSpec, NestedLanguages_PathPruning) { TEST(FTSSpec, NestedLanguages_Wildcard) { BSONObj indexSpec = BSON("key" << BSON("$**" << "text")); - FTSSpec spec(FTSSpec::fixSpec(indexSpec)); + FTSSpec spec(assertGet(FTSSpec::fixSpec(indexSpec))); TermFrequencyMap tfm; BSONObj obj = fromjson( @@ -519,7 +513,7 @@ TEST(FTSSpec, NestedLanguages_Wildcard) { TEST(FTSSpec, NestedLanguages_WildcardOverride) { BSONObj indexSpec = BSON("key" << BSON("$**" << "text") << "weights" << BSON("d.e.f" << 20)); - FTSSpec spec(FTSSpec::fixSpec(indexSpec)); + FTSSpec spec(assertGet(FTSSpec::fixSpec(indexSpec))); TermFrequencyMap tfm; BSONObj obj = fromjson( @@ -556,7 +550,7 @@ TEST(FTSSpec, TextIndexLegacyNestedArrays) { // textIndexVersion=1 FTSSpec objects do not index nested arrays. { BSONObj indexSpec = fromjson("{key: {'a.b': 'text'}, textIndexVersion: 1}"); - FTSSpec spec(FTSSpec::fixSpec(indexSpec)); + FTSSpec spec(assertGet(FTSSpec::fixSpec(indexSpec))); TermFrequencyMap tfm; spec.scoreDocument(obj, &tfm); ASSERT_EQUALS(tfm.size(), 0U); @@ -565,7 +559,7 @@ TEST(FTSSpec, TextIndexLegacyNestedArrays) { // textIndexVersion=2 FTSSpec objects do index nested arrays. { BSONObj indexSpec = fromjson("{key: {'a.b': 'text'}, textIndexVersion: 2}"); - FTSSpec spec(FTSSpec::fixSpec(indexSpec)); + FTSSpec spec(assertGet(FTSSpec::fixSpec(indexSpec))); TermFrequencyMap tfm; spec.scoreDocument(obj, &tfm); ASSERT_EQUALS(tfm.size(), 1U); @@ -580,7 +574,7 @@ TEST(FTSSpec, TextIndexLegacyLanguageRecognition) { // for purposes of stopword processing. { BSONObj indexSpec = fromjson("{key: {'a': 'text'}, textIndexVersion: 1}"); - FTSSpec spec(FTSSpec::fixSpec(indexSpec)); + FTSSpec spec(assertGet(FTSSpec::fixSpec(indexSpec))); TermFrequencyMap tfm; spec.scoreDocument(obj, &tfm); ASSERT_EQUALS(tfm.size(), 1U); // "the" not recognized as stopword @@ -589,7 +583,7 @@ TEST(FTSSpec, TextIndexLegacyLanguageRecognition) { // textIndexVersion=2 FTSSpec objects recognize two-letter codes. { BSONObj indexSpec = fromjson("{key: {'a': 'text'}, textIndexVersion: 2}"); - FTSSpec spec(FTSSpec::fixSpec(indexSpec)); + FTSSpec spec(assertGet(FTSSpec::fixSpec(indexSpec))); TermFrequencyMap tfm; spec.scoreDocument(obj, &tfm); ASSERT_EQUALS(tfm.size(), 0U); // "the" recognized as stopword diff --git a/src/mongo/db/index/s2_access_method.cpp b/src/mongo/db/index/s2_access_method.cpp index c6519519b44..a05f72855dc 100644 --- a/src/mongo/db/index/s2_access_method.cpp +++ b/src/mongo/db/index/s2_access_method.cpp @@ -80,7 +80,7 @@ S2AccessMethod::S2AccessMethod(IndexCatalogEntry* btreeState, SortedDataInterfac } // static -BSONObj S2AccessMethod::fixSpec(const BSONObj& specObj) { +StatusWith<BSONObj> S2AccessMethod::fixSpec(const BSONObj& specObj) { // If the spec object has the field "2dsphereIndexVersion", validate it. If it doesn't, add // {2dsphereIndexVersion: 3}, which is the default for newly-built indexes. @@ -92,22 +92,33 @@ BSONObj S2AccessMethod::fixSpec(const BSONObj& specObj) { return bob.obj(); } - uassert(40108, - str::stream() << "Invalid type or value for geo index version { " - << kIndexVersionFieldName << " : " << indexVersionElt - << " }, only versions: [" << S2_INDEX_VERSION_1 << "," - << S2_INDEX_VERSION_2 << "," << S2_INDEX_VERSION_3 << "] are supported", - indexVersionElt.isNumber() && ((indexVersionElt.type() != mongo::NumberDouble) || - std::isnormal(indexVersionElt.numberDouble()))); + if (!indexVersionElt.isNumber()) { + return {ErrorCodes::CannotCreateIndex, + str::stream() << "Invalid type for geo index version { " << kIndexVersionFieldName + << " : " << indexVersionElt << " }, only versions: [" + << S2_INDEX_VERSION_1 << "," << S2_INDEX_VERSION_2 << "," + << S2_INDEX_VERSION_3 << "] are supported"}; + } + + if (indexVersionElt.type() == BSONType::NumberDouble && + !std::isnormal(indexVersionElt.numberDouble())) { + return {ErrorCodes::CannotCreateIndex, + str::stream() << "Invalid value for geo index version { " << kIndexVersionFieldName + << " : " << indexVersionElt << " }, only versions: [" + << S2_INDEX_VERSION_1 << "," << S2_INDEX_VERSION_2 << "," + << S2_INDEX_VERSION_3 << "] are supported"}; + } const auto indexVersion = indexVersionElt.numberLong(); + if (indexVersion != S2_INDEX_VERSION_1 && indexVersion != S2_INDEX_VERSION_2 && + indexVersion != S2_INDEX_VERSION_3) { + return {ErrorCodes::CannotCreateIndex, + str::stream() << "unsupported geo index version { " << kIndexVersionFieldName + << " : " << indexVersionElt << " }, only versions: [" + << S2_INDEX_VERSION_1 << "," << S2_INDEX_VERSION_2 << "," + << S2_INDEX_VERSION_3 << "] are supported"}; + } - uassert(17394, - str::stream() << "unsupported geo index version { " << kIndexVersionFieldName << " : " - << indexVersionElt << " }, only versions: [" << S2_INDEX_VERSION_1 << "," - << S2_INDEX_VERSION_2 << "," << S2_INDEX_VERSION_3 << "] are supported", - (indexVersion == S2_INDEX_VERSION_3 || indexVersion == S2_INDEX_VERSION_2 || - indexVersion == S2_INDEX_VERSION_1)); return specObj; } diff --git a/src/mongo/db/index/s2_access_method.h b/src/mongo/db/index/s2_access_method.h index 7bc819adcce..3d39e10fcc9 100644 --- a/src/mongo/db/index/s2_access_method.h +++ b/src/mongo/db/index/s2_access_method.h @@ -29,6 +29,7 @@ #pragma once #include "mongo/base/status.h" +#include "mongo/base/status_with.h" #include "mongo/db/index/index_access_method.h" #include "mongo/db/index/index_descriptor.h" #include "mongo/db/index/s2_common.h" @@ -44,9 +45,11 @@ public: * Takes an index spec object for this index and returns a copy tweaked to conform to the * expected format. When an index build is initiated, this function is called on the spec * object the user provides, and the return value of this function is the final spec object - * that gets saved in the index catalog. Throws a UserException if 'specObj' is invalid. + * that gets saved in the index catalog. + * + * Returns a non-OK status if 'specObj' is invalid. */ - static BSONObj fixSpec(const BSONObj& specObj); + static StatusWith<BSONObj> fixSpec(const BSONObj& specObj); private: /** diff --git a/src/mongo/db/index_legacy.cpp b/src/mongo/db/index_legacy.cpp index e9d19cc511d..f477b562927 100644 --- a/src/mongo/db/index_legacy.cpp +++ b/src/mongo/db/index_legacy.cpp @@ -40,7 +40,7 @@ namespace mongo { // static -BSONObj IndexLegacy::adjustIndexSpecObject(const BSONObj& obj) { +StatusWith<BSONObj> IndexLegacy::adjustIndexSpecObject(const BSONObj& obj) { std::string pluginName = IndexNames::findPluginName(obj.getObjectField("key")); if (IndexNames::TEXT == pluginName) { diff --git a/src/mongo/db/index_legacy.h b/src/mongo/db/index_legacy.h index 19640c02e71..e1408a16dd3 100644 --- a/src/mongo/db/index_legacy.h +++ b/src/mongo/db/index_legacy.h @@ -29,6 +29,7 @@ #pragma once +#include "mongo/base/status_with.h" #include "mongo/db/jsobj.h" namespace mongo { @@ -54,7 +55,7 @@ public: * GEO_2DSPHERE provide additional validation on the index spec, and tweak the index spec * object to conform to their expected format. */ - static BSONObj adjustIndexSpecObject(const BSONObj& obj); + static StatusWith<BSONObj> adjustIndexSpecObject(const BSONObj& obj); /** * Returns the BSONObj that is inserted into an index when the object is missing the keys |