@@ -2208,6 +2208,152 @@ TEST(ExpressionPowTest, NegativeOneRaisedToNegativeOddExponentShouldOutPutNegati
22082208 });
22092209}
22102210
2211+ TEST (ExpressionArray, ExpressionArrayWithAllConstantValuesShouldOptimizeToExpressionConstant) {
2212+ intrusive_ptr<ExpressionContextForTest> expCtx (new ExpressionContextForTest ());
2213+ VariablesParseState vps = expCtx->variablesParseState ;
2214+
2215+ // ExpressionArray of constant values should optimize to ExpressionConsant.
2216+ BSONObj bsonarrayOfConstants = BSON (" " << BSON_ARRAY (1 << 2 << 3 << 4 ));
2217+ BSONElement elementArray = bsonarrayOfConstants.firstElement ();
2218+ auto expressionArr = ExpressionArray::parse (expCtx, elementArray, vps);
2219+ auto optimizedToConstant = expressionArr->optimize ();
2220+ auto exprConstant = dynamic_cast <ExpressionConstant*>(optimizedToConstant.get ());
2221+ ASSERT_TRUE (exprConstant);
2222+
2223+ // ExpressionArray with not all constant values should not optimize to ExpressionConstant.
2224+ BSONObj bsonarray = BSON (" " << BSON_ARRAY (1 << " $x" << 3 << 4 ));
2225+ BSONElement elementArrayNotConstant = bsonarray.firstElement ();
2226+ auto expressionArrNotConstant = ExpressionArray::parse (expCtx, elementArrayNotConstant, vps);
2227+ auto notOptimized = expressionArrNotConstant->optimize ();
2228+ auto notExprConstant = dynamic_cast <ExpressionConstant*>(notOptimized.get ());
2229+ ASSERT_FALSE (notExprConstant);
2230+ }
2231+
2232+ TEST (ExpressionArray, ExpressionArrayShouldOptimizeSubExpressionToExpressionConstant) {
2233+ intrusive_ptr<ExpressionContextForTest> expCtx (new ExpressionContextForTest ());
2234+ VariablesParseState vps = expCtx->variablesParseState ;
2235+
2236+
2237+ // ExpressionArray with constant values and sub expression that evaluates to constant should
2238+ // optimize to Expression constant.
2239+ BSONObj bsonarrayWithSubExpression =
2240+ BSON (" " << BSON_ARRAY (1 << BSON (" $add" << BSON_ARRAY (1 << 1 )) << 3 << 4 ));
2241+ BSONElement elementArrayWithSubExpression = bsonarrayWithSubExpression.firstElement ();
2242+ auto expressionArrWithSubExpression =
2243+ ExpressionArray::parse (expCtx, elementArrayWithSubExpression, vps);
2244+ auto optimizedToConstantWithSubExpression = expressionArrWithSubExpression->optimize ();
2245+ auto constantExpression =
2246+ dynamic_cast <ExpressionConstant*>(optimizedToConstantWithSubExpression.get ());
2247+ ASSERT_TRUE (constantExpression);
2248+ }
2249+
2250+ TEST (ExpressionIndexOfArray, ExpressionIndexOfArrayShouldOptimizeArguments) {
2251+ intrusive_ptr<ExpressionContextForTest> expCtx (new ExpressionContextForTest ());
2252+
2253+ auto expIndexOfArray = Expression::parseExpression (
2254+ expCtx, // 2, 1, 1
2255+ BSON (" $indexOfArray" << BSON_ARRAY (
2256+ BSON_ARRAY (BSON (" $add" << BSON_ARRAY (1 << 1 )) << 1 << 1 << 2 )
2257+ // Value we are searching for = 2.
2258+ << BSON (" $add" << BSON_ARRAY (1 << 1 ))
2259+ // Start index = 1.
2260+ << BSON (" $add" << BSON_ARRAY (0 << 1 ))
2261+ // End index = 4.
2262+ << BSON (" $add" << BSON_ARRAY (1 << 3 )))),
2263+ expCtx->variablesParseState );
2264+ auto argsOptimizedToConstants = expIndexOfArray->optimize ();
2265+ auto shouldBeIndexOfArray = dynamic_cast <ExpressionConstant*>(argsOptimizedToConstants.get ());
2266+ ASSERT_TRUE (shouldBeIndexOfArray);
2267+ ASSERT_VALUE_EQ (Value (3 ), shouldBeIndexOfArray->getValue ());
2268+ }
2269+
2270+ TEST (ExpressionIndexOfArray,
2271+ ExpressionIndexOfArrayShouldOptimizeNullishInputArrayToExpressionConstant) {
2272+ intrusive_ptr<ExpressionContextForTest> expCtx (new ExpressionContextForTest ());
2273+ VariablesParseState vps = expCtx->variablesParseState ;
2274+
2275+ auto expIndex = Expression::parseExpression (
2276+ expCtx, fromjson (" { $indexOfArray : [ undefined , 1, 1]}" ), expCtx->variablesParseState );
2277+
2278+ auto isExpIndexOfArray = dynamic_cast <ExpressionIndexOfArray*>(expIndex.get ());
2279+ ASSERT_TRUE (isExpIndexOfArray);
2280+
2281+ auto nullishValueOptimizedToExpConstant = isExpIndexOfArray->optimize ();
2282+ auto shouldBeExpressionConstant =
2283+ dynamic_cast <ExpressionConstant*>(nullishValueOptimizedToExpConstant.get ());
2284+ ASSERT_TRUE (shouldBeExpressionConstant);
2285+ // Nullish input array should become a Value(BSONNULL).
2286+ ASSERT_VALUE_EQ (Value (BSONNULL), shouldBeExpressionConstant->getValue ());
2287+ }
2288+
2289+ TEST (ExpressionIndexOfArray,
2290+ OptimizedExpressionIndexOfArrayWithConstantArgumentsShouldEvaluateProperly) {
2291+
2292+ intrusive_ptr<ExpressionContextForTest> expCtx (new ExpressionContextForTest ());
2293+
2294+ auto expIndexOfArray = Expression::parseExpression (
2295+ expCtx,
2296+ // Search for $x.
2297+ fromjson (" { $indexOfArray : [ [0, 1, 2, 3, 4, 5, 'val'] , '$x'] }" ),
2298+ expCtx->variablesParseState );
2299+ auto optimizedIndexOfArray = expIndexOfArray->optimize ();
2300+ ASSERT_VALUE_EQ (Value (0 ), optimizedIndexOfArray->evaluate (Document{{" x" , 0 }}));
2301+ ASSERT_VALUE_EQ (Value (1 ), optimizedIndexOfArray->evaluate (Document{{" x" , 1 }}));
2302+ ASSERT_VALUE_EQ (Value (2 ), optimizedIndexOfArray->evaluate (Document{{" x" , 2 }}));
2303+ ASSERT_VALUE_EQ (Value (3 ), optimizedIndexOfArray->evaluate (Document{{" x" , 3 }}));
2304+ ASSERT_VALUE_EQ (Value (4 ), optimizedIndexOfArray->evaluate (Document{{" x" , 4 }}));
2305+ ASSERT_VALUE_EQ (Value (5 ), optimizedIndexOfArray->evaluate (Document{{" x" , 5 }}));
2306+ ASSERT_VALUE_EQ (Value (6 ), optimizedIndexOfArray->evaluate (Document{{" x" , string (" val" )}}));
2307+
2308+ auto optimizedIndexNotFound = optimizedIndexOfArray->optimize ();
2309+ // Should evaluate to -1 if not found.
2310+ ASSERT_VALUE_EQ (Value (-1 ), optimizedIndexNotFound->evaluate (Document{{" x" , 10 }}));
2311+ ASSERT_VALUE_EQ (Value (-1 ), optimizedIndexNotFound->evaluate (Document{{" x" , 100 }}));
2312+ ASSERT_VALUE_EQ (Value (-1 ), optimizedIndexNotFound->evaluate (Document{{" x" , 1000 }}));
2313+ ASSERT_VALUE_EQ (Value (-1 ), optimizedIndexNotFound->evaluate (Document{{" x" , string (" string" )}}));
2314+ ASSERT_VALUE_EQ (Value (-1 ), optimizedIndexNotFound->evaluate (Document{{" x" , -1 }}));
2315+ }
2316+
2317+ TEST (ExpressionIndexOfArray,
2318+ OptimizedExpressionIndexOfArrayWithConstantArgumentsShouldEvaluateProperlyWithRange) {
2319+ intrusive_ptr<ExpressionContextForTest> expCtx (new ExpressionContextForTest ());
2320+
2321+ auto expIndexOfArray = Expression::parseExpression (
2322+ expCtx,
2323+ // Search for 4 between 3 and 5.
2324+ fromjson (" { $indexOfArray : [ [0, 1, 2, 3, 4, 5] , '$x', 3, 5] }" ),
2325+ expCtx->variablesParseState );
2326+ auto optimizedIndexOfArray = expIndexOfArray->optimize ();
2327+ ASSERT_VALUE_EQ (Value (4 ), optimizedIndexOfArray->evaluate (Document{{" x" , 4 }}));
2328+
2329+ // Should evaluate to -1 if not found in range.
2330+ ASSERT_VALUE_EQ (Value (-1 ), optimizedIndexOfArray->evaluate (Document{{" x" , 0 }}));
2331+ }
2332+
2333+ TEST (ExpressionIndexOfArray,
2334+ OptimizedExpressionIndexOfArrayWithConstantArrayShouldEvaluateProperlyWithDuplicateValues) {
2335+ intrusive_ptr<ExpressionContextForTest> expCtx (new ExpressionContextForTest ());
2336+
2337+ auto expIndexOfArrayWithDuplicateValues =
2338+ Expression::parseExpression (expCtx,
2339+ // Search for 4 between 3 and 5.
2340+ fromjson (" { $indexOfArray : [ [0, 1, 2, 2, 3, 4, 5] , '$x'] }" ),
2341+ expCtx->variablesParseState );
2342+ auto optimizedIndexOfArrayWithDuplicateValues = expIndexOfArrayWithDuplicateValues->optimize ();
2343+ ASSERT_VALUE_EQ (Value (2 ),
2344+ optimizedIndexOfArrayWithDuplicateValues->evaluate (Document{{" x" , 2 }}));
2345+ // Duplicate Values in a range.
2346+ auto expIndexInRangeWithhDuplicateValues = Expression::parseExpression (
2347+ expCtx,
2348+ // Search for 2 between 4 and 6.
2349+ fromjson (" { $indexOfArray : [ [0, 1, 2, 2, 2, 2, 4, 5] , '$x', 4, 6] }" ),
2350+ expCtx->variablesParseState );
2351+ auto optimizedIndexInRangeWithDuplcateValues = expIndexInRangeWithhDuplicateValues->optimize ();
2352+ // Should evaluate to 4.
2353+ ASSERT_VALUE_EQ (Value (4 ),
2354+ optimizedIndexInRangeWithDuplcateValues->evaluate (Document{{" x" , 2 }}));
2355+ }
2356+
22112357namespace FieldPath {
22122358
22132359/* * The provided field path does not pass validation. */
0 commit comments