31
31
* This file contains tests for mongo/db/query/plan_ranker.h
32
32
*/
33
33
34
- #include " mongo/db/query/plan_ranker.h"
35
-
36
34
#include < utility>
37
35
#include < vector>
38
36
37
+ #include " mongo/bson/bsonelement.h"
38
+ #include " mongo/bson/bsonmisc.h"
39
39
#include " mongo/db/exec/plan_stats.h"
40
+ #include " mongo/db/namespace_string.h"
41
+ #include " mongo/db/pipeline/document_source_group.h"
42
+ #include " mongo/db/pipeline/expression_context_for_test.h"
43
+ #include " mongo/db/query/canonical_query.h"
40
44
#include " mongo/db/query/plan_ranker_util.h"
45
+ #include " mongo/db/query/stage_types.h"
46
+ #include " mongo/idl/server_parameter_test_util.h"
41
47
#include " mongo/unittest/assert.h"
42
48
#include " mongo/unittest/framework.h"
43
49
@@ -49,11 +55,21 @@ using std::make_unique;
49
55
using std::string;
50
56
using std::unique_ptr;
51
57
58
+ unique_ptr<CanonicalQuery> makeCanonicalQuery () {
59
+ auto expCtx = new ExpressionContextForTest ();
60
+ auto findCommand = std::make_unique<FindCommandRequest>(NamespaceString ());
61
+ return std::make_unique<CanonicalQuery>(CanonicalQueryParams{
62
+ .expCtx = expCtx, .parsedFind = ParsedFindCommandParams{std::move (findCommand)}});
63
+ }
64
+
52
65
unique_ptr<PlanStageStats> makeStats (const char * name,
53
66
StageType type,
54
- unique_ptr<SpecificStats> specific) {
67
+ unique_ptr<SpecificStats> specific,
68
+ size_t works = 1 ,
69
+ size_t advances = 1 ) {
55
70
auto stats = make_unique<PlanStageStats>(name, type);
56
- stats->common .works = 1 ;
71
+ stats->common .works = works;
72
+ stats->common .advanced = advances;
57
73
stats->specific = std::move (specific);
58
74
return stats;
59
75
}
@@ -74,11 +90,59 @@ TEST(PlanRankerTest, NoFetchBonus) {
74
90
badPlan->children [0 ]->children .emplace_back (
75
91
makeStats (" IXSCAN" , STAGE_IXSCAN, make_unique<IndexScanStats>()));
76
92
93
+ auto cq = makeCanonicalQuery ();
77
94
auto scorer = plan_ranker::makePlanScorer ();
78
- auto goodScore = scorer->calculateScore (goodPlan.get ());
79
- auto badScore = scorer->calculateScore (badPlan.get ());
95
+ auto goodScore = scorer->calculateScore (goodPlan.get (), *cq );
96
+ auto badScore = scorer->calculateScore (badPlan.get (), *cq );
80
97
81
98
ASSERT_GT (goodScore, badScore);
82
99
}
83
100
101
+ TEST (PlanRankerTest, DistinctBonus) {
102
+ RAIIServerParameterControllerForTest shardFilteringDistinct (
103
+ " featureFlagShardFilteringDistinctScan" , true );
104
+
105
+ // Two plans: both fetch, one is a DISTINCT_SCAN, other is an IXSCAN.
106
+ // DISTINCT_SCAN does 2 advances / 10 works.
107
+ auto dsStats = make_unique<DistinctScanStats>();
108
+ dsStats->isFetching = true ;
109
+ dsStats->isShardFilteringDistinctScanEnabled = true ;
110
+ auto distinctScanPlan =
111
+ makeStats (" DISTINCT_SCAN" , STAGE_DISTINCT_SCAN, std::move (dsStats), 10 , 2 );
112
+
113
+ // IXSCAN plan does 2 advances / 10 works.
114
+ auto ixscanPlan = makeStats (" FETCH" , STAGE_FETCH, make_unique<FetchStats>(), 10 , 2 );
115
+ ixscanPlan->children .emplace_back (
116
+ makeStats (" IXSCAN" , STAGE_IXSCAN, make_unique<IndexScanStats>(), 10 , 2 ));
117
+
118
+ auto cq = makeCanonicalQuery ();
119
+ cq->setDistinct (CanonicalDistinct (" someKey" ));
120
+ auto scorer = plan_ranker::makePlanScorer ();
121
+ auto distinctScore = scorer->calculateScore (distinctScanPlan.get (), *cq);
122
+ auto ixscanScore = scorer->calculateScore (ixscanPlan.get (), *cq);
123
+
124
+ // Both plans should tie now- a tie-breaker will be applied at a later stage.
125
+ ASSERT_EQ (distinctScore, ixscanScore);
126
+
127
+ // Now we change to an aggregation context (simulate $groupByDistinct rewrite case).
128
+ auto groupBson = BSON (" $group" << BSON (" _id"
129
+ << " someKey" ));
130
+ cq->setCqPipeline (
131
+ {DocumentSourceGroup::createFromBson (groupBson.firstElement (), cq->getExpCtx ())}, true );
132
+
133
+ // When in a distinct() context, productivity is considered larger in a distinct, even if both
134
+ // plans have the same advances:work ratio. A DISTINCT_SCAN should now win by a large margin
135
+ // (tie breaker).
136
+ distinctScore = scorer->calculateScore (distinctScanPlan.get (), *cq);
137
+ ixscanScore = scorer->calculateScore (ixscanPlan.get (), *cq);
138
+ ASSERT_GT (distinctScore, ixscanScore);
139
+
140
+ // If we make the IXSCAN productive enough, however, it can still win!
141
+ ixscanPlan->children [0 ]->common .advanced = 10 ;
142
+ ixscanPlan->common .advanced = 10 ;
143
+ distinctScore = scorer->calculateScore (distinctScanPlan.get (), *cq);
144
+ ixscanScore = scorer->calculateScore (ixscanPlan.get (), *cq);
145
+ ASSERT_GT (ixscanScore, distinctScore);
146
+ }
147
+
84
148
}; // namespace
0 commit comments