mirror of https://github.com/mongodb/mongo
SERVER-114748 Process dropping fields when materializing result in JOO (#45240)
GitOrigin-RevId: b34440744ed2700cce07aeebd13cc8fd810959f1
This commit is contained in:
parent
1f7f8f8c54
commit
861513e6ea
|
|
@ -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"
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -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"},
|
||||||
|
]);
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue