/** * Copyright (C) 2014 MongoDB Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects for * all of the code used other than as permitted herein. If you modify file(s) * with this exception, you may extend this exception to your version of the * file(s), but you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. If you delete this * exception statement from all source files in the program, then also delete * it in the license file. */ #include "mongo/db/index/btree_key_generator.h" #include #include "mongo/db/json.h" #include "mongo/unittest/unittest.h" using namespace mongo; using std::unique_ptr; using std::cout; using std::endl; using std::vector; namespace { // // Helper functions // std::string dumpKeyset(const BSONObjSet& objs) { std::stringstream ss; ss << "[ "; for (BSONObjSet::iterator i = objs.begin(); i != objs.end(); ++i) { ss << i->toString() << " "; } ss << "]"; return ss.str(); } bool keysetsMatch(const BSONObjSet& expected, const BSONObjSet& actual) { if (expected.size() != actual.size()) { return false; } for (BSONObjSet::iterator i = expected.begin(); i != expected.end(); ++i) { if (actual.end() == actual.find(*i)) { return false; } } return true; } bool testKeygen(const BSONObj& kp, const BSONObj& obj, const BSONObjSet& expectedKeys, bool sparse = false) { // // Step 1: construct the btree key generator object, using the // index key pattern. // vector fieldNames; vector fixed; BSONObjIterator it(kp); while (it.more()) { BSONElement elt = it.next(); fieldNames.push_back(elt.fieldName()); fixed.push_back(BSONElement()); } unique_ptr keyGen(new BtreeKeyGeneratorV1(fieldNames, fixed, sparse)); // // Step 2: ask 'keyGen' to generate index keys for the object 'obj'. // BSONObjSet actualKeys; keyGen->getKeys(obj, &actualKeys); // // Step 3: check that the results match the expected result. // bool match = keysetsMatch(expectedKeys, actualKeys); if (!match) { cout << "Expected: " << dumpKeyset(expectedKeys) << ", " << "Actual: " << dumpKeyset(actualKeys) << endl; } return match; } // // Unit tests // TEST(BtreeKeyGeneratorTest, GetKeysFromObjectSimple) { BSONObj keyPattern = fromjson("{a: 1}"); BSONObj genKeysFrom = fromjson("{b: 4, a: 5}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 5}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysFromObjectDotted) { BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a: {b: 4}, c: 'foo'}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 4}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysFromArraySimple) { BSONObj keyPattern = fromjson("{a: 1}"); BSONObj genKeysFrom = fromjson("{a: [1, 2, 3]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 1}")); expectedKeys.insert(fromjson("{'': 2}")); expectedKeys.insert(fromjson("{'': 3}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysFromArrayFirstElement) { BSONObj keyPattern = fromjson("{a: 1, b: 1}"); BSONObj genKeysFrom = fromjson("{a: [1, 2, 3], b: 2}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 1, '': 2}")); expectedKeys.insert(fromjson("{'': 2, '': 2}")); expectedKeys.insert(fromjson("{'': 3, '': 2}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysFromArraySecondElement) { BSONObj keyPattern = fromjson("{first: 1, a: 1}"); BSONObj genKeysFrom = fromjson("{first: 5, a: [1, 2, 3]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 5, '': 1}")); expectedKeys.insert(fromjson("{'': 5, '': 2}")); expectedKeys.insert(fromjson("{'': 5, '': 3}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysFromSecondLevelArray) { BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a: {b: [1, 2, 3]}}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 1}")); expectedKeys.insert(fromjson("{'': 2}")); expectedKeys.insert(fromjson("{'': 3}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysFromParallelArraysBasic) { BSONObj keyPattern = fromjson("{'a': 1, 'b': 1}"); BSONObj genKeysFrom = fromjson("{a: [1, 2, 3], b: [1, 2, 3]}}"); BSONObjSet expectedKeys; ASSERT_THROWS(testKeygen(keyPattern, genKeysFrom, expectedKeys), UserException); } TEST(BtreeKeyGeneratorTest, GetKeysFromArraySubobjectBasic) { BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a: [{b:1,c:4}, {b:2,c:4}, {b:3,c:4}]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 1}")); expectedKeys.insert(fromjson("{'': 2}")); expectedKeys.insert(fromjson("{'': 3}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysArraySubobjectCompoundIndex) { BSONObj keyPattern = fromjson("{'a.b': 1, d: 99}"); BSONObj genKeysFrom = fromjson("{a: [{b:1,c:4}, {b:2,c:4}, {b:3,c:4}], d: 99}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 1, '': 99}")); expectedKeys.insert(fromjson("{'': 2, '': 99}")); expectedKeys.insert(fromjson("{'': 3, '': 99}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysArraySubobjectSingleMissing) { BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a: [{foo: 41}, {b:1,c:4}, {b:2,c:4}, {b:3,c:4}]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': null}")); expectedKeys.insert(fromjson("{'': 1}")); expectedKeys.insert(fromjson("{'': 2}")); expectedKeys.insert(fromjson("{'': 3}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysFromArraySubobjectMissing) { BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a: [{foo: 41}, {foo: 41}, {foo: 41}]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': null}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysMissingField) { BSONObj keyPattern = fromjson("{a: 1}"); BSONObj genKeysFrom = fromjson("{b: 1}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': null}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysSubobjectMissing) { BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a: [1, 2]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': null}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysFromCompound) { BSONObj keyPattern = fromjson("{x: 1, y: 1}"); BSONObj genKeysFrom = fromjson("{x: 'a', y: 'b'}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 'a', '': 'b'}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysFromCompoundMissing) { BSONObj keyPattern = fromjson("{x: 1, y: 1}"); BSONObj genKeysFrom = fromjson("{x: 'a'}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 'a', '': null}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysFromArraySubelementComplex) { BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a:[{b:[2]}]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 2}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysFromParallelArraysComplex) { BSONObj keyPattern = fromjson("{'a.b': 1, 'a.c': 1}"); BSONObj genKeysFrom = fromjson("{a:[{b:[1],c:[2]}]}"); BSONObjSet expectedKeys; ASSERT_THROWS(testKeygen(keyPattern, genKeysFrom, expectedKeys), UserException); } TEST(BtreeKeyGeneratorTest, GetKeysAlternateMissing) { BSONObj keyPattern = fromjson("{'a.b': 1, 'a.c': 1}"); BSONObj genKeysFrom = fromjson("{a:[{b:1},{c:2}]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': null, '': 2}")); expectedKeys.insert(fromjson("{'': 1, '': null}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysFromMultiComplex) { BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a:[{b:1},{b:[1,2,3]}]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 1}")); expectedKeys.insert(fromjson("{'': 2}")); expectedKeys.insert(fromjson("{'': 3}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysArrayEmpty) { BSONObj keyPattern = fromjson("{a: 1}"); BSONObj genKeysFrom = fromjson("{a:[1,2]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 1}")); expectedKeys.insert(fromjson("{'': 2}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); genKeysFrom = fromjson("{a: [1]}"); expectedKeys.clear(); expectedKeys.insert(fromjson("{'': 1}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); genKeysFrom = fromjson("{a: null}"); expectedKeys.clear(); expectedKeys.insert(fromjson("{'': null}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); genKeysFrom = fromjson("{a: []}"); expectedKeys.clear(); expectedKeys.insert(fromjson("{'': undefined}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysFromDoubleArray) { BSONObj keyPattern = fromjson("{a: 1, a: 1}"); BSONObj genKeysFrom = fromjson("{a:[1,2]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 1, '': 1}")); expectedKeys.insert(fromjson("{'': 2, '': 2}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysFromDoubleEmptyArray) { BSONObj keyPattern = fromjson("{a: 1, a: 1}"); BSONObj genKeysFrom = fromjson("{a:[]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': undefined, '': undefined}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysFromMultiEmptyArray) { BSONObj keyPattern = fromjson("{a: 1, b: 1}"); BSONObj genKeysFrom = fromjson("{a: 1, b: [1, 2]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 1, '': 1}")); expectedKeys.insert(fromjson("{'': 1, '': 2}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); genKeysFrom = fromjson("{a: 1, b: [1]}"); expectedKeys.clear(); expectedKeys.insert(fromjson("{'': 1, '': 1}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); genKeysFrom = fromjson("{a: 1, b: []}"); expectedKeys.clear(); expectedKeys.insert(fromjson("{'': 1, '': undefined}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysFromNestedEmptyArray) { BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a:[]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': null}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysFromMultiNestedEmptyArray) { BSONObj keyPattern = fromjson("{'a.b': 1, 'a.c': 1}"); BSONObj genKeysFrom = fromjson("{a:[]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': null, '': null}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysFromUnevenNestedEmptyArray) { BSONObj keyPattern = fromjson("{'a': 1, 'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a:[]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': undefined, '': null}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); genKeysFrom = fromjson("{a:[{b: 1}]}"); expectedKeys.clear(); expectedKeys.insert(fromjson("{'': {b:1}, '': 1}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); genKeysFrom = fromjson("{a:[{b: []}]}"); expectedKeys.clear(); expectedKeys.insert(fromjson("{'': {b:[]}, '': undefined}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysFromReverseUnevenNestedEmptyArray) { BSONObj keyPattern = fromjson("{'a.b': 1, 'a': 1}"); BSONObj genKeysFrom = fromjson("{a:[]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': null, '': undefined}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, SparseReverseUnevenNestedEmptyArray) { BSONObj keyPattern = fromjson("{'a.b': 1, 'a': 1}"); BSONObj genKeysFrom = fromjson("{a:[]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': null, '': undefined}")); // true means sparse ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, true)); } TEST(BtreeKeyGeneratorTest, GetKeysFromSparseEmptyArray) { BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a:1}"); BSONObjSet expectedKeys; // true means sparse ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, true)); genKeysFrom = fromjson("{a:[]}"); // true means sparse ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, true)); genKeysFrom = fromjson("{a:[{c:1}]}"); // true means sparse ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, true)); } TEST(BtreeKeyGeneratorTest, GetKeysFromSparseEmptyArraySecond) { BSONObj keyPattern = fromjson("{z: 1, 'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a:1}"); BSONObjSet expectedKeys; // true means sparse ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, true)); genKeysFrom = fromjson("{a:[]}"); // true means sparse ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, true)); genKeysFrom = fromjson("{a:[{c:1}]}"); // true means sparse ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, true)); } TEST(BtreeKeyGeneratorTest, SparseNonObjectMissingNestedField) { BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a:[]}"); BSONObjSet expectedKeys; // true means sparse ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, true)); genKeysFrom = fromjson("{a:[1]}"); // true means sparse ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, true)); genKeysFrom = fromjson("{a:[1,{b:1}]}"); expectedKeys.clear(); expectedKeys.insert(fromjson("{'': 1}")); // true means sparse ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys, true)); } TEST(BtreeKeyGeneratorTest, GetKeysFromIndexedArrayIndex) { BSONObj keyPattern = fromjson("{'a.0': 1}"); BSONObj genKeysFrom = fromjson("{a:[1]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 1}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); genKeysFrom = fromjson("{a:[[1]]}"); expectedKeys.clear(); expectedKeys.insert(fromjson("{'': [1]}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); genKeysFrom = fromjson("{a:[[]]}"); expectedKeys.clear(); expectedKeys.insert(fromjson("{'': undefined}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); genKeysFrom = fromjson("{a:[[]]}"); expectedKeys.clear(); expectedKeys.insert(fromjson("{'': undefined}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); genKeysFrom = fromjson("{a:{'0':1}}"); expectedKeys.clear(); expectedKeys.insert(fromjson("{'': 1}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); genKeysFrom = fromjson("{a:[{'0':1}]}"); expectedKeys.clear(); ASSERT_THROWS(testKeygen(keyPattern, genKeysFrom, expectedKeys), UserException); genKeysFrom = fromjson("{a:[1,{'0':2}]}"); ASSERT_THROWS(testKeygen(keyPattern, genKeysFrom, expectedKeys), UserException); } TEST(BtreeKeyGeneratorTest, GetKeysFromDoubleIndexedArrayIndex) { BSONObj keyPattern = fromjson("{'a.0.0': 1}"); BSONObj genKeysFrom = fromjson("{a:[[1]]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 1}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); genKeysFrom = fromjson("{a:[[]]}"); expectedKeys.clear(); expectedKeys.insert(fromjson("{'': null}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); genKeysFrom = fromjson("{a:[]}"); expectedKeys.clear(); expectedKeys.insert(fromjson("{'': null}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); genKeysFrom = fromjson("{a:[[[]]]}"); expectedKeys.clear(); expectedKeys.insert(fromjson("{'': undefined}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysFromObjectWithinArray) { BSONObj keyPattern = fromjson("{'a.0.b': 1}"); BSONObj genKeysFrom = fromjson("{a:[{b:1}]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 1}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); genKeysFrom = fromjson("{a:[{b:[1]}]}"); expectedKeys.clear(); expectedKeys.insert(fromjson("{'': 1}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); genKeysFrom = fromjson("{a:[{b:[[1]]}]}"); expectedKeys.clear(); expectedKeys.insert(fromjson("{'': [1]}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); genKeysFrom = fromjson("{a:[[{b:1}]]}"); expectedKeys.clear(); expectedKeys.insert(fromjson("{'': 1}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); genKeysFrom = fromjson("{a:[[{b:[1]}]]}"); expectedKeys.clear(); expectedKeys.insert(fromjson("{'': 1}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); genKeysFrom = fromjson("{a:[[{b:[[1]]}]]}"); expectedKeys.clear(); expectedKeys.insert(fromjson("{'': [1]}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); genKeysFrom = fromjson("{a:[[{b:[]}]]}"); expectedKeys.clear(); expectedKeys.insert(fromjson("{'': undefined}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysFromArrayWithinObjectWithinArray) { BSONObj keyPattern = fromjson("{'a.0.b.0': 1}"); BSONObj genKeysFrom = fromjson("{a:[{b:[1]}]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 1}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, ParallelArraysInNestedObjects) { BSONObj keyPattern = fromjson("{'a.a': 1, 'b.a': 1}"); BSONObj genKeysFrom = fromjson("{a:{a:[1]}, b:{a:[1]}}"); BSONObjSet expectedKeys; ASSERT_THROWS(testKeygen(keyPattern, genKeysFrom, expectedKeys), UserException); } TEST(BtreeKeyGeneratorTest, ParallelArraysUneven) { BSONObj keyPattern = fromjson("{'b.a': 1, 'a': 1}"); BSONObj genKeysFrom = fromjson("{b:{a:[1]}, a:[1,2]}"); BSONObjSet expectedKeys; ASSERT_THROWS(testKeygen(keyPattern, genKeysFrom, expectedKeys), UserException); } TEST(BtreeKeyGeneratorTest, MultipleArraysNotParallel) { BSONObj keyPattern = fromjson("{'a.b.c': 1}"); BSONObj genKeysFrom = fromjson("{a: [1, 2, {b: {c: [3, 4]}}]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': null}")); expectedKeys.insert(fromjson("{'': 3}")); expectedKeys.insert(fromjson("{'': 4}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, MultipleArraysNotParallelCompound) { BSONObj keyPattern = fromjson("{'a.b.c': 1, 'a.b.d': 1}"); BSONObj genKeysFrom = fromjson("{a: [1, 2, {b: {c: [3, 4], d: 5}}]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': null, '': null}")); expectedKeys.insert(fromjson("{'': 3, '': 5}")); expectedKeys.insert(fromjson("{'': 4, '': 5}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysComplexNestedArrays) { BSONObj keyPattern = fromjson("{'a.b.c.d': 1, 'a.g': 1, 'a.b.f': 1, 'a.b.c': 1, 'a.b.e': 1}"); BSONObj genKeysFrom = fromjson("{a: [1, {b: [2, {c: [3, {d: 1}], e: 4}, 5, {f: 6}], g: 7}]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'':null, '':null, '':null, '':null, '':null}")); expectedKeys.insert(fromjson("{'':null, '':7, '':null, '':null, '':null}")); expectedKeys.insert(fromjson("{'':null, '':7, '':null, '':3, '':4}")); expectedKeys.insert(fromjson("{'':null, '':7, '':6, '':null, '':null}")); expectedKeys.insert(fromjson("{'':1, '':7, '':null, '':{d: 1}, '':4}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } // Descriptive test. Should future index versions recursively index nested arrays? TEST(BtreeKeyGeneratorTest, GetKeys2DArray) { BSONObj keyPattern = fromjson("{a: 1}"); BSONObj genKeysFrom = fromjson("{a: [[2]]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': [2]}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } // Descriptive test. Should parallel indexed arrays be allowed? If not, should empty // or single-element arrays be considered for the parallel array check? TEST(BtreeKeyGeneratorTest, GetKeysParallelEmptyArrays) { BSONObj keyPattern = fromjson("{a: 1, b: 1}"); BSONObj genKeysFrom = fromjson("{a: [], b: []}"); BSONObjSet expectedKeys; ASSERT_THROWS(testKeygen(keyPattern, genKeysFrom, expectedKeys), UserException); } TEST(BtreeKeyGeneratorTest, GetKeysParallelArraysOneArrayEmpty) { BSONObj keyPattern = fromjson("{a: 1, b: 1}"); BSONObj genKeysFrom = fromjson("{a: [], b: [1, 2, 3]}"); BSONObjSet expectedKeys; ASSERT_THROWS(testKeygen(keyPattern, genKeysFrom, expectedKeys), UserException); } TEST(BtreeKeyGeneratorTest, GetKeysParallelArraysOneArrayEmptyNested) { BSONObj keyPattern = fromjson("{'a.b.c': 1, 'a.b.d': 1}"); BSONObj genKeysFrom = fromjson("{a: [{b: [{c: [1, 2, 3], d: []}]}]}"); BSONObjSet expectedKeys; ASSERT_THROWS(testKeygen(keyPattern, genKeysFrom, expectedKeys), UserException); } // Descriptive test. The semantics for key generation are odd for positional key patterns. TEST(BtreeKeyGeneratorTest, GetKeysPositionalKeyPatternMissingElement) { BSONObj keyPattern = fromjson("{'a.2': 1}"); BSONObj genKeysFrom = fromjson("{a: [{'2': 5}]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 5}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } // Descriptive test. The semantics for key generation are odd for positional key patterns. TEST(BtreeKeyGeneratorTest, GetKeysPositionalKeyPatternNestedArray) { BSONObj keyPattern = fromjson("{'a.2': 1}"); BSONObj genKeysFrom = fromjson("{a: [[1, 2, 5]]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': null}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } // Descriptive test. The semantics for key generation are odd for positional key patterns. TEST(BtreeKeyGeneratorTest, GetKeysPositionalKeyPatternNestedArray2) { BSONObj keyPattern = fromjson("{'a.2': 1}"); BSONObj genKeysFrom = fromjson("{a: [[1, 2, 5], [3, 4, 6], [0, 1, 2]]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': [0, 1, 2]}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } // Descriptive test. The semantics for key generation are odd for positional key patterns. TEST(BtreeKeyGeneratorTest, GetKeysPositionalKeyPatternNestedArray3) { BSONObj keyPattern = fromjson("{'a.2': 1}"); BSONObj genKeysFrom = fromjson("{a: [{'0': 1, '1': 2, '2': 5}]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 5}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } // Descriptive test. The semantics for key generation are odd for positional key patterns. TEST(BtreeKeyGeneratorTest, GetKeysPositionalKeyPatternNestedArray4) { BSONObj keyPattern = fromjson("{'a.b.2': 1}"); BSONObj genKeysFrom = fromjson("{a: [{b: [[1, 2, 5]]}]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': null}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } // Descriptive test. The semantics for key generation are odd for positional key patterns. TEST(BtreeKeyGeneratorTest, GetKeysPositionalKeyPatternNestedArray5) { BSONObj keyPattern = fromjson("{'a.2': 1}"); BSONObj genKeysFrom = fromjson("{a: [[1, 2, 5], {'2': 6}]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': null}")); expectedKeys.insert(fromjson("{'': 6}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetNullKeyNestedArray) { BSONObj keyPattern = fromjson("{'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a: [[1, 2, 5]]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': null}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysUnevenNestedArrays) { BSONObj keyPattern = fromjson("{a: 1, 'a.b': 1}"); BSONObj genKeysFrom = fromjson("{a: [1, {b: [2, 3, 4]}]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 1, '': null}")); expectedKeys.insert(fromjson("{'': {b:[2,3,4]}, '': 2}")); expectedKeys.insert(fromjson("{'': {b:[2,3,4]}, '': 3}")); expectedKeys.insert(fromjson("{'': {b:[2,3,4]}, '': 4}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } // Descriptive test. Should we define better semantics for future index versions in the case of // repeated field names? TEST(BtreeKeyGeneratorTest, GetKeysRepeatedFieldName) { BSONObj keyPattern = fromjson("{a: 1}"); BSONObj genKeysFrom = fromjson("{a: 2, a: 3}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 2}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } // Descriptive test. Future index versions may want different or at least more consistent // handling of empty path components. TEST(BtreeKeyGeneratorTest, GetKeysEmptyPathPiece) { BSONObj keyPattern = fromjson("{'a..c': 1}"); BSONObj genKeysFrom = fromjson("{a: {'': [{c: 1}, {c: 2}]}}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 1}")); expectedKeys.insert(fromjson("{'': 2}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } // Descriptive test. Future index versions may want different or at least more consistent // handling of empty path components. TEST(BtreeKeyGeneratorTest, GetKeysLastPathPieceEmpty) { BSONObj keyPattern = fromjson("{'a.': 1}"); BSONObj genKeysFrom = fromjson("{a: 2}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 2}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); genKeysFrom = fromjson("{a: {'': 2}}"); expectedKeys.clear(); expectedKeys.insert(fromjson("{'': {'': 2}}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysFirstPathPieceEmpty) { BSONObj keyPattern = fromjson("{'.a': 1}"); BSONObj genKeysFrom = fromjson("{a: 2}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': null}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, GetKeysFirstPathPieceEmpty2) { BSONObj keyPattern = fromjson("{'.a': 1}"); BSONObj genKeysFrom = fromjson("{'': [{a: [1, 2, 3]}]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 1}")); expectedKeys.insert(fromjson("{'': 2}")); expectedKeys.insert(fromjson("{'': 3}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, PositionalKeyPatternParallelArrays) { BSONObj keyPattern = fromjson("{a: 1, 'b.0': 1}"); BSONObj genKeysFrom = fromjson("{a: [1], b: [2]}"); BSONObjSet expectedKeys; ASSERT_THROWS(testKeygen(keyPattern, genKeysFrom, expectedKeys), UserException); } // Descriptive test. TEST(BtreeKeyGeneratorTest, PositionalKeyPatternNestedArrays) { BSONObj keyPattern = fromjson("{'a.0.b': 1}"); BSONObj genKeysFrom = fromjson("{a: [[{b: 1}]]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 1}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } // Descriptive test. TEST(BtreeKeyGeneratorTest, PositionalKeyPatternNestedArrays2) { BSONObj keyPattern = fromjson("{'a.0.0.b': 1}"); BSONObj genKeysFrom = fromjson("{a: [[{b: 1}]]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 1}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } // Descriptive test. TEST(BtreeKeyGeneratorTest, PositionalKeyPatternNestedArrays3) { BSONObj keyPattern = fromjson("{'a.0.0.b': 1}"); BSONObj genKeysFrom = fromjson("{a: [[[ {b: 1} ]]]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 1}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } // Descriptive test. TEST(BtreeKeyGeneratorTest, PositionalKeyPatternNestedArrays4) { BSONObj keyPattern = fromjson("{'a.0.0.b': 1}"); BSONObj genKeysFrom = fromjson("{a: [[[[ {b: 1} ]]]]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': null}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } TEST(BtreeKeyGeneratorTest, PositionalKeyPatternNestedArrays5) { BSONObj keyPattern = fromjson("{'a.b.1': 1}"); BSONObj genKeysFrom = fromjson("{a: [{b: [1, 2]}]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': 2}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } // Descriptive test. TEST(BtreeKeyGeneratorTest, PositionalKeyPatternNestedArrays6) { BSONObj keyPattern = fromjson("{'a': 1, 'a.b': 1, 'a.0.b':1, 'a.b.0': 1, 'a.0.b.0': 1}"); BSONObj genKeysFrom = fromjson("{a: [{b: [1,2]}, {b: 3}]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': {b:3}, '': 3, '': 1, '': null, '': 1}")); expectedKeys.insert(fromjson("{'': {b:3}, '': 3, '': 2, '': null, '': 1}")); expectedKeys.insert(fromjson("{'': {b:[1,2]}, '': 1, '': 1, '': 1, '': 1}")); expectedKeys.insert(fromjson("{'': {b:[1,2]}, '': 2, '': 2, '': 1, '': 1}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } // Descriptive test. TEST(BtreeKeyGeneratorTest, PositionalKeyPatternNestedArrays7) { BSONObj keyPattern = fromjson("{'a': 1, 'a.b': 1, 'a.0.b':1, 'a.b.0': 1, 'a.0.b.0': 1}"); BSONObj genKeysFrom = fromjson("{a: [{b: [1,2]}, {b: {'0': 3}}]}"); BSONObjSet expectedKeys; expectedKeys.insert(fromjson("{'': {b:{'0':3}}, '': {'0':3}, '': 1, '': 3, '': 1}")); expectedKeys.insert(fromjson("{'': {b:{'0':3}}, '': {'0':3}, '': 2, '': 3, '': 1}")); expectedKeys.insert(fromjson("{'': {b:[1,2]}, '': 1, '': 1, '': 1, '': 1}")); expectedKeys.insert(fromjson("{'': {b:[1,2]}, '': 2, '': 2, '': 1, '': 1}")); ASSERT(testKeygen(keyPattern, genKeysFrom, expectedKeys)); } } // namespace