SERVER-114748 Process dropping fields when materializing result in JOO (#45240)

GitOrigin-RevId: b34440744ed2700cce07aeebd13cc8fd810959f1
This commit is contained in:
Alberto Massari 2025-12-16 21:05:41 +01:00 committed by MongoDB Bot
parent 1f7f8f8c54
commit 861513e6ea
6 changed files with 1328 additions and 2 deletions

View File

@ -1604,3 +1604,328 @@ rightEmbeddingField: "k.y.z"
COLLSCAN [test.basic_joins_md] COLLSCAN [test.basic_joins_md]
direction: "forward" direction: "forward"
``` ```
## 6. Basic example with a $project excluding a field from the base collection
### No join opt
### Pipeline
```json
[
{
"$project" : {
"_id" : false
}
},
{
"$lookup" : {
"from" : "basic_joins_md_foreign1",
"as" : "x",
"localField" : "a",
"foreignField" : "a"
}
},
{
"$unwind" : "$x"
},
{
"$lookup" : {
"from" : "basic_joins_md_foreign2",
"as" : "y",
"localField" : "b",
"foreignField" : "b"
}
},
{
"$unwind" : "$y"
}
]
```
### Results
```json
{ "a" : 1, "b" : "bar", "x" : { "_id" : 0, "a" : 1, "c" : "zoo", "d" : 1 }, "y" : { "_id" : 0, "b" : "bar", "d" : 2 } }
{ "a" : 1, "b" : "bar", "x" : { "_id" : 0, "a" : 1, "c" : "zoo", "d" : 1 }, "y" : { "_id" : 1, "b" : "bar", "d" : 6 } }
{ "a" : 2, "b" : "bar", "x" : { "_id" : 1, "a" : 2, "c" : "blah", "d" : 2 }, "y" : { "_id" : 0, "b" : "bar", "d" : 2 } }
{ "a" : 2, "b" : "bar", "x" : { "_id" : 1, "a" : 2, "c" : "blah", "d" : 2 }, "y" : { "_id" : 1, "b" : "bar", "d" : 6 } }
{ "a" : 2, "b" : "bar", "x" : { "_id" : 2, "a" : 2, "c" : "x", "d" : 3 }, "y" : { "_id" : 0, "b" : "bar", "d" : 2 } }
{ "a" : 2, "b" : "bar", "x" : { "_id" : 2, "a" : 2, "c" : "x", "d" : 3 }, "y" : { "_id" : 1, "b" : "bar", "d" : 6 } }
```
### Summarized explain
Execution Engine: sbe
```json
{
"queryShapeHash" : "9A2C9BDFA638F0C7F703251AC894A297CDDB8AF81B931E490B488BCF87386796",
"rejectedPlans" : [ ],
"winningPlan" : {
"asField" : "y",
"foreignCollection" : "test.basic_joins_md_foreign2",
"foreignField" : "b",
"inputStage" : {
"asField" : "x",
"foreignCollection" : "test.basic_joins_md_foreign1",
"foreignField" : "a",
"inputStage" : {
"inputStage" : {
"direction" : "forward",
"filter" : {
},
"nss" : "test.basic_joins_md",
"stage" : "COLLSCAN"
},
"stage" : "PROJECTION_SIMPLE",
"transformBy" : {
"_id" : false
}
},
"localField" : "a",
"scanDirection" : "forward",
"stage" : "EQ_LOOKUP_UNWIND",
"strategy" : "HashJoin"
},
"localField" : "b",
"planNodeId" : 4,
"scanDirection" : "forward",
"stage" : "EQ_LOOKUP_UNWIND",
"strategy" : "HashJoin"
}
}
```
### With bottom-up plan enumeration (left-deep)
usedJoinOptimization: true
```
HASH_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "none"
rightEmbeddingField: "y"
| |
| COLLSCAN [test.basic_joins_md_foreign2]
| direction: "forward"
|
HASH_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "none"
rightEmbeddingField: "x"
| |
| COLLSCAN [test.basic_joins_md_foreign1]
| direction: "forward"
|
PROJECTION_SIMPLE
transformBy: { "_id" : false }
|
COLLSCAN [test.basic_joins_md]
direction: "forward"
```
### With bottom-up plan enumeration (right-deep)
usedJoinOptimization: true
```
HASH_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "y"
rightEmbeddingField: "none"
| |
| HASH_JOIN_EMBEDDING [a = a]
| leftEmbeddingField: "none"
| rightEmbeddingField: "x"
| | |
| | COLLSCAN [test.basic_joins_md_foreign1]
| | direction: "forward"
| |
| PROJECTION_SIMPLE
| transformBy: { "_id" : false }
| |
| COLLSCAN [test.basic_joins_md]
| direction: "forward"
|
COLLSCAN [test.basic_joins_md_foreign2]
direction: "forward"
```
### With bottom-up plan enumeration (zig-zag)
usedJoinOptimization: true
```
HASH_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "none"
rightEmbeddingField: "y"
| |
| COLLSCAN [test.basic_joins_md_foreign2]
| direction: "forward"
|
HASH_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "none"
rightEmbeddingField: "x"
| |
| COLLSCAN [test.basic_joins_md_foreign1]
| direction: "forward"
|
PROJECTION_SIMPLE
transformBy: { "_id" : false }
|
COLLSCAN [test.basic_joins_md]
direction: "forward"
```
### With random order, seed 44, nested loop joins
usedJoinOptimization: true
```
NESTED_LOOP_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "none"
rightEmbeddingField: "y"
| |
| COLLSCAN [test.basic_joins_md_foreign2]
| direction: "forward"
|
NESTED_LOOP_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "x"
rightEmbeddingField: "none"
| |
| PROJECTION_SIMPLE
| transformBy: { "_id" : false }
| |
| COLLSCAN [test.basic_joins_md]
| direction: "forward"
|
COLLSCAN [test.basic_joins_md_foreign1]
direction: "forward"
```
### With random order, seed 44, hash join enabled
usedJoinOptimization: true
```
HASH_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "none"
rightEmbeddingField: "y"
| |
| COLLSCAN [test.basic_joins_md_foreign2]
| direction: "forward"
|
HASH_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "x"
rightEmbeddingField: "none"
| |
| PROJECTION_SIMPLE
| transformBy: { "_id" : false }
| |
| COLLSCAN [test.basic_joins_md]
| direction: "forward"
|
COLLSCAN [test.basic_joins_md_foreign1]
direction: "forward"
```
### With random order, seed 45, nested loop joins
usedJoinOptimization: true
```
HASH_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "none"
rightEmbeddingField: "x"
| |
| COLLSCAN [test.basic_joins_md_foreign1]
| direction: "forward"
|
HASH_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "y"
rightEmbeddingField: "none"
| |
| PROJECTION_SIMPLE
| transformBy: { "_id" : false }
| |
| COLLSCAN [test.basic_joins_md]
| direction: "forward"
|
COLLSCAN [test.basic_joins_md_foreign2]
direction: "forward"
```
### With random order, seed 45, hash join enabled
usedJoinOptimization: true
```
HASH_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "none"
rightEmbeddingField: "x"
| |
| COLLSCAN [test.basic_joins_md_foreign1]
| direction: "forward"
|
HASH_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "y"
rightEmbeddingField: "none"
| |
| PROJECTION_SIMPLE
| transformBy: { "_id" : false }
| |
| COLLSCAN [test.basic_joins_md]
| direction: "forward"
|
COLLSCAN [test.basic_joins_md_foreign2]
direction: "forward"
```
### With fixed order, index join
usedJoinOptimization: true
```
INDEXED_NESTED_LOOP_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "none"
rightEmbeddingField: "x"
| |
| FETCH [test.basic_joins_md_foreign1]
|
| |
| INDEX_PROBE_NODE [test.basic_joins_md_foreign1]
| keyPattern: { "a" : 1 }
| indexName: "a_1"
| isMultiKey: false
| isUnique: false
| isSparse: false
| isPartial: false
|
NESTED_LOOP_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "y"
rightEmbeddingField: "none"
| |
| PROJECTION_SIMPLE
| transformBy: { "_id" : false }
| |
| COLLSCAN [test.basic_joins_md]
| direction: "forward"
|
COLLSCAN [test.basic_joins_md_foreign2]
direction: "forward"
```
### With bottom-up plan enumeration and indexes
usedJoinOptimization: true
```
INDEXED_NESTED_LOOP_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "none"
rightEmbeddingField: "y"
| |
| FETCH [test.basic_joins_md_foreign2]
|
| |
| INDEX_PROBE_NODE [test.basic_joins_md_foreign2]
| keyPattern: { "b" : 1 }
| indexName: "b_1"
| isMultiKey: false
| isUnique: false
| isSparse: false
| isPartial: false
|
INDEXED_NESTED_LOOP_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "none"
rightEmbeddingField: "x"
| |
| FETCH [test.basic_joins_md_foreign1]
|
| |
| INDEX_PROBE_NODE [test.basic_joins_md_foreign1]
| keyPattern: { "a" : 1 }
| indexName: "a_1"
| isMultiKey: false
| isUnique: false
| isSparse: false
| isPartial: false
|
PROJECTION_SIMPLE
transformBy: { "_id" : false }
|
COLLSCAN [test.basic_joins_md]
direction: "forward"
```

View File

@ -3233,3 +3233,335 @@ rightEmbeddingField: "k.y.z"
COLLSCAN [test.basic_joins_md] COLLSCAN [test.basic_joins_md]
direction: "forward" direction: "forward"
``` ```
## 10. Basic example with a $project excluding a field from the base collection
### No join opt
### Pipeline
```json
[
{
"$project" : {
"_id" : false
}
},
{
"$lookup" : {
"from" : "basic_joins_md_foreign1",
"as" : "x",
"localField" : "a",
"foreignField" : "a"
}
},
{
"$unwind" : "$x"
},
{
"$lookup" : {
"from" : "basic_joins_md_foreign2",
"as" : "y",
"localField" : "b",
"foreignField" : "b"
}
},
{
"$unwind" : "$y"
}
]
```
### Results
```json
{ "a" : 1, "b" : "bar", "x" : { "_id" : 0, "a" : 1, "c" : "zoo", "d" : 1 }, "y" : { "_id" : 0, "b" : "bar", "d" : 2 } }
{ "a" : 1, "b" : "bar", "x" : { "_id" : 0, "a" : 1, "c" : "zoo", "d" : 1 }, "y" : { "_id" : 1, "b" : "bar", "d" : 6 } }
{ "a" : 2, "b" : "bar", "x" : { "_id" : 1, "a" : 2, "c" : "blah", "d" : 2 }, "y" : { "_id" : 0, "b" : "bar", "d" : 2 } }
{ "a" : 2, "b" : "bar", "x" : { "_id" : 1, "a" : 2, "c" : "blah", "d" : 2 }, "y" : { "_id" : 1, "b" : "bar", "d" : 6 } }
{ "a" : 2, "b" : "bar", "x" : { "_id" : 2, "a" : 2, "c" : "x", "d" : 3 }, "y" : { "_id" : 0, "b" : "bar", "d" : 2 } }
{ "a" : 2, "b" : "bar", "x" : { "_id" : 2, "a" : 2, "c" : "x", "d" : 3 }, "y" : { "_id" : 1, "b" : "bar", "d" : 6 } }
```
### Summarized explain
Execution Engine: classic
```json
{
"queryShapeHash" : "9A2C9BDFA638F0C7F703251AC894A297CDDB8AF81B931E490B488BCF87386796",
"stages" : [
{
"$cursor" : {
"rejectedPlans" : [ ],
"winningPlan" : {
"inputStage" : {
"direction" : "forward",
"nss" : "test.basic_joins_md",
"stage" : "COLLSCAN"
},
"isCached" : false,
"stage" : "PROJECTION_SIMPLE",
"transformBy" : {
"_id" : false
}
}
}
},
{
"$lookup" : {
"as" : "x",
"foreignField" : "a",
"from" : "basic_joins_md_foreign1",
"localField" : "a",
"unwinding" : {
"preserveNullAndEmptyArrays" : false
}
}
},
{
"$lookup" : {
"as" : "y",
"foreignField" : "b",
"from" : "basic_joins_md_foreign2",
"localField" : "b",
"unwinding" : {
"preserveNullAndEmptyArrays" : false
}
}
}
]
}
```
### With bottom-up plan enumeration (left-deep)
usedJoinOptimization: true
```
HASH_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "none"
rightEmbeddingField: "y"
| |
| COLLSCAN [test.basic_joins_md_foreign2]
| direction: "forward"
|
HASH_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "none"
rightEmbeddingField: "x"
| |
| COLLSCAN [test.basic_joins_md_foreign1]
| direction: "forward"
|
PROJECTION_SIMPLE
transformBy: { "_id" : false }
|
COLLSCAN [test.basic_joins_md]
direction: "forward"
```
### With bottom-up plan enumeration (right-deep)
usedJoinOptimization: true
```
HASH_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "y"
rightEmbeddingField: "none"
| |
| HASH_JOIN_EMBEDDING [a = a]
| leftEmbeddingField: "none"
| rightEmbeddingField: "x"
| | |
| | COLLSCAN [test.basic_joins_md_foreign1]
| | direction: "forward"
| |
| PROJECTION_SIMPLE
| transformBy: { "_id" : false }
| |
| COLLSCAN [test.basic_joins_md]
| direction: "forward"
|
COLLSCAN [test.basic_joins_md_foreign2]
direction: "forward"
```
### With bottom-up plan enumeration (zig-zag)
usedJoinOptimization: true
```
HASH_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "none"
rightEmbeddingField: "y"
| |
| COLLSCAN [test.basic_joins_md_foreign2]
| direction: "forward"
|
HASH_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "none"
rightEmbeddingField: "x"
| |
| COLLSCAN [test.basic_joins_md_foreign1]
| direction: "forward"
|
PROJECTION_SIMPLE
transformBy: { "_id" : false }
|
COLLSCAN [test.basic_joins_md]
direction: "forward"
```
### With random order, seed 44, nested loop joins
usedJoinOptimization: true
```
NESTED_LOOP_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "none"
rightEmbeddingField: "y"
| |
| COLLSCAN [test.basic_joins_md_foreign2]
| direction: "forward"
|
NESTED_LOOP_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "x"
rightEmbeddingField: "none"
| |
| PROJECTION_SIMPLE
| transformBy: { "_id" : false }
| |
| COLLSCAN [test.basic_joins_md]
| direction: "forward"
|
COLLSCAN [test.basic_joins_md_foreign1]
direction: "forward"
```
### With random order, seed 44, hash join enabled
usedJoinOptimization: true
```
HASH_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "none"
rightEmbeddingField: "y"
| |
| COLLSCAN [test.basic_joins_md_foreign2]
| direction: "forward"
|
HASH_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "x"
rightEmbeddingField: "none"
| |
| PROJECTION_SIMPLE
| transformBy: { "_id" : false }
| |
| COLLSCAN [test.basic_joins_md]
| direction: "forward"
|
COLLSCAN [test.basic_joins_md_foreign1]
direction: "forward"
```
### With random order, seed 45, nested loop joins
usedJoinOptimization: true
```
HASH_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "none"
rightEmbeddingField: "x"
| |
| COLLSCAN [test.basic_joins_md_foreign1]
| direction: "forward"
|
HASH_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "y"
rightEmbeddingField: "none"
| |
| PROJECTION_SIMPLE
| transformBy: { "_id" : false }
| |
| COLLSCAN [test.basic_joins_md]
| direction: "forward"
|
COLLSCAN [test.basic_joins_md_foreign2]
direction: "forward"
```
### With random order, seed 45, hash join enabled
usedJoinOptimization: true
```
HASH_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "none"
rightEmbeddingField: "x"
| |
| COLLSCAN [test.basic_joins_md_foreign1]
| direction: "forward"
|
HASH_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "y"
rightEmbeddingField: "none"
| |
| PROJECTION_SIMPLE
| transformBy: { "_id" : false }
| |
| COLLSCAN [test.basic_joins_md]
| direction: "forward"
|
COLLSCAN [test.basic_joins_md_foreign2]
direction: "forward"
```
### With fixed order, index join
usedJoinOptimization: true
```
INDEXED_NESTED_LOOP_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "none"
rightEmbeddingField: "x"
| |
| FETCH [test.basic_joins_md_foreign1]
|
| |
| INDEX_PROBE_NODE [test.basic_joins_md_foreign1]
| keyPattern: { "a" : 1 }
| indexName: "a_1"
| isMultiKey: false
| isUnique: false
| isSparse: false
| isPartial: false
|
NESTED_LOOP_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "y"
rightEmbeddingField: "none"
| |
| PROJECTION_SIMPLE
| transformBy: { "_id" : false }
| |
| COLLSCAN [test.basic_joins_md]
| direction: "forward"
|
COLLSCAN [test.basic_joins_md_foreign2]
direction: "forward"
```
### With bottom-up plan enumeration and indexes
usedJoinOptimization: true
```
INDEXED_NESTED_LOOP_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "none"
rightEmbeddingField: "y"
| |
| FETCH [test.basic_joins_md_foreign2]
|
| |
| INDEX_PROBE_NODE [test.basic_joins_md_foreign2]
| keyPattern: { "b" : 1 }
| indexName: "b_1"
| isMultiKey: false
| isUnique: false
| isSparse: false
| isPartial: false
|
INDEXED_NESTED_LOOP_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "none"
rightEmbeddingField: "x"
| |
| FETCH [test.basic_joins_md_foreign1]
|
| |
| INDEX_PROBE_NODE [test.basic_joins_md_foreign1]
| keyPattern: { "a" : 1 }
| indexName: "a_1"
| isMultiKey: false
| isUnique: false
| isSparse: false
| isPartial: false
|
PROJECTION_SIMPLE
transformBy: { "_id" : false }
|
COLLSCAN [test.basic_joins_md]
direction: "forward"
```

View File

@ -3193,3 +3193,328 @@ rightEmbeddingField: "k.y.z"
COLLSCAN [test.basic_joins_md] COLLSCAN [test.basic_joins_md]
direction: "forward" direction: "forward"
``` ```
## 10. Basic example with a $project excluding a field from the base collection
### No join opt
### Pipeline
```json
[
{
"$project" : {
"_id" : false
}
},
{
"$lookup" : {
"from" : "basic_joins_md_foreign1",
"as" : "x",
"localField" : "a",
"foreignField" : "a"
}
},
{
"$unwind" : "$x"
},
{
"$lookup" : {
"from" : "basic_joins_md_foreign2",
"as" : "y",
"localField" : "b",
"foreignField" : "b"
}
},
{
"$unwind" : "$y"
}
]
```
### Results
```json
{ "a" : 1, "b" : "bar", "x" : { "_id" : 0, "a" : 1, "c" : "zoo", "d" : 1 }, "y" : { "_id" : 0, "b" : "bar", "d" : 2 } }
{ "a" : 1, "b" : "bar", "x" : { "_id" : 0, "a" : 1, "c" : "zoo", "d" : 1 }, "y" : { "_id" : 1, "b" : "bar", "d" : 6 } }
{ "a" : 2, "b" : "bar", "x" : { "_id" : 1, "a" : 2, "c" : "blah", "d" : 2 }, "y" : { "_id" : 0, "b" : "bar", "d" : 2 } }
{ "a" : 2, "b" : "bar", "x" : { "_id" : 1, "a" : 2, "c" : "blah", "d" : 2 }, "y" : { "_id" : 1, "b" : "bar", "d" : 6 } }
{ "a" : 2, "b" : "bar", "x" : { "_id" : 2, "a" : 2, "c" : "x", "d" : 3 }, "y" : { "_id" : 0, "b" : "bar", "d" : 2 } }
{ "a" : 2, "b" : "bar", "x" : { "_id" : 2, "a" : 2, "c" : "x", "d" : 3 }, "y" : { "_id" : 1, "b" : "bar", "d" : 6 } }
```
### Summarized explain
Execution Engine: sbe
```json
{
"queryShapeHash" : "9A2C9BDFA638F0C7F703251AC894A297CDDB8AF81B931E490B488BCF87386796",
"rejectedPlans" : [ ],
"winningPlan" : {
"asField" : "y",
"foreignCollection" : "test.basic_joins_md_foreign2",
"foreignField" : "b",
"inputStage" : {
"asField" : "x",
"foreignCollection" : "test.basic_joins_md_foreign1",
"foreignField" : "a",
"inputStage" : {
"inputStage" : {
"direction" : "forward",
"filter" : {
},
"nss" : "test.basic_joins_md",
"stage" : "COLLSCAN"
},
"stage" : "PROJECTION_SIMPLE",
"transformBy" : {
"_id" : false
}
},
"localField" : "a",
"scanDirection" : "forward",
"stage" : "EQ_LOOKUP_UNWIND",
"strategy" : "HashJoin"
},
"localField" : "b",
"planNodeId" : 4,
"scanDirection" : "forward",
"stage" : "EQ_LOOKUP_UNWIND",
"strategy" : "HashJoin"
}
}
```
### With bottom-up plan enumeration (left-deep)
usedJoinOptimization: true
```
HASH_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "none"
rightEmbeddingField: "y"
| |
| COLLSCAN [test.basic_joins_md_foreign2]
| direction: "forward"
|
HASH_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "none"
rightEmbeddingField: "x"
| |
| COLLSCAN [test.basic_joins_md_foreign1]
| direction: "forward"
|
PROJECTION_SIMPLE
transformBy: { "_id" : false }
|
COLLSCAN [test.basic_joins_md]
direction: "forward"
```
### With bottom-up plan enumeration (right-deep)
usedJoinOptimization: true
```
HASH_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "y"
rightEmbeddingField: "none"
| |
| HASH_JOIN_EMBEDDING [a = a]
| leftEmbeddingField: "none"
| rightEmbeddingField: "x"
| | |
| | COLLSCAN [test.basic_joins_md_foreign1]
| | direction: "forward"
| |
| PROJECTION_SIMPLE
| transformBy: { "_id" : false }
| |
| COLLSCAN [test.basic_joins_md]
| direction: "forward"
|
COLLSCAN [test.basic_joins_md_foreign2]
direction: "forward"
```
### With bottom-up plan enumeration (zig-zag)
usedJoinOptimization: true
```
HASH_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "none"
rightEmbeddingField: "y"
| |
| COLLSCAN [test.basic_joins_md_foreign2]
| direction: "forward"
|
HASH_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "none"
rightEmbeddingField: "x"
| |
| COLLSCAN [test.basic_joins_md_foreign1]
| direction: "forward"
|
PROJECTION_SIMPLE
transformBy: { "_id" : false }
|
COLLSCAN [test.basic_joins_md]
direction: "forward"
```
### With random order, seed 44, nested loop joins
usedJoinOptimization: true
```
NESTED_LOOP_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "none"
rightEmbeddingField: "y"
| |
| COLLSCAN [test.basic_joins_md_foreign2]
| direction: "forward"
|
NESTED_LOOP_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "x"
rightEmbeddingField: "none"
| |
| PROJECTION_SIMPLE
| transformBy: { "_id" : false }
| |
| COLLSCAN [test.basic_joins_md]
| direction: "forward"
|
COLLSCAN [test.basic_joins_md_foreign1]
direction: "forward"
```
### With random order, seed 44, hash join enabled
usedJoinOptimization: true
```
HASH_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "none"
rightEmbeddingField: "y"
| |
| COLLSCAN [test.basic_joins_md_foreign2]
| direction: "forward"
|
HASH_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "x"
rightEmbeddingField: "none"
| |
| PROJECTION_SIMPLE
| transformBy: { "_id" : false }
| |
| COLLSCAN [test.basic_joins_md]
| direction: "forward"
|
COLLSCAN [test.basic_joins_md_foreign1]
direction: "forward"
```
### With random order, seed 45, nested loop joins
usedJoinOptimization: true
```
HASH_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "none"
rightEmbeddingField: "x"
| |
| COLLSCAN [test.basic_joins_md_foreign1]
| direction: "forward"
|
HASH_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "y"
rightEmbeddingField: "none"
| |
| PROJECTION_SIMPLE
| transformBy: { "_id" : false }
| |
| COLLSCAN [test.basic_joins_md]
| direction: "forward"
|
COLLSCAN [test.basic_joins_md_foreign2]
direction: "forward"
```
### With random order, seed 45, hash join enabled
usedJoinOptimization: true
```
HASH_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "none"
rightEmbeddingField: "x"
| |
| COLLSCAN [test.basic_joins_md_foreign1]
| direction: "forward"
|
HASH_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "y"
rightEmbeddingField: "none"
| |
| PROJECTION_SIMPLE
| transformBy: { "_id" : false }
| |
| COLLSCAN [test.basic_joins_md]
| direction: "forward"
|
COLLSCAN [test.basic_joins_md_foreign2]
direction: "forward"
```
### With fixed order, index join
usedJoinOptimization: true
```
INDEXED_NESTED_LOOP_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "none"
rightEmbeddingField: "x"
| |
| FETCH [test.basic_joins_md_foreign1]
|
| |
| INDEX_PROBE_NODE [test.basic_joins_md_foreign1]
| keyPattern: { "a" : 1 }
| indexName: "a_1"
| isMultiKey: false
| isUnique: false
| isSparse: false
| isPartial: false
|
NESTED_LOOP_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "y"
rightEmbeddingField: "none"
| |
| PROJECTION_SIMPLE
| transformBy: { "_id" : false }
| |
| COLLSCAN [test.basic_joins_md]
| direction: "forward"
|
COLLSCAN [test.basic_joins_md_foreign2]
direction: "forward"
```
### With bottom-up plan enumeration and indexes
usedJoinOptimization: true
```
INDEXED_NESTED_LOOP_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "none"
rightEmbeddingField: "y"
| |
| FETCH [test.basic_joins_md_foreign2]
|
| |
| INDEX_PROBE_NODE [test.basic_joins_md_foreign2]
| keyPattern: { "b" : 1 }
| indexName: "b_1"
| isMultiKey: false
| isUnique: false
| isSparse: false
| isPartial: false
|
INDEXED_NESTED_LOOP_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "none"
rightEmbeddingField: "x"
| |
| FETCH [test.basic_joins_md_foreign1]
|
| |
| INDEX_PROBE_NODE [test.basic_joins_md_foreign1]
| keyPattern: { "a" : 1 }
| indexName: "a_1"
| isMultiKey: false
| isUnique: false
| isSparse: false
| isPartial: false
|
PROJECTION_SIMPLE
transformBy: { "_id" : false }
|
COLLSCAN [test.basic_joins_md]
direction: "forward"
```

View File

@ -3233,3 +3233,335 @@ rightEmbeddingField: "k.y.z"
COLLSCAN [test.basic_joins_md] COLLSCAN [test.basic_joins_md]
direction: "forward" direction: "forward"
``` ```
## 10. Basic example with a $project excluding a field from the base collection
### No join opt
### Pipeline
```json
[
{
"$project" : {
"_id" : false
}
},
{
"$lookup" : {
"from" : "basic_joins_md_foreign1",
"as" : "x",
"localField" : "a",
"foreignField" : "a"
}
},
{
"$unwind" : "$x"
},
{
"$lookup" : {
"from" : "basic_joins_md_foreign2",
"as" : "y",
"localField" : "b",
"foreignField" : "b"
}
},
{
"$unwind" : "$y"
}
]
```
### Results
```json
{ "a" : 1, "b" : "bar", "x" : { "_id" : 0, "a" : 1, "c" : "zoo", "d" : 1 }, "y" : { "_id" : 0, "b" : "bar", "d" : 2 } }
{ "a" : 1, "b" : "bar", "x" : { "_id" : 0, "a" : 1, "c" : "zoo", "d" : 1 }, "y" : { "_id" : 1, "b" : "bar", "d" : 6 } }
{ "a" : 2, "b" : "bar", "x" : { "_id" : 1, "a" : 2, "c" : "blah", "d" : 2 }, "y" : { "_id" : 0, "b" : "bar", "d" : 2 } }
{ "a" : 2, "b" : "bar", "x" : { "_id" : 1, "a" : 2, "c" : "blah", "d" : 2 }, "y" : { "_id" : 1, "b" : "bar", "d" : 6 } }
{ "a" : 2, "b" : "bar", "x" : { "_id" : 2, "a" : 2, "c" : "x", "d" : 3 }, "y" : { "_id" : 0, "b" : "bar", "d" : 2 } }
{ "a" : 2, "b" : "bar", "x" : { "_id" : 2, "a" : 2, "c" : "x", "d" : 3 }, "y" : { "_id" : 1, "b" : "bar", "d" : 6 } }
```
### Summarized explain
Execution Engine: classic
```json
{
"queryShapeHash" : "9A2C9BDFA638F0C7F703251AC894A297CDDB8AF81B931E490B488BCF87386796",
"stages" : [
{
"$cursor" : {
"rejectedPlans" : [ ],
"winningPlan" : {
"inputStage" : {
"direction" : "forward",
"nss" : "test.basic_joins_md",
"stage" : "COLLSCAN"
},
"isCached" : false,
"stage" : "PROJECTION_SIMPLE",
"transformBy" : {
"_id" : false
}
}
}
},
{
"$lookup" : {
"as" : "x",
"foreignField" : "a",
"from" : "basic_joins_md_foreign1",
"localField" : "a",
"unwinding" : {
"preserveNullAndEmptyArrays" : false
}
}
},
{
"$lookup" : {
"as" : "y",
"foreignField" : "b",
"from" : "basic_joins_md_foreign2",
"localField" : "b",
"unwinding" : {
"preserveNullAndEmptyArrays" : false
}
}
}
]
}
```
### With bottom-up plan enumeration (left-deep)
usedJoinOptimization: true
```
HASH_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "none"
rightEmbeddingField: "y"
| |
| COLLSCAN [test.basic_joins_md_foreign2]
| direction: "forward"
|
HASH_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "none"
rightEmbeddingField: "x"
| |
| COLLSCAN [test.basic_joins_md_foreign1]
| direction: "forward"
|
PROJECTION_SIMPLE
transformBy: { "_id" : false }
|
COLLSCAN [test.basic_joins_md]
direction: "forward"
```
### With bottom-up plan enumeration (right-deep)
usedJoinOptimization: true
```
HASH_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "y"
rightEmbeddingField: "none"
| |
| HASH_JOIN_EMBEDDING [a = a]
| leftEmbeddingField: "none"
| rightEmbeddingField: "x"
| | |
| | COLLSCAN [test.basic_joins_md_foreign1]
| | direction: "forward"
| |
| PROJECTION_SIMPLE
| transformBy: { "_id" : false }
| |
| COLLSCAN [test.basic_joins_md]
| direction: "forward"
|
COLLSCAN [test.basic_joins_md_foreign2]
direction: "forward"
```
### With bottom-up plan enumeration (zig-zag)
usedJoinOptimization: true
```
HASH_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "none"
rightEmbeddingField: "y"
| |
| COLLSCAN [test.basic_joins_md_foreign2]
| direction: "forward"
|
HASH_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "none"
rightEmbeddingField: "x"
| |
| COLLSCAN [test.basic_joins_md_foreign1]
| direction: "forward"
|
PROJECTION_SIMPLE
transformBy: { "_id" : false }
|
COLLSCAN [test.basic_joins_md]
direction: "forward"
```
### With random order, seed 44, nested loop joins
usedJoinOptimization: true
```
NESTED_LOOP_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "none"
rightEmbeddingField: "y"
| |
| COLLSCAN [test.basic_joins_md_foreign2]
| direction: "forward"
|
NESTED_LOOP_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "x"
rightEmbeddingField: "none"
| |
| PROJECTION_SIMPLE
| transformBy: { "_id" : false }
| |
| COLLSCAN [test.basic_joins_md]
| direction: "forward"
|
COLLSCAN [test.basic_joins_md_foreign1]
direction: "forward"
```
### With random order, seed 44, hash join enabled
usedJoinOptimization: true
```
HASH_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "none"
rightEmbeddingField: "y"
| |
| COLLSCAN [test.basic_joins_md_foreign2]
| direction: "forward"
|
HASH_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "x"
rightEmbeddingField: "none"
| |
| PROJECTION_SIMPLE
| transformBy: { "_id" : false }
| |
| COLLSCAN [test.basic_joins_md]
| direction: "forward"
|
COLLSCAN [test.basic_joins_md_foreign1]
direction: "forward"
```
### With random order, seed 45, nested loop joins
usedJoinOptimization: true
```
HASH_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "none"
rightEmbeddingField: "x"
| |
| COLLSCAN [test.basic_joins_md_foreign1]
| direction: "forward"
|
HASH_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "y"
rightEmbeddingField: "none"
| |
| PROJECTION_SIMPLE
| transformBy: { "_id" : false }
| |
| COLLSCAN [test.basic_joins_md]
| direction: "forward"
|
COLLSCAN [test.basic_joins_md_foreign2]
direction: "forward"
```
### With random order, seed 45, hash join enabled
usedJoinOptimization: true
```
HASH_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "none"
rightEmbeddingField: "x"
| |
| COLLSCAN [test.basic_joins_md_foreign1]
| direction: "forward"
|
HASH_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "y"
rightEmbeddingField: "none"
| |
| PROJECTION_SIMPLE
| transformBy: { "_id" : false }
| |
| COLLSCAN [test.basic_joins_md]
| direction: "forward"
|
COLLSCAN [test.basic_joins_md_foreign2]
direction: "forward"
```
### With fixed order, index join
usedJoinOptimization: true
```
INDEXED_NESTED_LOOP_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "none"
rightEmbeddingField: "x"
| |
| FETCH [test.basic_joins_md_foreign1]
|
| |
| INDEX_PROBE_NODE [test.basic_joins_md_foreign1]
| keyPattern: { "a" : 1 }
| indexName: "a_1"
| isMultiKey: false
| isUnique: false
| isSparse: false
| isPartial: false
|
NESTED_LOOP_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "y"
rightEmbeddingField: "none"
| |
| PROJECTION_SIMPLE
| transformBy: { "_id" : false }
| |
| COLLSCAN [test.basic_joins_md]
| direction: "forward"
|
COLLSCAN [test.basic_joins_md_foreign2]
direction: "forward"
```
### With bottom-up plan enumeration and indexes
usedJoinOptimization: true
```
INDEXED_NESTED_LOOP_JOIN_EMBEDDING [b = b]
leftEmbeddingField: "none"
rightEmbeddingField: "y"
| |
| FETCH [test.basic_joins_md_foreign2]
|
| |
| INDEX_PROBE_NODE [test.basic_joins_md_foreign2]
| keyPattern: { "b" : 1 }
| indexName: "b_1"
| isMultiKey: false
| isUnique: false
| isSparse: false
| isPartial: false
|
INDEXED_NESTED_LOOP_JOIN_EMBEDDING [a = a]
leftEmbeddingField: "none"
rightEmbeddingField: "x"
| |
| FETCH [test.basic_joins_md_foreign1]
|
| |
| INDEX_PROBE_NODE [test.basic_joins_md_foreign1]
| keyPattern: { "a" : 1 }
| indexName: "a_1"
| isMultiKey: false
| isUnique: false
| isSparse: false
| isPartial: false
|
PROJECTION_SIMPLE
transformBy: { "_id" : false }
|
COLLSCAN [test.basic_joins_md]
direction: "forward"
```

View File

@ -315,3 +315,12 @@ runBasicJoinTest([
{$lookup: {from: foreignColl2.getName(), as: "k.y.z", localField: "w.y.d", foreignField: "d"}}, {$lookup: {from: foreignColl2.getName(), as: "k.y.z", localField: "w.y.d", foreignField: "d"}},
{$unwind: "$k.y.z"}, {$unwind: "$k.y.z"},
]); ]);
section("Basic example with a $project excluding a field from the base collection");
runBasicJoinTest([
{$project: {_id: false}},
{$lookup: {from: foreignColl1.getName(), as: "x", localField: "a", foreignField: "a"}},
{$unwind: "$x"},
{$lookup: {from: foreignColl2.getName(), as: "y", localField: "b", foreignField: "b"}},
{$unwind: "$y"},
]);

View File

@ -1655,8 +1655,11 @@ std::pair<SbStage, PlanStageSlots> generateJoinResult(const BinaryJoinEmbeddingN
nodes.emplace_back(leftOutputs.get(SlotBasedStageBuilder::kResult)); nodes.emplace_back(leftOutputs.get(SlotBasedStageBuilder::kResult));
} }
for (const auto& field : fieldEffect.getFieldList()) { for (const auto& field : fieldEffect.getFieldList()) {
if ((!node->rightEmbeddingField || field != node->rightEmbeddingField->fullPath()) && if (!fieldEffect.isAllowedField(field)) {
(!node->leftEmbeddingField || field != node->leftEmbeddingField->fullPath())) { paths.emplace_back(field);
nodes.emplace_back(ProjectNode::Drop{});
} else if ((!node->rightEmbeddingField || field != node->rightEmbeddingField->fullPath()) &&
(!node->leftEmbeddingField || field != node->leftEmbeddingField->fullPath())) {
paths.emplace_back(field); paths.emplace_back(field);
// TODO: SERVER-113230 in case of conflict, we give priority to the left side. Depending // TODO: SERVER-113230 in case of conflict, we give priority to the left side. Depending
// on the join graph, an embedding having the same name of a top-level field in the base // on the join graph, an embedding having the same name of a top-level field in the base