Files
mongo/jstests/aggregation/bugs/match.js

171 lines
6.1 KiB
JavaScript

// Check $match pipeline stage.
// - Filtering behavior equivalent to a mongo query.
// - $where and geo operators are not allowed
(function() {
"use strict";
load('jstests/aggregation/extras/utils.js');
const coll = db.jstests_aggregation_match;
coll.drop();
const identityProjection = {
_id: '$_id',
a: '$a'
};
/** Assert that an aggregation generated the expected error. */
function assertError(expectedCode, matchSpec) {
const matchStage = {$match: matchSpec};
// Check where matching is folded in to DocumentSourceCursor.
assertErrorCode(coll, [matchStage], expectedCode);
// Check where matching is not folded in to DocumentSourceCursor.
assertErrorCode(coll, [{$project: identityProjection}, matchStage], expectedCode);
}
/** Assert that the contents of two arrays are equal, ignoring element ordering. */
function assertEqualResultsUnordered(one, two) {
let oneStr = one.map(function(x) {
return tojson(x);
});
let twoStr = two.map(function(x) {
return tojson(x);
});
oneStr.sort();
twoStr.sort();
assert.eq(oneStr, twoStr);
}
/** Assert that an aggregation result is as expected. */
function assertResults(expectedResults, matchSpec) {
const findResults = coll.find(matchSpec).toArray();
if (expectedResults) {
assertEqualResultsUnordered(expectedResults, findResults);
}
const matchStage = {$match: matchSpec};
// Check where matching is folded in to DocumentSourceCursor.
assertEqualResultsUnordered(findResults, coll.aggregate(matchStage).toArray());
// Check where matching is not folded in to DocumentSourceCursor.
assertEqualResultsUnordered(
findResults, coll.aggregate({$project: identityProjection}, matchStage).toArray());
}
// Invalid matcher syntax.
assertError(2, {a: {$mod: [0 /* invalid */, 0]}});
// $where not allowed.
assertError(ErrorCodes.BadValue, {$where: 'true'});
// Geo not allowed.
assertError(ErrorCodes.BadValue, {$match: {a: {$near: [0, 0]}}});
function checkMatchResults(indexed) {
// No results.
coll.remove({});
assertResults([], {});
assert.commandWorked(coll.insert({_id: 0, a: 1}));
assert.commandWorked(coll.insert({_id: 1, a: 2}));
assert.commandWorked(coll.insert({_id: 2, a: 3}));
// Empty query.
assertResults([{_id: 0, a: 1}, {_id: 1, a: 2}, {_id: 2, a: 3}], {});
// Simple queries.
assertResults([{_id: 0, a: 1}], {a: 1});
assertResults([{_id: 1, a: 2}], {a: 2});
assertResults([{_id: 1, a: 2}, {_id: 2, a: 3}], {a: {$gt: 1}});
assertResults([{_id: 0, a: 1}, {_id: 1, a: 2}], {a: {$lte: 2}});
assertResults([{_id: 0, a: 1}, {_id: 2, a: 3}], {a: {$in: [1, 3]}});
// Regular expression.
coll.remove({});
assert.commandWorked(coll.insert({_id: 0, a: 'x'}));
assert.commandWorked(coll.insert({_id: 1, a: 'yx'}));
assertResults([{_id: 0, a: 'x'}], {a: /^x/});
assertResults([{_id: 0, a: 'x'}, {_id: 1, a: 'yx'}], {a: /x/});
// Dotted field.
coll.remove({});
assert.commandWorked(coll.insert({_id: 0, a: {b: 4}}));
assert.commandWorked(coll.insert({_id: 1, a: 2}));
assertResults([{_id: 0, a: {b: 4}}], {'a.b': 4});
// Value within an array.
coll.remove({});
assert.commandWorked(coll.insert({_id: 0, a: [1, 2, 3]}));
assert.commandWorked(coll.insert({_id: 1, a: [2, 2, 3]}));
assert.commandWorked(coll.insert({_id: 2, a: [2, 2, 2]}));
assertResults([{_id: 0, a: [1, 2, 3]}, {_id: 1, a: [2, 2, 3]}], {a: 3});
// Missing, null, $exists matching.
coll.remove({});
assert.commandWorked(coll.insert({_id: 0}));
assert.commandWorked(coll.insert({_id: 1, a: null}));
assert.commandWorked(coll.insert({_id: 3, a: 0}));
assertResults([{_id: 0}, {_id: 1, a: null}], {a: null});
assertResults(null, {a: {$exists: true}});
assertResults(null, {a: {$exists: false}});
// $elemMatch
coll.remove({});
assert.commandWorked(coll.insert({_id: 0, a: [1, 2]}));
assert.commandWorked(coll.insert({_id: 1, a: [1, 2, 3]}));
assertResults([{_id: 1, a: [1, 2, 3]}], {a: {$elemMatch: {$gt: 1, $mod: [2, 1]}}});
coll.remove({});
assert.commandWorked(coll.insert({_id: 0, a: [{b: 1}, {c: 2}]}));
assert.commandWorked(coll.insert({_id: 1, a: [{b: 1, c: 2}]}));
assertResults([{_id: 1, a: [{b: 1, c: 2}]}], {a: {$elemMatch: {b: 1, c: 2}}});
// $size
coll.remove({});
assert.commandWorked(coll.insert({}));
assert.commandWorked(coll.insert({a: null}));
assert.commandWorked(coll.insert({a: []}));
assert.commandWorked(coll.insert({a: [1]}));
assert.commandWorked(coll.insert({a: [1, 2]}));
assertResults(null, {a: {$size: 0}});
assertResults(null, {a: {$size: 1}});
assertResults(null, {a: {$size: 2}});
// $type
coll.remove({});
assert.commandWorked(coll.insert({}));
assert.commandWorked(coll.insert({a: null}));
assert.commandWorked(coll.insert({a: NumberInt(1)}));
assert.commandWorked(coll.insert({a: NumberLong(2)}));
assert.commandWorked(coll.insert({a: 66.6}));
assert.commandWorked(coll.insert({a: 'abc'}));
assert.commandWorked(coll.insert({a: /xyz/}));
assert.commandWorked(coll.insert({a: {q: 1}}));
assert.commandWorked(coll.insert({a: true}));
assert.commandWorked(coll.insert({a: new Date()}));
assert.commandWorked(coll.insert({a: new ObjectId()}));
for (let type = 1; type <= 18; ++type) {
assertResults(null, {a: {$type: type}});
}
coll.remove({});
assert.commandWorked(coll.insert({_id: 0, a: 1}));
assert.commandWorked(coll.insert({_id: 1, a: 2}));
assert.commandWorked(coll.insert({_id: 2, a: 3}));
// $and
assertResults([{_id: 1, a: 2}], {$and: [{a: 2}, {_id: 1}]});
assertResults([], {$and: [{a: 1}, {_id: 1}]});
assertResults([{_id: 1, a: 2}, {_id: 2, a: 3}],
{$and: [{$or: [{_id: 1}, {a: 3}]}, {$or: [{_id: 2}, {a: 2}]}]});
// $or
assertResults([{_id: 0, a: 1}, {_id: 2, a: 3}], {$or: [{_id: 0}, {a: 3}]});
}
checkMatchResults(false);
coll.createIndex({a: 1});
checkMatchResults(true);
coll.createIndex({'a.b': 1});
coll.createIndex({'a.c': 1});
checkMatchResults(true);
})();