Skip to content

Commit d4b58bf

Browse files
HanaPearlmanEvergreen Agent
authored andcommitted
SERVER-58923: Add tests for lookup against a view containing another lookup
1 parent 7d58a3a commit d4b58bf

File tree

2 files changed

+231
-0
lines changed

2 files changed

+231
-0
lines changed

jstests/sharding/query/sharded_graph_lookup_execution.js

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,180 @@ assertGraphLookupExecution(
364364
}
365365
]);
366366

367+
// Test sharded local collection where the foreign namespace is a sharded view with another
368+
// $graphLookup against a sharded collection. Note that the $graphLookup in the view should be
369+
// treated as a "nested" $graphLookup and should execute on the merging node.
370+
st.shardColl(airportsColl, {airport: 1}, {airport: "JFK"}, {airport: "JFK"}, mongosDB.getName());
371+
st.shardColl(
372+
travelersColl, {firstName: 1}, {firstName: "Bob"}, {firstName: "Bob"}, mongosDB.getName());
373+
st.shardColl(
374+
airfieldsColl, {airfield: 1}, {airfield: "LHR"}, {airfield: "LHR"}, mongosDB.getName());
375+
376+
assert.commandWorked(mongosDB.createView("airportsView", airportsColl.getName(),
377+
[{$graphLookup: {
378+
from: "airfields",
379+
startWith: "$airport",
380+
connectFromField: "connects",
381+
connectToField: "airfield",
382+
maxDepth: 1,
383+
as: "nearbyAirfields"
384+
}}]
385+
));
386+
pipeline = [
387+
{$graphLookup: {
388+
from: "airportsView",
389+
startWith: "$nearestAirport",
390+
connectFromField: "connects",
391+
connectToField: "airport",
392+
maxDepth: 0,
393+
as: "destinations"
394+
}},
395+
{$unwind: "$destinations"},
396+
{$project: {firstName: 1, 'destinations.airport' : 1, 'destinations.nearbyAirfields.airfield' : 1}}
397+
];
398+
399+
assertGraphLookupExecution(pipeline, {comment: "sharded_to_sharded_view_to_sharded"}, expectedRes, [
400+
{
401+
// The 'travelers' collection is sharded, so the $graphLookup stage is executed in parallel
402+
// on every shard that contains the local collection.
403+
toplevelExec: [1, 1],
404+
// Each node executing the $graphLookup will, for every document that flows through the
405+
// stage, target the shard(s) that holds the relevant data for the sharded foreign view.
406+
recursiveMatchExec: [0, 3],
407+
},
408+
{
409+
collName: airportsColl.getName(),
410+
fromCollName: airfieldsColl.getName(),
411+
// When executing the subpipeline, the "nested" $graphLookup stage contained in the view
412+
// pipeline will stay on the merging half of the pipeline and execute on the merging node,
413+
// targeting shards to execute the nested $matches.
414+
toplevelExec: [0, 0],
415+
recursiveMatchExec: [1, 5]
416+
}
417+
]);
418+
mongosDB.airportsView.drop();
419+
420+
// Test top-level $lookup on a sharded local collection where the foreign namespace is a sharded
421+
// view with a $graphLookup against a sharded collection. Note that the $graphLookup in the view
422+
// should be treated as a "nested" $graphLookup and should execute on the merging node.
423+
st.shardColl(airportsColl, {airport: 1}, {airport: "JFK"}, {airport: "JFK"}, mongosDB.getName());
424+
st.shardColl(
425+
travelersColl, {firstName: 1}, {firstName: "Bob"}, {firstName: "Bob"}, mongosDB.getName());
426+
st.shardColl(
427+
airfieldsColl, {airfield: 1}, {airfield: "LHR"}, {airfield: "LHR"}, mongosDB.getName());
428+
429+
assert.commandWorked(mongosDB.createView("airportsView", airportsColl.getName(),
430+
[{$graphLookup: {
431+
from: "airfields",
432+
startWith: "$airport",
433+
connectFromField: "connects",
434+
connectToField: "airfield",
435+
maxDepth: 1,
436+
as: "nearbyAirfields"
437+
}}]
438+
));
439+
pipeline = [
440+
{$lookup: {
441+
from: "airportsView",
442+
localField: "nearestAirport",
443+
foreignField: "airport",
444+
as: "destinations"
445+
}},
446+
{$unwind: "$destinations"},
447+
{$project: {firstName: 1, 'destinations.airport' : 1, 'destinations.nearbyAirfields.airfield' : 1}}
448+
];
449+
450+
assertGraphLookupExecution(
451+
pipeline, {comment: "sharded_lookup_to_sharded_view_to_sharded"}, expectedRes, [
452+
{
453+
// The 'travelers' collection is sharded, so the $lookup stage is executed in parallel
454+
// on every shard that contains the local collection.
455+
toplevelExec: [1, 1],
456+
// Each node executing the $lookup will, for every document that flows through the stage
457+
// target the shard(s) that holds the relevant data for the sharded foreign view.
458+
subpipelineExec: [0, 3],
459+
},
460+
{
461+
collName: airportsColl.getName(),
462+
fromCollName: airfieldsColl.getName(),
463+
// When executing the subpipeline, the "nested" $graphLookup stage contained in the view
464+
// pipeline will stay on the merging half of the pipeline and execute on the merging
465+
// node, targeting shards to execute the nested $matches.
466+
toplevelExec: [0, 0],
467+
recursiveMatchExec: [1, 5]
468+
}
469+
]);
470+
mongosDB.airportsView.drop();
471+
472+
// Test sharded local collection where the foreign namespace is a sharded view with a $lookup
473+
// against a sharded collection. Note that the $lookup in the view should be treated as a "nested"
474+
// $lookup and should execute on the merging node.
475+
st.shardColl(airportsColl, {airport: 1}, {airport: "JFK"}, {airport: "JFK"}, mongosDB.getName());
476+
st.shardColl(
477+
travelersColl, {firstName: 1}, {firstName: "Bob"}, {firstName: "Bob"}, mongosDB.getName());
478+
st.shardColl(
479+
airfieldsColl, {airfield: 1}, {airfield: "LHR"}, {airfield: "LHR"}, mongosDB.getName());
480+
481+
assert.commandWorked(mongosDB.createView("airportsView", airportsColl.getName(),
482+
[{$lookup: {
483+
from: "airfields",
484+
localField: "airport",
485+
foreignField: "airfield",
486+
as: "nearbyAirfields"
487+
}}]
488+
));
489+
pipeline = [
490+
{$graphLookup: {
491+
from: "airportsView",
492+
startWith: "$nearestAirport",
493+
connectFromField: "connects",
494+
connectToField: "airport",
495+
maxDepth: 0,
496+
as: "destinations"
497+
}},
498+
{$unwind: "$destinations"},
499+
{$project: {firstName: 1, 'destinations.airport' : 1, 'destinations.nearbyAirfields.airfield' : 1}}
500+
];
501+
expectedRes = [
502+
{
503+
_id: 1,
504+
firstName: "Alice",
505+
destinations: {airport: "LHR", nearbyAirfields: [{airfield: "LHR"}]},
506+
},
507+
{
508+
_id: 2,
509+
firstName: "Alice",
510+
destinations: {airport: "ORD", nearbyAirfields: [{airfield: "ORD"}]},
511+
},
512+
{
513+
_id: 3,
514+
firstName: "Bob",
515+
destinations: {airport: "JFK", nearbyAirfields: [{airfield: "JFK"}]},
516+
}
517+
];
518+
519+
assertGraphLookupExecution(
520+
pipeline, {comment: "sharded_to_sharded_lookup_view_to_sharded"}, expectedRes, [
521+
{
522+
// The 'travelers' collection is sharded, so the $graphLookup stage is executed in
523+
// parallel on every shard that contains the local collection.
524+
toplevelExec: [1, 1],
525+
// Each node executing the $graphLookup will, for every document that flows through the
526+
// stage, target the shard(s) that holds the relevant data for the sharded foreign view.
527+
recursiveMatchExec: [0, 3],
528+
},
529+
{
530+
collName: airportsColl.getName(),
531+
fromCollName: airfieldsColl.getName(),
532+
// When executing the subpipeline, the "nested" $lookup stage contained in the view
533+
// pipeline will stay on the merging half of the pipeline and execute on the merging
534+
// node, targeting shards to execute the nested subpipelines.
535+
toplevelExec: [0, 0],
536+
subpipelineExec: [1, 2]
537+
}
538+
]);
539+
mongosDB.airportsView.drop();
540+
367541
// Test that a targeted $graphLookup on a sharded collection can execute correctly on mongos.
368542
st.shardColl(airportsColl, {airport: 1}, {airport: "LHR"}, {airport: "LHR"}, mongosDB.getName());
369543
st.shardColl(

jstests/sharding/query/sharded_lookup_execution.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,63 @@ assertLookupExecution(pipeline, {comment: "sharded_to_sharded_to_sharded_scatter
361361
nestedExec: [3, 3]
362362
});
363363

364+
// Test sharded local collection where the foreign namespace is a sharded view with another
365+
// $lookup against a sharded collection. Note that the $lookup in the view should be treated as
366+
// "nested" $lookup and should execute on the merging node.
367+
st.shardColl(ordersColl, {_id: 1}, {_id: 1}, {_id: 1}, mongosDB.getName());
368+
st.shardColl(
369+
reviewsColl, {product_id: 1}, {product_id: "hat"}, {product_id: "hat"}, mongosDB.getName());
370+
st.shardColl(updatesColl,
371+
{original_review_id: 1},
372+
{original_review_id: 1},
373+
{original_review_id: 1},
374+
mongosDB.getName());
375+
376+
assert.commandWorked(mongosDB.createView("reviewsView", reviewsColl.getName(),
377+
[{$lookup: {
378+
from: "updates",
379+
let: {review_id: "$_id"},
380+
pipeline: [{$match: {$expr: {$eq: ["$original_review_id", "$$review_id"]}}}],
381+
as: "updates"
382+
}},
383+
{$unwind: {path: "$updates", preserveNullAndEmptyArrays: true}},
384+
{$project: {product_id: 1, stars: {$ifNull: ["$updates.updated_stars", "$stars"]}}}
385+
]));
386+
pipeline = [
387+
{$match: {customer: "Alice"}},
388+
{$unwind: "$products"},
389+
{$lookup: {
390+
from: "reviewsView",
391+
let: {customers_product_id: "$products._id"},
392+
pipeline: [
393+
{$match: {$expr: {$eq: ["$product_id", "$$customers_product_id"]}}},
394+
],
395+
as: "reviews"
396+
}},
397+
{$group: {
398+
_id: "$_id",
399+
products: {$push: {
400+
_id: "$products._id",
401+
price: "$products.price",
402+
avg_review: {$avg: "$reviews.stars"}
403+
}}
404+
}}
405+
];
406+
407+
assertLookupExecution(pipeline, {comment: "sharded_to_sharded_view_to_sharded"}, {
408+
results: expectedRes,
409+
// The 'orders' collection is sharded, so the top-level stage $lookup is executed in parallel on
410+
// every shard that contains the local collection.
411+
toplevelExec: [1, 1],
412+
// For every document that flows through the $lookup stage, the node executing the $lookup will
413+
// target the shard(s) that holds the relevant data for the sharded foreign view.
414+
subpipelineExec: [0, 2],
415+
// When executing the subpipeline, the "nested" $lookup stage contained in the view pipeline
416+
// will stay on the merging half of the pipeline and execute on the merging node, targeting
417+
// shards to execute the nested subpipeline.
418+
nestedExec: [1, 2]
419+
});
420+
364421
// Test that a targeted $lookup on a sharded collection can execute correctly on mongos.
365422
st.shardColl(ordersColl, {_id: 1}, {_id: 1}, {_id: 1}, mongosDB.getName());
366423
st.shardColl(

0 commit comments

Comments
 (0)