bool TypeMatchExpression::matches(const MatchableDocument* doc, MatchDetails* details) const { MatchableDocument::IteratorHolder cursor(doc, &_elementPath); while (cursor->more()) { ElementIterator::Context e = cursor->next(); // In the case where _elementPath is referring to an array, // $type should match elements of that array only. // outerArray() helps to identify elements of the array // and the containing array itself. // This matters when we are looking for {$type: Array}. // Example (_elementPath refers to field 'a' and _type is Array): // a : [ // outer array. should not match // 123, // inner array // [ 456 ], // inner array. should match // ... // ] if (_type == mongo::Array && e.outerArray()) { continue; } if (!matchesSingleElement(e.element())) { continue; } if (details && details->needRecord() && !e.arrayOffset().eoo()) { details->setElemMatchKey(e.arrayOffset().fieldName()); } return true; } return false; }
bool LeafMatchExpression::matches(const MatchableDocument* doc, MatchDetails* details) const { MatchableDocument::IteratorHolder cursor(doc, &_elementPath); while (cursor->more()) { ElementIterator::Context e = cursor->next(); if (!matchesSingleElement(e.element())) continue; if (details && details->needRecord() && !e.arrayOffset().eoo()) { details->setElemMatchKey(e.arrayOffset().fieldName()); } return true; } return false; }
bool ArrayMatchingMatchExpression::matches( const MatchableDocument* doc, MatchDetails* details ) const { MatchableDocument::IteratorHolder cursor( doc, &_elementPath ); while ( cursor->more() ) { ElementIterator::Context e = cursor->next(); if ( e.element().type() != Array ) continue; bool amIRoot = e.arrayOffset().eoo(); if ( !matchesArray( e.element().Obj(), amIRoot ? details : NULL ) ) continue; if ( !amIRoot && details && details->needRecord() && !e.arrayOffset().eoo() ) { details->setElemMatchKey( e.arrayOffset().fieldName() ); } return true; } return false; }
// SERVER-14886: when an array is being traversed explictly at the same time that a nested array // is being traversed implicitly, ElementIterator::Context::arrayOffset() should return the // current offset of the array being implicitly traversed. TEST(Path, ArrayOffsetWithImplicitAndExplicitTraversal) { ElementPath p; ASSERT(p.init("a.0.b").isOK()); BSONObj doc = fromjson("{a: [{b: [2, 3]}, {b: [4, 5]}]}"); BSONElementIterator cursor(&p, doc); ASSERT(cursor.more()); ElementIterator::Context e = cursor.next(); ASSERT_EQUALS(EOO, e.element().type()); ASSERT_EQUALS("0", e.arrayOffset().fieldNameStringData()); // First elt of outer array. ASSERT(cursor.more()); e = cursor.next(); ASSERT_EQUALS(NumberInt, e.element().type()); ASSERT_EQUALS(2, e.element().numberInt()); ASSERT_EQUALS("0", e.arrayOffset().fieldNameStringData()); // First elt of inner array. ASSERT(cursor.more()); e = cursor.next(); ASSERT_EQUALS(NumberInt, e.element().type()); ASSERT_EQUALS(3, e.element().numberInt()); ASSERT_EQUALS("1", e.arrayOffset().fieldNameStringData()); // Second elt of inner array. ASSERT(cursor.more()); e = cursor.next(); ASSERT_EQUALS(Array, e.element().type()); ASSERT_BSONOBJ_EQ(BSON("0" << 2 << "1" << 3), e.element().Obj()); ASSERT(e.arrayOffset().eoo()); ASSERT(cursor.more()); e = cursor.next(); ASSERT_EQUALS(EOO, e.element().type()); ASSERT_EQUALS("1", e.arrayOffset().fieldNameStringData()); // Second elt of outer array. ASSERT(!cursor.more()); }
// When multiple arrays are traversed implicitly in the same path, // ElementIterator::Context::arrayOffset() should always refer to the current offset of the // outermost array that is implicitly traversed. TEST(Path, NestedArrayImplicitTraversal) { ElementPath p; ASSERT(p.init("a.b").isOK()); BSONObj doc = fromjson("{a: [{b: [2, 3]}, {b: [4, 5]}]}"); BSONElementIterator cursor(&p, doc); ASSERT(cursor.more()); ElementIterator::Context e = cursor.next(); ASSERT_EQUALS(NumberInt, e.element().type()); ASSERT_EQUALS(2, e.element().numberInt()); ASSERT_EQUALS("0", e.arrayOffset().fieldNameStringData()); ASSERT(cursor.more()); e = cursor.next(); ASSERT_EQUALS(NumberInt, e.element().type()); ASSERT_EQUALS(3, e.element().numberInt()); ASSERT_EQUALS("0", e.arrayOffset().fieldNameStringData()); ASSERT(cursor.more()); e = cursor.next(); ASSERT_EQUALS(Array, e.element().type()); ASSERT_BSONOBJ_EQ(BSON("0" << 2 << "1" << 3), e.element().Obj()); ASSERT_EQUALS("0", e.arrayOffset().fieldNameStringData()); ASSERT(cursor.more()); e = cursor.next(); ASSERT_EQUALS(NumberInt, e.element().type()); ASSERT_EQUALS(4, e.element().numberInt()); ASSERT_EQUALS("1", e.arrayOffset().fieldNameStringData()); ASSERT(cursor.more()); e = cursor.next(); ASSERT_EQUALS(NumberInt, e.element().type()); ASSERT_EQUALS(5, e.element().numberInt()); ASSERT_EQUALS("1", e.arrayOffset().fieldNameStringData()); ASSERT(cursor.more()); e = cursor.next(); ASSERT_EQUALS(Array, e.element().type()); ASSERT_BSONOBJ_EQ(BSON("0" << 4 << "1" << 5), e.element().Obj()); ASSERT_EQUALS("1", e.arrayOffset().fieldNameStringData()); ASSERT(!cursor.more()); }