1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
// Tests that given a $text stage before a $lookup stage, the $lookup's subpipeline cannot
// reference the text score metadata from that $text search.
// TODO: Reenable test on passthroughs with sharded collections as part of SERVER-38996.
// @tags: [assumes_unsharded_collection]
(function() {
"use strict";
load("jstests/aggregation/extras/utils.js"); // For "assertErrorCode".
const outer = db.outer;
const inner = db.inner;
outer.drop();
inner.drop();
const kNoTextScoreAvailableErrCode = 40218;
// This pipeline is never legal, because the subpipeline projects out a textScore but does not
// begin with a $text search.
let pipeline = [
{$match: {$text: {$search: "foo"}}},
{$lookup: {from: "inner", as: "as", pipeline: [{$project: {score: {$meta: "textScore"}}}]}}
];
assert.commandWorked(outer.insert({_id: 100, a: "foo"}));
assert.commandWorked(inner.insert({_id: 100, a: "bar apple banana"}));
// Neither 'outer' nor 'inner' have text indexes.
assertErrorCode(outer, pipeline, ErrorCodes.IndexNotFound);
// Only 'outer' has a text index.
assert.commandWorked(outer.createIndex({a: "text"}, {name: "outer_first_index"}));
assertErrorCode(outer, pipeline, kNoTextScoreAvailableErrCode);
// Only 'inner' has a text index.
assert.commandWorked(outer.dropIndex("outer_first_index"));
assert.commandWorked(inner.createIndex({a: "text"}));
assertErrorCode(outer, pipeline, ErrorCodes.IndexNotFound);
// Both 'outer' and 'inner' have a text index.
assert.commandWorked(outer.createIndex({a: "text"}));
assertErrorCode(outer, pipeline, kNoTextScoreAvailableErrCode);
// A pipeline with two text searches, one within a $lookup, will work.
pipeline = [
{$match: {$text: {$search: "foo"}}},
{
$lookup: {
from: "inner",
as: "as",
pipeline: [
{$match: {$text: {$search: "bar apple banana hello"}}},
{$project: {score: {$meta: "textScore"}}}
]
}
}
];
let expected = [{"_id": 100, "a": "foo", "as": [{"_id": 100, "score": 2}]}];
assert.eq(outer.aggregate(pipeline).toArray(), expected);
// A lookup with a text search in the subpipeline will correctly perform that search on 'from'.
pipeline = [{
$lookup: {
from: "inner",
as: "as",
pipeline: [{$match: {$text: {$search: "bar apple banana hello"}}}]
}
}];
expected = [{"_id": 100, "a": "foo", "as": [{"_id": 100, "a": "bar apple banana"}]}];
assert.eq(outer.aggregate(pipeline).toArray(), expected);
// A lookup with two text searches and two text score $projects will have the text scores
// reference the relevant text search.
pipeline = [
{$match: {$text: {$search: "foo"}}},
{
$lookup: {
from: "inner",
as: "as",
pipeline: [
{$match: {$text: {$search: "bar apple banana hello"}}},
{$project: {score: {$meta: "textScore"}}}
]
}
},
{$project: {score: {$meta: "textScore"}, as: 1}},
];
expected = [{"_id": 100, "as": [{"_id": 100, "score": 2}], "score": 1.1}];
assert.eq(outer.aggregate(pipeline).toArray(), expected);
// Given a $text stage in the 'from' pipeline, the outer pipeline will not be able to access
// this $text stage's text score.
pipeline = [
{
$lookup: {
from: "inner",
as: "as",
pipeline: [{$match: {$text: {$search: "bar apple banana hello"}}}]
}
},
{$project: {score: {$meta: "textScore"}, as: 1}},
];
assertErrorCode(outer, pipeline, kNoTextScoreAvailableErrCode);
}());
|