Files
mongo/jstests/aggregation/bugs/server11675.js
Mathias Stearn 0ffa726629 SERVER-12708 Report errors from shards when mergingPresorted results.
To prevent similar issues from happening in the future, all code to pull
results off of the shards cursors in agg now go through a single function.
This function was enhanced to propagate runtime error codes as we already do
with parse-time errors reported from the shards.
2014-02-19 17:15:07 -05:00

163 lines
7.3 KiB
JavaScript

// SERVER-11675 Text search integration with aggregation
load('jstests/aggregation/extras/utils.js');
var server11675 = function() {
var t = db.server11675;
t.drop();
if (typeof(RUNNING_IN_SHARDED_AGG_TEST) != 'undefined') { // see end of testshard1.js
db.adminCommand( { shardcollection : t.getFullName(), key : { "_id" : 1 } } );
}
t.insert({_id: 1, text: "apple", words: 1});
t.insert({_id: 2, text: "banana", words: 1});
t.insert({_id: 3, text: "apple banana", words: 2});
t.insert({_id: 4, text: "cantaloupe", words: 1});
t.ensureIndex({text: "text"});
// query should have subfields query, project, sort, skip and limit. All but query are optional.
var assertSameAsFind = function(query) {
var cursor = t.find(query.query);
var pipeline = [{$match: query.query}];
if ('project' in query) {
cursor = t.find(query.query, query.project); // no way to add to constructed cursor
pipeline.push({$project: query.project});
}
if ('sort' in query) {
cursor = cursor.sort(query.sort);
pipeline.push({$sort: query.sort});
}
if ('skip' in query) {
cursor = cursor.skip(query.skip);
pipeline.push({$skip: query.skip});
}
if ('limit' in query) {
cursor = cursor.limit(query.limit);
pipeline.push({$limit: query.limit});
}
var findRes = cursor.toArray();
var aggRes = t.aggregate(pipeline).toArray();
assert.docEq(aggRes, findRes);
}
assertSameAsFind({query: {}}); // sanity check
assertSameAsFind({query: {$text:{$search:"apple"}}});
assertSameAsFind({query: {$and:[{$text:{$search:"apple"}}, {_id:1}]}});
assertSameAsFind({query: {$text:{$search:"apple"}}
,project: {_id:1, score: {$meta: "textScore"}}
});
assertSameAsFind({query: {$text:{$search:"apple banana"}}
,project: {_id:1, score: {$meta: "textScore"}}
});
assertSameAsFind({query: {$text:{$search:"apple banana"}}
,project: {_id:1, score: {$meta: "textScore"}}
,sort: {score: {$meta: "textScore"}}
});
assertSameAsFind({query: {$text:{$search:"apple banana"}}
,project: {_id:1, score: {$meta: "textScore"}}
,sort: {score: {$meta: "textScore"}}
,limit: 1
});
assertSameAsFind({query: {$text:{$search:"apple banana"}}
,project: {_id:1, score: {$meta: "textScore"}}
,sort: {score: {$meta: "textScore"}}
,skip: 1
});
assertSameAsFind({query: {$text:{$search:"apple banana"}}
,project: {_id:1, score: {$meta: "textScore"}}
,sort: {score: {$meta: "textScore"}}
,skip: 1
,limit: 1
});
// sharded find requires projecting the score to sort, but sharded agg does not.
var findRes = t.find({$text: {$search: "apple banana"}}, {textScore: {$meta: 'textScore'}})
.sort({textScore: {$meta: 'textScore'}})
.map(function(obj) {
delete obj.textScore; // remove it to match agg output
return obj;
});
var res = t.aggregate([{$match: {$text: {$search: 'apple banana'}}}
,{$sort: {textScore: {$meta: 'textScore'}}}
]).toArray();
assert.eq(res, findRes);
// Make sure {$meta: 'textScore'} can be used as a sub-expression
var res = t.aggregate([{$match: {_id:1, $text: {$search: 'apple'}}}
,{$project: {words: 1
,score: {$meta: 'textScore'}
,wordsTimesScore: {$multiply: ['$words', {$meta:'textScore'}]}
}}
]).toArray();
assert.eq(res[0].wordsTimesScore, res[0].words * res[0].score, tojson(res));
// And can be used in $group
var res = t.aggregate([{$match: {_id: 1, $text: {$search: 'apple banana'}}}
,{$group: {_id: {$meta: 'textScore'}
,score: {$first: {$meta: 'textScore'}}
}}
]).toArray();
assert.eq(res[0]._id, res[0].score, tojson(res));
// Make sure metadata crosses shard -> merger boundary
var res = t.aggregate([{$match: {_id:1, $text: {$search: 'apple'}}}
,{$project: {scoreOnShard: {$meta: 'textScore'} }}
,{$limit:1} // force a split. later stages run on merger
,{$project: {scoreOnShard:1
,scoreOnMerger: {$meta: 'textScore'} }}
]).toArray();
assert.eq(res[0].scoreOnMerger, res[0].scoreOnShard);
var score = res[0].scoreOnMerger; // save for later tests
// Make sure metadata crosses shard -> merger boundary even if not used on shard
var res = t.aggregate([{$match: {_id:1, $text: {$search: 'apple'}}}
,{$limit:1} // force a split. later stages run on merger
,{$project: {scoreOnShard:1
,scoreOnMerger: {$meta: 'textScore'} }}
]).toArray();
assert.eq(res[0].scoreOnMerger, score);
// Make sure metadata works if first $project doesn't use it.
var res = t.aggregate([{$match: {_id:1, $text: {$search: 'apple'}}}
,{$project: {_id:1}}
,{$project: {_id:1
,score: {$meta: 'textScore'} }}
]).toArray();
assert.eq(res[0].score, score);
// Make sure the metadata is 'missing()' when it doesn't exist because it was never created
var res = t.aggregate([{$project: {_id: 1, score: {$meta: 'textScore'}}}]).toArray();
assert(!("score" in res[0]));
// Make sure the metadata is 'missing()' when it doesn't exist because the document changed
var res = t.aggregate([{$match: {_id: 1, $text: {$search: 'apple banana'}}},
{$group: {_id: 1, score: {$first: {$meta: 'textScore'}}}},
{$project: {_id: 1, scoreAgain: {$meta: 'textScore'}}},
]).toArray();
assert(!("scoreAgain" in res[0]));
// Make sure metadata works after a $unwind
t.insert({_id: 5, text: 'mango', words: [1, 2, 3]});
var res = t.aggregate([{$match: {$text: {$search: 'mango'}}},
{$project: {score: {$meta: "textScore"}, _id:1, words: 1}},
{$unwind: '$words'},
{$project: {scoreAgain: {$meta: "textScore"}, score: 1}}
]).toArray();
assert.eq(res[0].scoreAgain, res[0].score);
// Error checking
// $match, but wrong position
assertErrorCode(t, [{$sort: {text: 1}} ,{$match: {$text: {$search: 'apple banana'}}}], 17313);
// wrong $stage, but correct position
assertErrorCode(t, [{$project: {searchValue: {$text: {$search: 'apple banana'}}}}], 15999);
assertErrorCode(t, [{$sort: {$text: {$search: 'apple banana'}}}], 17312);
}
server11675();